From 457f24300005b56af1e71da1aa6398b60d8299e7 Mon Sep 17 00:00:00 2001 From: ron Date: Wed, 12 Mar 2025 01:16:03 +0800 Subject: [PATCH 01/14] Allow only the asset owner to register it on BH --- .../src/bridge_to_ethereum_config.rs | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs index 722d0bc98a751..ed702ca4bdf8c 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs @@ -16,7 +16,7 @@ use crate::{ weights, xcm_config, xcm_config::{AssetTransactors, XcmConfig}, - Runtime, RuntimeEvent, + ForeignAssets, Runtime, RuntimeEvent, }; use assets_common::matching::FromSiblingParachain; use frame_support::{parameter_types, traits::Everything}; @@ -83,11 +83,14 @@ impl snowbridge_pallet_system_frontend::Config for Runtime { #[cfg(feature = "runtime-benchmarks")] type Helper = (); type RegisterTokenOrigin = EitherOf< - ForeignTokenCreator< + ForeignTokenCreatorAsOwner< ( FromSiblingParachain, Location>, xcm_config::bridging::to_rococo::RococoAssetFromAssetHubRococo, ), + ForeignAssets, + crate::AccountId, + crate::LocationToAccountId, Location, >, EnsureRootWithSuccess, @@ -105,19 +108,32 @@ impl snowbridge_pallet_system_frontend::Config for Runtime { type BackendWeightInfo = weights::snowbridge_pallet_system_backend::WeightInfo; } -/// `EnsureOriginWithArg` impl for `ForeignTokenCreator` that allows only XCM origins that are -/// locations containing the class location. -pub struct ForeignTokenCreator(core::marker::PhantomData<(IsForeign, L)>); +/// `EnsureOriginWithArg` impl for `ForeignTokenCreator` that +/// a. allows only XCM origins that are locations containing the class location. +/// b. check the asset already exists +/// c. only the owner of the asset can create +pub struct ForeignTokenCreatorAsOwner< + IsForeign, + AssetInspect, + AccountId, + LocationToAccountId, + L = Location, +>(core::marker::PhantomData<(IsForeign, AssetInspect, AccountId, LocationToAccountId, L)>); impl< IsForeign: ContainsPair, + AssetInspect: frame_support::traits::fungibles::roles::Inspect, + AccountId: Eq + Clone, + LocationToAccountId: xcm_executor::traits::ConvertLocation, RuntimeOrigin: From + OriginTrait + Clone, - L: TryFrom + TryInto + Clone, - > EnsureOriginWithArg for ForeignTokenCreator + L: From + Into + Clone, + > EnsureOriginWithArg + for ForeignTokenCreatorAsOwner where RuntimeOrigin::PalletsOrigin: From + TryInto, + >::AssetId: From, { - type Success = Location; + type Success = L; fn try_origin( origin: RuntimeOrigin, @@ -127,9 +143,16 @@ where if !IsForeign::contains(asset_location, &origin_location) { return Err(origin) } + let asset_location: Location = asset_location.clone().into(); + let owner = AssetInspect::owner(asset_location.into()); + let location: Location = origin_location.clone().into(); + let from = LocationToAccountId::convert_location(&location); + if !owner.eq(&from) { + return Err(origin) + } let latest_location: Location = origin_location.clone().try_into().map_err(|_| origin.clone())?; - Ok(latest_location) + Ok(latest_location.into()) } #[cfg(feature = "runtime-benchmarks")] From dc679eae281bdf7113e31ce486d9f2663c7ea5e4 Mon Sep 17 00:00:00 2001 From: ron Date: Wed, 12 Mar 2025 20:54:24 +0800 Subject: [PATCH 02/14] Create origin for local assets --- .../src/bridge_to_ethereum_config.rs | 117 ++++++++++++++---- 1 file changed, 95 insertions(+), 22 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs index ed702ca4bdf8c..b8b6b4e055619 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs @@ -15,25 +15,30 @@ use crate::{ weights, xcm_config, - xcm_config::{AssetTransactors, XcmConfig}, - ForeignAssets, Runtime, RuntimeEvent, + xcm_config::{ + AssetTransactors, LocationToAccountId, TrustBackedAssetsPalletLocation, UniversalLocation, + XcmConfig, + }, + AccountId, Assets, ForeignAssets, Runtime, RuntimeEvent, }; -use assets_common::matching::FromSiblingParachain; -use frame_support::{parameter_types, traits::Everything}; +use assets_common::{matching::FromSiblingParachain, AssetIdForTrustBackedAssetsConvert}; +use frame_support::{ + dispatch::RawOrigin, + parameter_types, + traits::{ContainsPair, EitherOf, EnsureOrigin, EnsureOriginWithArg, Everything, OriginTrait}, +}; +use frame_system::{ensure_signed, EnsureRootWithSuccess}; use pallet_xcm::{EnsureXcm, Origin as XcmOrigin}; +use parachains_common::AssetIdForTrustBackedAssets; +use sp_runtime::traits::{MaybeEquivalence, TryConvert}; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; use xcm::prelude::{Asset, InteriorLocation, Location, PalletInstance, Parachain}; use xcm_executor::XcmExecutor; -use crate::xcm_config::UniversalLocation; #[cfg(not(feature = "runtime-benchmarks"))] use crate::xcm_config::XcmRouter; #[cfg(feature = "runtime-benchmarks")] use benchmark_helpers::DoNothingRouter; -use frame_support::traits::{ - ContainsPair, EitherOf, EnsureOrigin, EnsureOriginWithArg, OriginTrait, -}; -use frame_system::EnsureRootWithSuccess; #[cfg(feature = "runtime-benchmarks")] pub mod benchmark_helpers { @@ -83,17 +88,27 @@ impl snowbridge_pallet_system_frontend::Config for Runtime { #[cfg(feature = "runtime-benchmarks")] type Helper = (); type RegisterTokenOrigin = EitherOf< - ForeignTokenCreatorAsOwner< - ( - FromSiblingParachain, Location>, - xcm_config::bridging::to_rococo::RococoAssetFromAssetHubRococo, - ), - ForeignAssets, - crate::AccountId, - crate::LocationToAccountId, - Location, + EitherOf< + LocalAssetCreatorAsOwner< + AssetIdForTrustBackedAssetsConvert, + Assets, + AccountId, + AssetIdForTrustBackedAssets, + xcm_builder::AliasesIntoAccountId32, + Location, + >, + ForeignAssetCreatorAsOwner< + ( + FromSiblingParachain, Location>, + xcm_config::bridging::to_rococo::RococoAssetFromAssetHubRococo, + ), + ForeignAssets, + AccountId, + LocationToAccountId, + Location, + >, >, - EnsureRootWithSuccess, + EnsureRootWithSuccess, >; #[cfg(not(feature = "runtime-benchmarks"))] type XcmSender = XcmRouter; @@ -108,11 +123,11 @@ impl snowbridge_pallet_system_frontend::Config for Runtime { type BackendWeightInfo = weights::snowbridge_pallet_system_backend::WeightInfo; } -/// `EnsureOriginWithArg` impl for `ForeignTokenCreator` that +/// `EnsureOriginWithArg` impl for `ForeignAssetCreatorAsOwner` that /// a. allows only XCM origins that are locations containing the class location. /// b. check the asset already exists /// c. only the owner of the asset can create -pub struct ForeignTokenCreatorAsOwner< +pub struct ForeignAssetCreatorAsOwner< IsForeign, AssetInspect, AccountId, @@ -127,7 +142,7 @@ impl< RuntimeOrigin: From + OriginTrait + Clone, L: From + Into + Clone, > EnsureOriginWithArg - for ForeignTokenCreatorAsOwner + for ForeignAssetCreatorAsOwner where RuntimeOrigin::PalletsOrigin: From + TryInto, @@ -161,3 +176,61 @@ where Ok(pallet_xcm::Origin::Xcm(latest_location).into()) } } + +/// `EnsureOriginWithArg` impl for `LocalAssetCreatorAsOwner` that +/// a. allows signed origins +/// b. check the asset already exists +/// c. only the owner of the asset can create +pub struct LocalAssetCreatorAsOwner< + MatchAssetId, + AssetInspect, + AccountId, + AssetId, + AccountToLocation, + L = Location, +>( + core::marker::PhantomData<( + MatchAssetId, + AssetInspect, + AccountId, + AssetId, + AccountToLocation, + L, + )>, +); +impl< + MatchAssetId: MaybeEquivalence, + AssetInspect: frame_support::traits::fungibles::roles::Inspect, + AccountId: Eq + Clone, + AssetId: Eq + Clone, + AccountToLocation: for<'a> TryConvert<&'a AccountId, Location>, + RuntimeOrigin: OriginTrait + Clone, + L: From + Into + Clone, + > EnsureOriginWithArg + for LocalAssetCreatorAsOwner +where + RuntimeOrigin: Into, RuntimeOrigin>> + From>, + >::AssetId: From, +{ + type Success = L; + + fn try_origin( + origin: RuntimeOrigin, + asset_location: &L, + ) -> Result { + let who = ensure_signed(origin.clone()).map_err(|_| origin.clone())?; + let asset_id = MatchAssetId::convert(asset_location).ok_or(origin.clone())?; + let owner = AssetInspect::owner(asset_id.into()).ok_or(origin.clone())?; + if !owner.eq(&who) { + return Err(origin) + } + let latest_location: Location = + AccountToLocation::try_convert(&who).map_err(|_| origin.clone())?; + Ok(latest_location.into()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(_: &L) -> Result { + Ok(RawOrigin::Root.into()) + } +} From 7bd10eae1d6dce153a50818ccf65a8bfdd167290 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 13 Mar 2025 00:39:57 +0800 Subject: [PATCH 03/14] Add integration tests --- .../src/tests/snowbridge_v2_outbound.rs | 29 +++++------ .../tests/snowbridge_v2_outbound_edge_case.rs | 51 +++++++++++++++++++ .../snowbridge_v2_outbound_from_rococo.rs | 8 +++ 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound.rs index 295d761ff0364..b6062342db109 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound.rs @@ -13,7 +13,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{imports::*, tests::snowbridge_common::*}; +use crate::{ + imports::*, + tests::{snowbridge_common::*, usdt_at_ah_westend}, +}; use bridge_hub_westend_runtime::{ bridge_to_ethereum_config::EthereumGatewayAddress, EthereumOutboundQueueV2, }; @@ -140,8 +143,8 @@ pub fn register_relay_token_from_asset_hub_with_sudo() { }); } -#[allow(dead_code)] -pub fn register_relay_token_from_asset_hub_user_origin() { +#[test] +pub fn register_usdt_from_owner_on_asset_hub() { fund_on_bh(); register_assets_on_ah(); fund_on_ah(); @@ -150,24 +153,16 @@ pub fn register_relay_token_from_asset_hub_user_origin() { assert_ok!( ::SnowbridgeSystemFrontend::register_token( - RuntimeOrigin::signed(AssetHubWestendSender::get()), - bx!(VersionedLocation::from(Location { parents: 1, interior: [].into() })), + RuntimeOrigin::signed(AssetHubWestendAssetOwner::get()), + bx!(VersionedLocation::from(usdt_at_ah_westend())), AssetMetadata { - name: "wnd".as_bytes().to_vec().try_into().unwrap(), - symbol: "wnd".as_bytes().to_vec().try_into().unwrap(), - decimals: 12, - }, + name: "usdt".as_bytes().to_vec().try_into().unwrap(), + symbol: "usdt".as_bytes().to_vec().try_into().unwrap(), + decimals: 6, + } ) ); }); - - BridgeHubWestend::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - assert_expected_events!( - BridgeHubWestend, - vec![RuntimeEvent::EthereumOutboundQueueV2(snowbridge_pallet_outbound_queue_v2::Event::MessageQueued{ .. }) => {},] - ); - }); } #[test] diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_edge_case.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_edge_case.rs index 8b0098b5aecea..cad0b17edd626 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_edge_case.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_edge_case.rs @@ -1,6 +1,9 @@ // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 +use frame_support::assert_noop; +use snowbridge_core::AssetMetadata; +use sp_runtime::DispatchError::BadOrigin; // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -18,6 +21,7 @@ use crate::{ tests::{ snowbridge_common::*, snowbridge_v2_outbound::{EthereumSystemFrontend, EthereumSystemFrontendCall}, + usdt_at_ah_westend, }, }; use xcm::v5::AssetTransferFilter; @@ -167,3 +171,50 @@ fn export_from_non_system_parachain_will_fail() { ); }); } + +#[test] +pub fn register_usdt_not_from_owner_on_asset_hub_will_fail() { + fund_on_bh(); + register_assets_on_ah(); + fund_on_ah(); + AssetHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + + assert_noop!( + ::SnowbridgeSystemFrontend::register_token( + // The wwner is Alice, while AssetHubWestendReceiver is Bob, so it should fail + RuntimeOrigin::signed(AssetHubWestendReceiver::get()), + bx!(VersionedLocation::from(usdt_at_ah_westend())), + AssetMetadata { + name: "usdt".as_bytes().to_vec().try_into().unwrap(), + symbol: "usdt".as_bytes().to_vec().try_into().unwrap(), + decimals: 6, + } + ), + BadOrigin + ); + }); +} + +#[test] +pub fn register_relay_token_from_asset_hub_user_origin_will_fail() { + fund_on_bh(); + register_assets_on_ah(); + fund_on_ah(); + AssetHubWestend::execute_with(|| { + type RuntimeOrigin = ::RuntimeOrigin; + + assert_noop!( + ::SnowbridgeSystemFrontend::register_token( + RuntimeOrigin::signed(AssetHubWestendSender::get()), + bx!(VersionedLocation::from(Location { parents: 1, interior: [].into() })), + AssetMetadata { + name: "wnd".as_bytes().to_vec().try_into().unwrap(), + symbol: "wnd".as_bytes().to_vec().try_into().unwrap(), + decimals: 12, + }, + ), + BadOrigin + ); + }); +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_from_rococo.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_from_rococo.rs index 10d60b5bd39a3..dd4d042b8fd4e 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_from_rococo.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_from_rococo.rs @@ -345,6 +345,14 @@ fn register_rococo_asset_on_ethereum_from_rah() { ], ); + AssetHubWestend::force_create_foreign_asset( + bridged_asset_at_wah.clone(), + sa_of_rah_on_wah.clone(), + true, + ASSET_MIN_BALANCE, + vec![], + ); + let call = EthereumSystemFrontend::EthereumSystemFrontend(EthereumSystemFrontendCall::RegisterToken { asset_id: Box::new(VersionedLocation::from(bridged_asset_at_wah.clone())), From 75d4e2086f3416857fc8495d493ce87ccf8e1ca3 Mon Sep 17 00:00:00 2001 From: Vincent Geddes <117534+vgeddes@users.noreply.github.com> Date: Thu, 13 Mar 2025 16:06:38 +0200 Subject: [PATCH 04/14] Improve token registration check (#16) --- .../src/bridge_to_ethereum_config.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs index b8b6b4e055619..90352142a51ad 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs @@ -30,7 +30,7 @@ use frame_support::{ use frame_system::{ensure_signed, EnsureRootWithSuccess}; use pallet_xcm::{EnsureXcm, Origin as XcmOrigin}; use parachains_common::AssetIdForTrustBackedAssets; -use sp_runtime::traits::{MaybeEquivalence, TryConvert}; +use sp_runtime::traits::{MaybeEquivalence}; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; use xcm::prelude::{Asset, InteriorLocation, Location, PalletInstance, Parachain}; use xcm_executor::XcmExecutor; @@ -94,7 +94,6 @@ impl snowbridge_pallet_system_frontend::Config for Runtime { Assets, AccountId, AssetIdForTrustBackedAssets, - xcm_builder::AliasesIntoAccountId32, Location, >, ForeignAssetCreatorAsOwner< @@ -162,7 +161,7 @@ where let owner = AssetInspect::owner(asset_location.into()); let location: Location = origin_location.clone().into(); let from = LocationToAccountId::convert_location(&location); - if !owner.eq(&from) { + if from != owner { return Err(origin) } let latest_location: Location = @@ -186,7 +185,6 @@ pub struct LocalAssetCreatorAsOwner< AssetInspect, AccountId, AssetId, - AccountToLocation, L = Location, >( core::marker::PhantomData<( @@ -194,20 +192,18 @@ pub struct LocalAssetCreatorAsOwner< AssetInspect, AccountId, AssetId, - AccountToLocation, L, )>, ); impl< MatchAssetId: MaybeEquivalence, AssetInspect: frame_support::traits::fungibles::roles::Inspect, - AccountId: Eq + Clone, + AccountId: Eq + Clone + Into, AssetId: Eq + Clone, - AccountToLocation: for<'a> TryConvert<&'a AccountId, Location>, RuntimeOrigin: OriginTrait + Clone, L: From + Into + Clone, > EnsureOriginWithArg - for LocalAssetCreatorAsOwner + for LocalAssetCreatorAsOwner where RuntimeOrigin: Into, RuntimeOrigin>> + From>, >::AssetId: From, @@ -221,12 +217,10 @@ where let who = ensure_signed(origin.clone()).map_err(|_| origin.clone())?; let asset_id = MatchAssetId::convert(asset_location).ok_or(origin.clone())?; let owner = AssetInspect::owner(asset_id.into()).ok_or(origin.clone())?; - if !owner.eq(&who) { + if who != owner { return Err(origin) } - let latest_location: Location = - AccountToLocation::try_convert(&who).map_err(|_| origin.clone())?; - Ok(latest_location.into()) + Ok(who.into()) } #[cfg(feature = "runtime-benchmarks")] From cc1355f9f96424a8b88f5ab9d61a86dd0c7d68e6 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 13 Mar 2025 22:11:43 +0800 Subject: [PATCH 05/14] Assert events on BH --- .../src/tests/snowbridge_v2_outbound.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound.rs index b6062342db109..82ba7f15a259d 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound.rs @@ -163,6 +163,14 @@ pub fn register_usdt_from_owner_on_asset_hub() { ) ); }); + + BridgeHubWestend::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + assert_expected_events!( + BridgeHubWestend, + vec![RuntimeEvent::EthereumOutboundQueueV2(snowbridge_pallet_outbound_queue_v2::Event::MessageQueued{ .. }) => {},] + ); + }); } #[test] From 6e1aa1c8b07b49e938d1b2058d611bd26f9dcfb7 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 13 Mar 2025 22:16:09 +0800 Subject: [PATCH 06/14] Cleanup --- .../src/bridge_to_ethereum_config.rs | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs index 90352142a51ad..f5a3bf636de7f 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs @@ -30,7 +30,7 @@ use frame_support::{ use frame_system::{ensure_signed, EnsureRootWithSuccess}; use pallet_xcm::{EnsureXcm, Origin as XcmOrigin}; use parachains_common::AssetIdForTrustBackedAssets; -use sp_runtime::traits::{MaybeEquivalence}; +use sp_runtime::traits::MaybeEquivalence; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; use xcm::prelude::{Asset, InteriorLocation, Location, PalletInstance, Parachain}; use xcm_executor::XcmExecutor; @@ -164,9 +164,7 @@ where if from != owner { return Err(origin) } - let latest_location: Location = - origin_location.clone().try_into().map_err(|_| origin.clone())?; - Ok(latest_location.into()) + Ok(location.into()) } #[cfg(feature = "runtime-benchmarks")] @@ -180,20 +178,8 @@ where /// a. allows signed origins /// b. check the asset already exists /// c. only the owner of the asset can create -pub struct LocalAssetCreatorAsOwner< - MatchAssetId, - AssetInspect, - AccountId, - AssetId, - L = Location, ->( - core::marker::PhantomData<( - MatchAssetId, - AssetInspect, - AccountId, - AssetId, - L, - )>, +pub struct LocalAssetCreatorAsOwner( + core::marker::PhantomData<(MatchAssetId, AssetInspect, AccountId, AssetId, L)>, ); impl< MatchAssetId: MaybeEquivalence, From da2c1055bc85b8c388d3cf307f338b44ec316a3d Mon Sep 17 00:00:00 2001 From: Ron Date: Thu, 13 Mar 2025 22:17:21 +0800 Subject: [PATCH 07/14] Update cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_edge_case.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- .../src/tests/snowbridge_v2_outbound_edge_case.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_edge_case.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_edge_case.rs index cad0b17edd626..ece58f762e2a3 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_edge_case.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2_outbound_edge_case.rs @@ -182,7 +182,7 @@ pub fn register_usdt_not_from_owner_on_asset_hub_will_fail() { assert_noop!( ::SnowbridgeSystemFrontend::register_token( - // The wwner is Alice, while AssetHubWestendReceiver is Bob, so it should fail + // The owner is Alice, while AssetHubWestendReceiver is Bob, so it should fail RuntimeOrigin::signed(AssetHubWestendReceiver::get()), bx!(VersionedLocation::from(usdt_at_ah_westend())), AssetMetadata { From d6de046c66be826d3de958eb7ae697a7bc8a6f10 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 13 Mar 2025 23:04:14 +0800 Subject: [PATCH 08/14] Move the snowbridge-runtime-common --- Cargo.lock | 3 + .../runtime/runtime-common/Cargo.toml | 16 +- .../runtime/runtime-common/src/fee_handler.rs | 145 +++++++++++++++++ .../runtime/runtime-common/src/lib.rs | 148 +----------------- .../runtime-common/src/register_token.rs | 104 ++++++++++++ .../assets/asset-hub-westend/Cargo.toml | 4 + .../src/bridge_to_ethereum_config.rs | 104 +----------- 7 files changed, 278 insertions(+), 246 deletions(-) create mode 100644 bridges/snowbridge/runtime/runtime-common/src/fee_handler.rs create mode 100644 bridges/snowbridge/runtime/runtime-common/src/register_token.rs diff --git a/Cargo.lock b/Cargo.lock index 96d0862680321..eae7702cbfbe4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1146,6 +1146,7 @@ dependencies = [ "snowbridge-inbound-queue-primitives", "snowbridge-outbound-queue-primitives", "snowbridge-pallet-system-frontend", + "snowbridge-runtime-common", "sp-api 26.0.0", "sp-block-builder", "sp-consensus-aura", @@ -22129,7 +22130,9 @@ name = "snowbridge-runtime-common" version = "0.2.0" dependencies = [ "frame-support", + "frame-system", "log", + "pallet-xcm", "parity-scale-codec", "snowbridge-core", "snowbridge-outbound-queue-primitives", diff --git a/bridges/snowbridge/runtime/runtime-common/Cargo.toml b/bridges/snowbridge/runtime/runtime-common/Cargo.toml index cc1b3c5427cef..121fba6664555 100644 --- a/bridges/snowbridge/runtime/runtime-common/Cargo.toml +++ b/bridges/snowbridge/runtime/runtime-common/Cargo.toml @@ -17,16 +17,17 @@ exclude-from-umbrella = true [dependencies] codec = { workspace = true } frame-support = { workspace = true } +frame-system = { workspace = true } log = { workspace = true } +pallet-xcm = { workspace = true } +snowbridge-core = { workspace = true } +snowbridge-outbound-queue-primitives = { workspace = true } sp-arithmetic = { workspace = true } sp-std = { workspace = true } xcm = { workspace = true } xcm-builder = { workspace = true } xcm-executor = { workspace = true } -snowbridge-core = { workspace = true } -snowbridge-outbound-queue-primitives = { workspace = true } - [dev-dependencies] [features] @@ -34,7 +35,9 @@ default = ["std"] std = [ "codec/std", "frame-support/std", + "frame-system/std", "log/std", + "pallet-xcm/std", "snowbridge-core/std", "snowbridge-outbound-queue-primitives/std", "sp-arithmetic/std", @@ -45,8 +48,15 @@ std = [ ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", "snowbridge-core/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", "xcm/runtime-benchmarks", ] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-xcm/try-runtime", +] diff --git a/bridges/snowbridge/runtime/runtime-common/src/fee_handler.rs b/bridges/snowbridge/runtime/runtime-common/src/fee_handler.rs new file mode 100644 index 0000000000000..3c28e17bd7508 --- /dev/null +++ b/bridges/snowbridge/runtime/runtime-common/src/fee_handler.rs @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork + +use codec::FullCodec; +use core::marker::PhantomData; +use frame_support::traits::Get; +use snowbridge_outbound_queue_primitives::SendMessageFeeProvider; +use sp_arithmetic::traits::{BaseArithmetic, Unsigned}; +use sp_std::fmt::Debug; +use xcm::prelude::*; +use xcm_builder::HandleFee; +use xcm_executor::traits::{FeeReason, TransactAsset}; + +pub const LOG_TARGET: &str = "xcm::export-fee-to-sibling"; + +/// A `HandleFee` implementation that takes fees from `ExportMessage` XCM instructions +/// to Snowbridge and splits off the remote fee and deposits it to the origin +/// parachain sovereign account. The local fee is then returned back to be handled by +/// the next fee handler in the chain. Most likely the treasury account. +pub struct XcmExportFeeToSibling< + Balance, + AccountId, + FeeAssetLocation, + EthereumNetwork, + AssetTransactor, + FeeProvider, +>( + PhantomData<( + Balance, + AccountId, + FeeAssetLocation, + EthereumNetwork, + AssetTransactor, + FeeProvider, + )>, +); + +impl HandleFee + for XcmExportFeeToSibling< + Balance, + AccountId, + FeeAssetLocation, + EthereumNetwork, + AssetTransactor, + FeeProvider, + > +where + Balance: BaseArithmetic + Unsigned + Copy + From + Into + Debug, + AccountId: Clone + FullCodec, + FeeAssetLocation: Get, + EthereumNetwork: Get, + AssetTransactor: TransactAsset, + FeeProvider: SendMessageFeeProvider, +{ + fn handle_fee(fees: Assets, context: Option<&XcmContext>, reason: FeeReason) -> Assets { + let token_location = FeeAssetLocation::get(); + + // Check the reason to see if this export is for snowbridge. + if !matches!( + reason, + FeeReason::Export { network: bridged_network, ref destination } + if bridged_network == EthereumNetwork::get() && destination == &Here + ) { + return fees + } + + // Get the parachain sovereign from the `context`. + let maybe_para_id: Option = + if let Some(XcmContext { origin: Some(Location { parents: 1, interior }), .. }) = + context + { + if let Some(Parachain(sibling_para_id)) = interior.first() { + Some(*sibling_para_id) + } else { + None + } + } else { + None + }; + if maybe_para_id.is_none() { + log::error!( + target: LOG_TARGET, + "invalid location in context {:?}", + context, + ); + return fees + } + let para_id = maybe_para_id.unwrap(); + + // Get the total fee offered by export message. + let maybe_total_supplied_fee: Option<(usize, Balance)> = fees + .inner() + .iter() + .enumerate() + .filter_map(|(index, asset)| { + if let Asset { id: location, fun: Fungible(amount) } = asset { + if location.0 == token_location { + return Some((index, (*amount).into())) + } + } + None + }) + .next(); + if maybe_total_supplied_fee.is_none() { + log::error!( + target: LOG_TARGET, + "could not find fee asset item in fees: {:?}", + fees, + ); + return fees + } + let (fee_index, total_fee) = maybe_total_supplied_fee.unwrap(); + let local_fee = FeeProvider::local_fee(); + let remote_fee = total_fee.saturating_sub(local_fee); + if local_fee == Balance::zero() || remote_fee == Balance::zero() { + log::error!( + target: LOG_TARGET, + "calculated refund incorrect with local_fee: {:?} and remote_fee: {:?}", + local_fee, + remote_fee, + ); + return fees + } + // Refund remote component of fee to physical origin + let result = AssetTransactor::deposit_asset( + &Asset { id: AssetId(token_location.clone()), fun: Fungible(remote_fee.into()) }, + &Location::new(1, [Parachain(para_id)]), + context, + ); + if result.is_err() { + log::error!( + target: LOG_TARGET, + "transact fee asset failed: {:?}", + result.unwrap_err() + ); + return fees + } + + // Return remaining fee to the next fee handler in the chain. + let mut modified_fees = fees.inner().clone(); + modified_fees.remove(fee_index); + modified_fees.push(Asset { id: AssetId(token_location), fun: Fungible(local_fee.into()) }); + modified_fees.into() + } +} diff --git a/bridges/snowbridge/runtime/runtime-common/src/lib.rs b/bridges/snowbridge/runtime/runtime-common/src/lib.rs index 9e96ceb4a9ec2..4278750998057 100644 --- a/bridges/snowbridge/runtime/runtime-common/src/lib.rs +++ b/bridges/snowbridge/runtime/runtime-common/src/lib.rs @@ -5,148 +5,12 @@ //! Common traits and types shared by runtimes. #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(test)] -mod tests; - -use codec::FullCodec; -use core::marker::PhantomData; -use frame_support::traits::Get; -use snowbridge_outbound_queue_primitives::SendMessageFeeProvider; -use sp_arithmetic::traits::{BaseArithmetic, Unsigned}; -use sp_std::fmt::Debug; -use xcm::prelude::*; -use xcm_builder::HandleFee; -use xcm_executor::traits::{FeeReason, TransactAsset}; - -pub const LOG_TARGET: &str = "xcm::export-fee-to-sibling"; - -/// A `HandleFee` implementation that takes fees from `ExportMessage` XCM instructions -/// to Snowbridge and splits off the remote fee and deposits it to the origin -/// parachain sovereign account. The local fee is then returned back to be handled by -/// the next fee handler in the chain. Most likely the treasury account. -pub struct XcmExportFeeToSibling< - Balance, - AccountId, - FeeAssetLocation, - EthereumNetwork, - AssetTransactor, - FeeProvider, ->( - PhantomData<( - Balance, - AccountId, - FeeAssetLocation, - EthereumNetwork, - AssetTransactor, - FeeProvider, - )>, -); +pub mod fee_handler; +pub mod register_token; -impl HandleFee - for XcmExportFeeToSibling< - Balance, - AccountId, - FeeAssetLocation, - EthereumNetwork, - AssetTransactor, - FeeProvider, - > -where - Balance: BaseArithmetic + Unsigned + Copy + From + Into + Debug, - AccountId: Clone + FullCodec, - FeeAssetLocation: Get, - EthereumNetwork: Get, - AssetTransactor: TransactAsset, - FeeProvider: SendMessageFeeProvider, -{ - fn handle_fee(fees: Assets, context: Option<&XcmContext>, reason: FeeReason) -> Assets { - let token_location = FeeAssetLocation::get(); +pub use fee_handler::XcmExportFeeToSibling; - // Check the reason to see if this export is for snowbridge. - if !matches!( - reason, - FeeReason::Export { network: bridged_network, ref destination } - if bridged_network == EthereumNetwork::get() && destination == &Here - ) { - return fees - } +pub use register_token::{ForeignAssetCreatorAsOwner, LocalAssetCreatorAsOwner}; - // Get the parachain sovereign from the `context`. - let maybe_para_id: Option = - if let Some(XcmContext { origin: Some(Location { parents: 1, interior }), .. }) = - context - { - if let Some(Parachain(sibling_para_id)) = interior.first() { - Some(*sibling_para_id) - } else { - None - } - } else { - None - }; - if maybe_para_id.is_none() { - log::error!( - target: LOG_TARGET, - "invalid location in context {:?}", - context, - ); - return fees - } - let para_id = maybe_para_id.unwrap(); - - // Get the total fee offered by export message. - let maybe_total_supplied_fee: Option<(usize, Balance)> = fees - .inner() - .iter() - .enumerate() - .filter_map(|(index, asset)| { - if let Asset { id: location, fun: Fungible(amount) } = asset { - if location.0 == token_location { - return Some((index, (*amount).into())) - } - } - None - }) - .next(); - if maybe_total_supplied_fee.is_none() { - log::error!( - target: LOG_TARGET, - "could not find fee asset item in fees: {:?}", - fees, - ); - return fees - } - let (fee_index, total_fee) = maybe_total_supplied_fee.unwrap(); - let local_fee = FeeProvider::local_fee(); - let remote_fee = total_fee.saturating_sub(local_fee); - if local_fee == Balance::zero() || remote_fee == Balance::zero() { - log::error!( - target: LOG_TARGET, - "calculated refund incorrect with local_fee: {:?} and remote_fee: {:?}", - local_fee, - remote_fee, - ); - return fees - } - // Refund remote component of fee to physical origin - let result = AssetTransactor::deposit_asset( - &Asset { id: AssetId(token_location.clone()), fun: Fungible(remote_fee.into()) }, - &Location::new(1, [Parachain(para_id)]), - context, - ); - if result.is_err() { - log::error!( - target: LOG_TARGET, - "transact fee asset failed: {:?}", - result.unwrap_err() - ); - return fees - } - - // Return remaining fee to the next fee handler in the chain. - let mut modified_fees = fees.inner().clone(); - modified_fees.remove(fee_index); - modified_fees.push(Asset { id: AssetId(token_location), fun: Fungible(local_fee.into()) }); - modified_fees.into() - } -} +#[cfg(test)] +mod tests; diff --git a/bridges/snowbridge/runtime/runtime-common/src/register_token.rs b/bridges/snowbridge/runtime/runtime-common/src/register_token.rs new file mode 100644 index 0000000000000..83aced8376003 --- /dev/null +++ b/bridges/snowbridge/runtime/runtime-common/src/register_token.rs @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork + +use frame_support::{ + dispatch::RawOrigin, + sp_runtime::traits::MaybeEquivalence, + traits::{ContainsPair, EnsureOrigin, EnsureOriginWithArg, Everything, OriginTrait}, +}; +use frame_system::ensure_signed; +use pallet_xcm::{EnsureXcm, Origin as XcmOrigin}; +use xcm::prelude::Location; + +/// `EnsureOriginWithArg` impl for `ForeignAssetCreatorAsOwner` that +/// a. allows only XCM origins that are locations containing the class location. +/// b. check the asset already exists +/// c. only the owner of the asset can create +pub struct ForeignAssetCreatorAsOwner< + IsForeign, + AssetInspect, + AccountId, + LocationToAccountId, + L = Location, +>(core::marker::PhantomData<(IsForeign, AssetInspect, AccountId, LocationToAccountId, L)>); +impl< + IsForeign: ContainsPair, + AssetInspect: frame_support::traits::fungibles::roles::Inspect, + AccountId: Eq + Clone, + LocationToAccountId: xcm_executor::traits::ConvertLocation, + RuntimeOrigin: From + OriginTrait + Clone, + L: From + Into + Clone, + > EnsureOriginWithArg + for ForeignAssetCreatorAsOwner +where + RuntimeOrigin::PalletsOrigin: + From + TryInto, + >::AssetId: From, +{ + type Success = L; + + fn try_origin( + origin: RuntimeOrigin, + asset_location: &L, + ) -> Result { + let origin_location = EnsureXcm::::try_origin(origin.clone())?; + if !IsForeign::contains(asset_location, &origin_location) { + return Err(origin) + } + let asset_location: Location = asset_location.clone().into(); + let owner = AssetInspect::owner(asset_location.into()); + let location: Location = origin_location.clone().into(); + let from = LocationToAccountId::convert_location(&location); + if from != owner { + return Err(origin) + } + Ok(location.into()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(a: &L) -> Result { + let latest_location: Location = (*a).clone().try_into().map_err(|_| ())?; + Ok(pallet_xcm::Origin::Xcm(latest_location).into()) + } +} + +/// `EnsureOriginWithArg` impl for `LocalAssetCreatorAsOwner` that +/// a. allows signed origins +/// b. check the asset already exists +/// c. only the owner of the asset can create +pub struct LocalAssetCreatorAsOwner( + core::marker::PhantomData<(MatchAssetId, AssetInspect, AccountId, AssetId, L)>, +); +impl< + MatchAssetId: MaybeEquivalence, + AssetInspect: frame_support::traits::fungibles::roles::Inspect, + AccountId: Eq + Clone + Into, + AssetId: Eq + Clone, + RuntimeOrigin: OriginTrait + Clone, + L: From + Into + Clone, + > EnsureOriginWithArg + for LocalAssetCreatorAsOwner +where + RuntimeOrigin: Into, RuntimeOrigin>> + From>, + >::AssetId: From, +{ + type Success = L; + + fn try_origin( + origin: RuntimeOrigin, + asset_location: &L, + ) -> Result { + let who = ensure_signed(origin.clone()).map_err(|_| origin.clone())?; + let asset_id = MatchAssetId::convert(asset_location).ok_or(origin.clone())?; + let owner = AssetInspect::owner(asset_id.into()).ok_or(origin.clone())?; + if who != owner { + return Err(origin) + } + Ok(who.into()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(_: &L) -> Result { + Ok(RawOrigin::Root.into()) + } +} diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml index 419a16c94c8bd..497c6ee096b9f 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/Cargo.toml @@ -110,6 +110,7 @@ pallet-xcm-bridge-hub-router = { workspace = true } snowbridge-inbound-queue-primitives = { workspace = true } snowbridge-outbound-queue-primitives = { workspace = true } snowbridge-pallet-system-frontend = { workspace = true } +snowbridge-runtime-common = { workspace = true } [dev-dependencies] asset-test-utils = { workspace = true, default-features = true } @@ -160,6 +161,7 @@ runtime-benchmarks = [ "polkadot-runtime-common/runtime-benchmarks", "snowbridge-inbound-queue-primitives/runtime-benchmarks", "snowbridge-pallet-system-frontend/runtime-benchmarks", + "snowbridge-runtime-common/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", @@ -204,6 +206,7 @@ try-runtime = [ "parachain-info/try-runtime", "polkadot-runtime-common/try-runtime", "snowbridge-pallet-system-frontend/try-runtime", + "snowbridge-runtime-common/try-runtime", "sp-runtime/try-runtime", ] std = [ @@ -269,6 +272,7 @@ std = [ "snowbridge-inbound-queue-primitives/std", "snowbridge-outbound-queue-primitives/std", "snowbridge-pallet-system-frontend/std", + "snowbridge-runtime-common/std", "sp-api/std", "sp-block-builder/std", "sp-consensus-aura/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs index f5a3bf636de7f..fc41f0cc22d47 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs @@ -22,15 +22,10 @@ use crate::{ AccountId, Assets, ForeignAssets, Runtime, RuntimeEvent, }; use assets_common::{matching::FromSiblingParachain, AssetIdForTrustBackedAssetsConvert}; -use frame_support::{ - dispatch::RawOrigin, - parameter_types, - traits::{ContainsPair, EitherOf, EnsureOrigin, EnsureOriginWithArg, Everything, OriginTrait}, -}; -use frame_system::{ensure_signed, EnsureRootWithSuccess}; -use pallet_xcm::{EnsureXcm, Origin as XcmOrigin}; +use frame_support::{parameter_types, traits::EitherOf}; +use frame_system::EnsureRootWithSuccess; use parachains_common::AssetIdForTrustBackedAssets; -use sp_runtime::traits::MaybeEquivalence; +use snowbridge_runtime_common::{ForeignAssetCreatorAsOwner, LocalAssetCreatorAsOwner}; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; use xcm::prelude::{Asset, InteriorLocation, Location, PalletInstance, Parachain}; use xcm_executor::XcmExecutor; @@ -121,96 +116,3 @@ impl snowbridge_pallet_system_frontend::Config for Runtime { type PalletLocation = SystemFrontendPalletLocation; type BackendWeightInfo = weights::snowbridge_pallet_system_backend::WeightInfo; } - -/// `EnsureOriginWithArg` impl for `ForeignAssetCreatorAsOwner` that -/// a. allows only XCM origins that are locations containing the class location. -/// b. check the asset already exists -/// c. only the owner of the asset can create -pub struct ForeignAssetCreatorAsOwner< - IsForeign, - AssetInspect, - AccountId, - LocationToAccountId, - L = Location, ->(core::marker::PhantomData<(IsForeign, AssetInspect, AccountId, LocationToAccountId, L)>); -impl< - IsForeign: ContainsPair, - AssetInspect: frame_support::traits::fungibles::roles::Inspect, - AccountId: Eq + Clone, - LocationToAccountId: xcm_executor::traits::ConvertLocation, - RuntimeOrigin: From + OriginTrait + Clone, - L: From + Into + Clone, - > EnsureOriginWithArg - for ForeignAssetCreatorAsOwner -where - RuntimeOrigin::PalletsOrigin: - From + TryInto, - >::AssetId: From, -{ - type Success = L; - - fn try_origin( - origin: RuntimeOrigin, - asset_location: &L, - ) -> Result { - let origin_location = EnsureXcm::::try_origin(origin.clone())?; - if !IsForeign::contains(asset_location, &origin_location) { - return Err(origin) - } - let asset_location: Location = asset_location.clone().into(); - let owner = AssetInspect::owner(asset_location.into()); - let location: Location = origin_location.clone().into(); - let from = LocationToAccountId::convert_location(&location); - if from != owner { - return Err(origin) - } - Ok(location.into()) - } - - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin(a: &L) -> Result { - let latest_location: Location = (*a).clone().try_into().map_err(|_| ())?; - Ok(pallet_xcm::Origin::Xcm(latest_location).into()) - } -} - -/// `EnsureOriginWithArg` impl for `LocalAssetCreatorAsOwner` that -/// a. allows signed origins -/// b. check the asset already exists -/// c. only the owner of the asset can create -pub struct LocalAssetCreatorAsOwner( - core::marker::PhantomData<(MatchAssetId, AssetInspect, AccountId, AssetId, L)>, -); -impl< - MatchAssetId: MaybeEquivalence, - AssetInspect: frame_support::traits::fungibles::roles::Inspect, - AccountId: Eq + Clone + Into, - AssetId: Eq + Clone, - RuntimeOrigin: OriginTrait + Clone, - L: From + Into + Clone, - > EnsureOriginWithArg - for LocalAssetCreatorAsOwner -where - RuntimeOrigin: Into, RuntimeOrigin>> + From>, - >::AssetId: From, -{ - type Success = L; - - fn try_origin( - origin: RuntimeOrigin, - asset_location: &L, - ) -> Result { - let who = ensure_signed(origin.clone()).map_err(|_| origin.clone())?; - let asset_id = MatchAssetId::convert(asset_location).ok_or(origin.clone())?; - let owner = AssetInspect::owner(asset_id.into()).ok_or(origin.clone())?; - if who != owner { - return Err(origin) - } - Ok(who.into()) - } - - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin(_: &L) -> Result { - Ok(RawOrigin::Root.into()) - } -} From 1f570c5a6ca5b753cb6e91d04f2fc596909520ab Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 18 Mar 2025 09:50:43 +0800 Subject: [PATCH 09/14] Update bridges/snowbridge/runtime/runtime-common/src/register_token.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- .../runtime/runtime-common/src/register_token.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bridges/snowbridge/runtime/runtime-common/src/register_token.rs b/bridges/snowbridge/runtime/runtime-common/src/register_token.rs index 83aced8376003..71eaeead77818 100644 --- a/bridges/snowbridge/runtime/runtime-common/src/register_token.rs +++ b/bridges/snowbridge/runtime/runtime-common/src/register_token.rs @@ -62,10 +62,11 @@ where } } -/// `EnsureOriginWithArg` impl for `LocalAssetCreatorAsOwner` that -/// a. allows signed origins -/// b. check the asset already exists -/// c. only the owner of the asset can create +/// Origin check that verifies that an origin is the owner of a local trusted asset in +/// the `Assets` pallet on AssetHub. +/// 1. Allows signed origins +/// 2. Checks that the asset exists +/// 3. The origin must be the owner of the asset pub struct LocalAssetCreatorAsOwner( core::marker::PhantomData<(MatchAssetId, AssetInspect, AccountId, AssetId, L)>, ); From a647b18a25d12042deb985437d25af802988dde5 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 18 Mar 2025 09:57:55 +0800 Subject: [PATCH 10/14] Rename --- .../runtime/runtime-common/src/lib.rs | 2 +- .../runtime-common/src/register_token.rs | 27 +++++++++---------- .../src/bridge_to_ethereum_config.rs | 6 ++--- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/bridges/snowbridge/runtime/runtime-common/src/lib.rs b/bridges/snowbridge/runtime/runtime-common/src/lib.rs index 4278750998057..698b70aa0fbc0 100644 --- a/bridges/snowbridge/runtime/runtime-common/src/lib.rs +++ b/bridges/snowbridge/runtime/runtime-common/src/lib.rs @@ -10,7 +10,7 @@ pub mod register_token; pub use fee_handler::XcmExportFeeToSibling; -pub use register_token::{ForeignAssetCreatorAsOwner, LocalAssetCreatorAsOwner}; +pub use register_token::{ForeignAssetOwner, LocalAssetOwner}; #[cfg(test)] mod tests; diff --git a/bridges/snowbridge/runtime/runtime-common/src/register_token.rs b/bridges/snowbridge/runtime/runtime-common/src/register_token.rs index 83aced8376003..9a02bc15fb750 100644 --- a/bridges/snowbridge/runtime/runtime-common/src/register_token.rs +++ b/bridges/snowbridge/runtime/runtime-common/src/register_token.rs @@ -10,17 +10,14 @@ use frame_system::ensure_signed; use pallet_xcm::{EnsureXcm, Origin as XcmOrigin}; use xcm::prelude::Location; -/// `EnsureOriginWithArg` impl for `ForeignAssetCreatorAsOwner` that -/// a. allows only XCM origins that are locations containing the class location. -/// b. check the asset already exists -/// c. only the owner of the asset can create -pub struct ForeignAssetCreatorAsOwner< - IsForeign, - AssetInspect, - AccountId, - LocationToAccountId, - L = Location, ->(core::marker::PhantomData<(IsForeign, AssetInspect, AccountId, LocationToAccountId, L)>); +/// Origin check that verifies that an origin is the owner of a foreign asset in +/// the `ForeignAssets` pallet on AssetHub. +/// 1. Allows XCM origins +/// 2. Checks that the asset exists +/// 3. The origin must be the owner of the asset +pub struct ForeignAssetOwner( + core::marker::PhantomData<(IsForeign, AssetInspect, AccountId, LocationToAccountId, L)>, +); impl< IsForeign: ContainsPair, AssetInspect: frame_support::traits::fungibles::roles::Inspect, @@ -29,7 +26,7 @@ impl< RuntimeOrigin: From + OriginTrait + Clone, L: From + Into + Clone, > EnsureOriginWithArg - for ForeignAssetCreatorAsOwner + for ForeignAssetOwner where RuntimeOrigin::PalletsOrigin: From + TryInto, @@ -62,11 +59,11 @@ where } } -/// `EnsureOriginWithArg` impl for `LocalAssetCreatorAsOwner` that +/// `EnsureOriginWithArg` impl for `LocalAssetOwner` that /// a. allows signed origins /// b. check the asset already exists /// c. only the owner of the asset can create -pub struct LocalAssetCreatorAsOwner( +pub struct LocalAssetOwner( core::marker::PhantomData<(MatchAssetId, AssetInspect, AccountId, AssetId, L)>, ); impl< @@ -77,7 +74,7 @@ impl< RuntimeOrigin: OriginTrait + Clone, L: From + Into + Clone, > EnsureOriginWithArg - for LocalAssetCreatorAsOwner + for LocalAssetOwner where RuntimeOrigin: Into, RuntimeOrigin>> + From>, >::AssetId: From, diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs index 959608a371923..aa5a96cc7036d 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/bridge_to_ethereum_config.rs @@ -25,7 +25,7 @@ use assets_common::{matching::FromSiblingParachain, AssetIdForTrustBackedAssetsC use frame_support::{parameter_types, traits::EitherOf}; use frame_system::EnsureRootWithSuccess; use parachains_common::AssetIdForTrustBackedAssets; -use snowbridge_runtime_common::{ForeignAssetCreatorAsOwner, LocalAssetCreatorAsOwner}; +use snowbridge_runtime_common::{ForeignAssetOwner, LocalAssetOwner}; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; use xcm::prelude::{Asset, InteriorLocation, Location, PalletInstance, Parachain}; use xcm_executor::XcmExecutor; @@ -84,14 +84,14 @@ impl snowbridge_pallet_system_frontend::Config for Runtime { type Helper = (); type RegisterTokenOrigin = EitherOf< EitherOf< - LocalAssetCreatorAsOwner< + LocalAssetOwner< AssetIdForTrustBackedAssetsConvert, Assets, AccountId, AssetIdForTrustBackedAssets, Location, >, - ForeignAssetCreatorAsOwner< + ForeignAssetOwner< ( FromSiblingParachain, Location>, xcm_config::bridging::to_rococo::RococoAssetFromAssetHubRococo, From 658e513ba808a8350dcdc6a553f0b913c2f48b9b Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 18 Mar 2025 10:11:09 +0800 Subject: [PATCH 11/14] Update bridges/snowbridge/runtime/runtime-common/src/fee_handler.rs Co-authored-by: Vincent Geddes <117534+vgeddes@users.noreply.github.com> --- bridges/snowbridge/runtime/runtime-common/src/fee_handler.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bridges/snowbridge/runtime/runtime-common/src/fee_handler.rs b/bridges/snowbridge/runtime/runtime-common/src/fee_handler.rs index 3c28e17bd7508..fa2b9486f2cd8 100644 --- a/bridges/snowbridge/runtime/runtime-common/src/fee_handler.rs +++ b/bridges/snowbridge/runtime/runtime-common/src/fee_handler.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork +//! Handling of fees for `ExportMessage` on BridgeHub in V1. use codec::FullCodec; use core::marker::PhantomData; From b65c250a3f70e03b41f3c4b9479c18487ecca3e5 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 18 Mar 2025 11:28:34 +0800 Subject: [PATCH 12/14] XcmExportFeeToSibling to V1 and AssetOwner to V2 --- bridges/snowbridge/runtime/runtime-common/src/lib.rs | 12 ++++-------- .../runtime-common/src/{ => v1}/fee_handler.rs | 0 .../snowbridge/runtime/runtime-common/src/v1/mod.rs | 3 +++ .../snowbridge/runtime/runtime-common/src/v2/mod.rs | 3 +++ .../runtime-common/src/{ => v2}/register_token.rs | 0 .../bridge-hubs/bridge-hub-westend/src/lib.rs | 1 - .../bridge-hubs/bridge-hub-westend/src/xcm_config.rs | 2 +- 7 files changed, 11 insertions(+), 10 deletions(-) rename bridges/snowbridge/runtime/runtime-common/src/{ => v1}/fee_handler.rs (100%) create mode 100644 bridges/snowbridge/runtime/runtime-common/src/v1/mod.rs create mode 100644 bridges/snowbridge/runtime/runtime-common/src/v2/mod.rs rename bridges/snowbridge/runtime/runtime-common/src/{ => v2}/register_token.rs (100%) diff --git a/bridges/snowbridge/runtime/runtime-common/src/lib.rs b/bridges/snowbridge/runtime/runtime-common/src/lib.rs index 698b70aa0fbc0..479a1b34008ee 100644 --- a/bridges/snowbridge/runtime/runtime-common/src/lib.rs +++ b/bridges/snowbridge/runtime/runtime-common/src/lib.rs @@ -4,13 +4,9 @@ //! //! Common traits and types shared by runtimes. #![cfg_attr(not(feature = "std"), no_std)] - -pub mod fee_handler; -pub mod register_token; - -pub use fee_handler::XcmExportFeeToSibling; - -pub use register_token::{ForeignAssetOwner, LocalAssetOwner}; - #[cfg(test)] mod tests; +pub mod v1; +pub mod v2; +pub use v1::fee_handler::XcmExportFeeToSibling; +pub use v2::register_token::{ForeignAssetOwner, LocalAssetOwner}; diff --git a/bridges/snowbridge/runtime/runtime-common/src/fee_handler.rs b/bridges/snowbridge/runtime/runtime-common/src/v1/fee_handler.rs similarity index 100% rename from bridges/snowbridge/runtime/runtime-common/src/fee_handler.rs rename to bridges/snowbridge/runtime/runtime-common/src/v1/fee_handler.rs diff --git a/bridges/snowbridge/runtime/runtime-common/src/v1/mod.rs b/bridges/snowbridge/runtime/runtime-common/src/v1/mod.rs new file mode 100644 index 0000000000000..eee91c7a38d90 --- /dev/null +++ b/bridges/snowbridge/runtime/runtime-common/src/v1/mod.rs @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +pub mod fee_handler; diff --git a/bridges/snowbridge/runtime/runtime-common/src/v2/mod.rs b/bridges/snowbridge/runtime/runtime-common/src/v2/mod.rs new file mode 100644 index 0000000000000..7b882430af59d --- /dev/null +++ b/bridges/snowbridge/runtime/runtime-common/src/v2/mod.rs @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +pub mod register_token; diff --git a/bridges/snowbridge/runtime/runtime-common/src/register_token.rs b/bridges/snowbridge/runtime/runtime-common/src/v2/register_token.rs similarity index 100% rename from bridges/snowbridge/runtime/runtime-common/src/register_token.rs rename to bridges/snowbridge/runtime/runtime-common/src/v2/register_token.rs diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index b781191e93198..699e403e179d9 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -42,7 +42,6 @@ use bridge_runtime_common::extensions::{ }; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::{ClaimQueueOffset, CoreSelector, ParaId}; -use frame_support::traits::Contains; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs index 84362abe33f57..dc6261b4874a1 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/xcm_config.rs @@ -220,7 +220,7 @@ impl xcm_executor::Config for XcmConfig { WestendLocation, EthereumNetwork, Self::AssetTransactor, - crate::EthereumOutboundQueueV2, + crate::EthereumOutboundQueue, >, SendXcmFeeToAccount, ), From d76bf808eac488c40b4abf2fe354fce8ae4611d6 Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 18 Mar 2025 17:26:24 +0800 Subject: [PATCH 13/14] Update bridges/snowbridge/runtime/runtime-common/src/v2/register_token.rs Co-authored-by: Adrian Catangiu --- .../snowbridge/runtime/runtime-common/src/v2/register_token.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bridges/snowbridge/runtime/runtime-common/src/v2/register_token.rs b/bridges/snowbridge/runtime/runtime-common/src/v2/register_token.rs index aad48fe30c378..48122ce3dd875 100644 --- a/bridges/snowbridge/runtime/runtime-common/src/v2/register_token.rs +++ b/bridges/snowbridge/runtime/runtime-common/src/v2/register_token.rs @@ -10,8 +10,7 @@ use frame_system::ensure_signed; use pallet_xcm::{EnsureXcm, Origin as XcmOrigin}; use xcm::prelude::Location; -/// Origin check that verifies that an origin is the owner of a foreign asset in -/// the `ForeignAssets` pallet on AssetHub. +/// Origin check that verifies that an origin is the owner of a foreign asset. /// 1. Allows XCM origins /// 2. Checks that the asset exists /// 3. The origin must be the owner of the asset From 519920a3badf0bd7ecc6fb272b31328bb3b15371 Mon Sep 17 00:00:00 2001 From: Ron Date: Tue, 18 Mar 2025 17:26:39 +0800 Subject: [PATCH 14/14] Update bridges/snowbridge/runtime/runtime-common/src/v2/register_token.rs Co-authored-by: Adrian Catangiu --- .../snowbridge/runtime/runtime-common/src/v2/register_token.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bridges/snowbridge/runtime/runtime-common/src/v2/register_token.rs b/bridges/snowbridge/runtime/runtime-common/src/v2/register_token.rs index 48122ce3dd875..16860c5483b68 100644 --- a/bridges/snowbridge/runtime/runtime-common/src/v2/register_token.rs +++ b/bridges/snowbridge/runtime/runtime-common/src/v2/register_token.rs @@ -58,8 +58,7 @@ where } } -/// Origin check that verifies that an origin is the owner of a local trusted asset in -/// the `Assets` pallet on AssetHub. +/// Origin check that verifies that an origin is the owner of a local trusted asset. /// 1. Allows signed origins /// 2. Checks that the asset exists /// 3. The origin must be the owner of the asset