From 2edc0dd1317b6ec284c9edb0ca1cb5930c46023a Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 16 Jul 2024 19:24:18 +0800 Subject: [PATCH 01/15] Add a helper pallet for xcm stuff from Substrate to Ethereum --- Cargo.lock | 30 + Cargo.toml | 1 + bridges/snowbridge/Cargo.lock | 45 ++ bridges/snowbridge/Cargo.toml | 1 + .../snowbridge/pallets/xcm-helper/Cargo.toml | 80 +++ .../snowbridge/pallets/xcm-helper/src/lib.rs | 371 +++++++++++ .../snowbridge/pallets/xcm-helper/src/mock.rs | 600 ++++++++++++++++++ 7 files changed, 1128 insertions(+) create mode 100644 bridges/snowbridge/pallets/xcm-helper/Cargo.toml create mode 100644 bridges/snowbridge/pallets/xcm-helper/src/lib.rs create mode 100644 bridges/snowbridge/pallets/xcm-helper/src/mock.rs diff --git a/Cargo.lock b/Cargo.lock index 4df6191313838..de07c7f9da80c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -846,6 +846,7 @@ dependencies = [ "primitive-types", "rococo-runtime-constants", "scale-info", + "snowbridge-pallet-xcm-helper", "snowbridge-router-primitives", "sp-api", "sp-block-builder", @@ -19235,6 +19236,35 @@ dependencies = [ "staging-xcm-executor", ] +[[package]] +name = "snowbridge-pallet-xcm-helper" +version = "0.2.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "hex", + "hex-literal", + "log", + "pallet-assets", + "pallet-balances", + "pallet-message-queue", + "parity-scale-codec", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-runtime-parachains", + "scale-info", + "snowbridge-core", + "sp-core", + "sp-io", + "sp-keyring", + "sp-runtime", + "sp-std 14.0.0", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + [[package]] name = "snowbridge-router-primitives" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index d6099e420f918..d753f337a8365 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ members = [ "bridges/snowbridge/pallets/outbound-queue/runtime-api", "bridges/snowbridge/pallets/system", "bridges/snowbridge/pallets/system/runtime-api", + "bridges/snowbridge/pallets/xcm-helper", "bridges/snowbridge/primitives/beacon", "bridges/snowbridge/primitives/core", "bridges/snowbridge/primitives/ethereum", diff --git a/bridges/snowbridge/Cargo.lock b/bridges/snowbridge/Cargo.lock index fa1335eef6fc2..b87b9f77ec52b 100644 --- a/bridges/snowbridge/Cargo.lock +++ b/bridges/snowbridge/Cargo.lock @@ -2877,6 +2877,21 @@ dependencies = [ "sp-std 14.0.0", ] +[[package]] +name = "pallet-assets" +version = "29.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std 14.0.0", +] + [[package]] name = "pallet-authority-discovery" version = "28.0.0" @@ -3073,6 +3088,7 @@ name = "pallet-migrations" version = "1.0.0" dependencies = [ "docify", + "frame-benchmarking", "frame-support", "frame-system", "impl-trait-for-tuples", @@ -4695,6 +4711,35 @@ dependencies = [ "staging-xcm-executor", ] +[[package]] +name = "snowbridge-pallet-xcm-helper" +version = "0.2.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "hex", + "hex-literal", + "log", + "pallet-assets", + "pallet-balances", + "pallet-message-queue", + "parity-scale-codec", + "polkadot-parachain-primitives", + "polkadot-primitives", + "polkadot-runtime-parachains", + "scale-info", + "snowbridge-core", + "sp-core", + "sp-io", + "sp-keyring", + "sp-runtime", + "sp-std 14.0.0", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + [[package]] name = "snowbridge-router-primitives" version = "0.9.0" diff --git a/bridges/snowbridge/Cargo.toml b/bridges/snowbridge/Cargo.toml index ca70de1d0e074..605a433222108 100644 --- a/bridges/snowbridge/Cargo.toml +++ b/bridges/snowbridge/Cargo.toml @@ -15,6 +15,7 @@ members = [ "pallets/outbound-queue/runtime-api", "pallets/system", "pallets/system/runtime-api", + "pallets/xcm-helper", "primitives/beacon", "primitives/core", "primitives/ethereum", diff --git a/bridges/snowbridge/pallets/xcm-helper/Cargo.toml b/bridges/snowbridge/pallets/xcm-helper/Cargo.toml new file mode 100644 index 0000000000000..f261cddbc1192 --- /dev/null +++ b/bridges/snowbridge/pallets/xcm-helper/Cargo.toml @@ -0,0 +1,80 @@ +[package] +name = "snowbridge-pallet-xcm-helper" +description = "Snowbridge Xcm Helper Pallet" +version = "0.2.0" +authors = ["Snowfork "] +edition.workspace = true +repository.workspace = true +license = "Apache-2.0" +categories = ["cryptography::cryptocurrencies"] + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.6.12", default-features = false, features = [ + "derive", +] } +scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } +frame-benchmarking = { path = "../../../../substrate/frame/benchmarking", default-features = false, optional = true } +frame-support = { path = "../../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../../substrate/frame/system", default-features = false } +log = { workspace = true } +sp-core = { path = "../../../../substrate/primitives/core", default-features = false } +sp-std = { path = "../../../../substrate/primitives/std", default-features = false } +sp-io = { path = "../../../../substrate/primitives/io", default-features = false } +sp-runtime = { path = "../../../../substrate/primitives/runtime", default-features = false } +xcm = { package = "staging-xcm", path = "../../../../polkadot/xcm", default-features = false } +xcm-executor = { package = "staging-xcm-executor", path = "../../../../polkadot/xcm/xcm-executor", default-features = false } +snowbridge-core = { path = "../../primitives/core", default-features = false } + +[dev-dependencies] +hex = "0.4.1" +hex-literal = { version = "0.4.1" } +pallet-balances = { path = "../../../../substrate/frame/balances" } +sp-keyring = { path = "../../../../substrate/primitives/keyring" } +polkadot-primitives = { path = "../../../../polkadot/primitives" } +pallet-message-queue = { path = "../../../../substrate/frame/message-queue" } +xcm-builder = { package = "staging-xcm-builder", path = "../../../../polkadot/xcm/xcm-builder" } +pallet-assets = { path = "../../../../substrate/frame/assets" } +polkadot-runtime-parachains = { path = "../../../../polkadot/runtime/parachains" } +polkadot-parachain-primitives = { path = "../../../../polkadot/parachain" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "snowbridge-core/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "xcm-executor/std", + "xcm/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks", + "snowbridge-core/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-balances/try-runtime", + "pallet-message-queue/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/bridges/snowbridge/pallets/xcm-helper/src/lib.rs b/bridges/snowbridge/pallets/xcm-helper/src/lib.rs new file mode 100644 index 0000000000000..b3ea62be22d98 --- /dev/null +++ b/bridges/snowbridge/pallets/xcm-helper/src/lib.rs @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(test)] +mod mock; + +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use frame_support::{ + dispatch::{GetDispatchInfo, PostDispatchInfo}, + pallet_prelude::*, + }; + use frame_system::pallet_prelude::*; + use sp_core::H160; + use sp_runtime::traits::Dispatchable; + use sp_std::{boxed::Box, vec, vec::Vec}; + use xcm::prelude::*; + use xcm_executor::traits::{TransferType, WeightBounds, XcmAssetTransfers}; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// The ExecuteXcmOrigin type. + type ExecuteXcmOrigin: EnsureOrigin< + ::RuntimeOrigin, + Success = Location, + >; + + /// The XcmRouter type. + type XcmRouter: SendXcm; + + /// The XcmExecutor type. + type XcmExecutor: ExecuteXcm<::RuntimeCall> + XcmAssetTransfers; + + /// The runtime `Origin` type. + type RuntimeOrigin: From + From<::RuntimeOrigin>; + + /// The runtime `Call` type. + type RuntimeCall: Parameter + + GetDispatchInfo + + Dispatchable< + RuntimeOrigin = ::RuntimeOrigin, + PostInfo = PostDispatchInfo, + >; + + type Weigher: WeightBounds<::RuntimeCall>; + + type UniversalLocation: Get; + + type Destination: Get; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Fees were paid from a location for an operation (often for using `SendXcm`). + FeesPaid { paying: Location, fees: Assets }, + /// Execution of an XCM message was attempted. + Attempted { outcome: Outcome }, + /// A XCM message was sent. + Sent { origin: Location, destination: Location, message: Xcm<()>, message_id: XcmHash }, + } + + #[pallet::origin] + #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] + pub enum Origin { + /// It comes from somewhere in the XCM space wanting to transact. + Xcm(Location), + } + impl From for Origin { + fn from(location: Location) -> Origin { + Origin::Xcm(location) + } + } + + #[pallet::error] + pub enum Error { + InvalidXcm, + SendFailure, + BadVersion, + Empty, + CannotReanchor, + CannotDetermine, + InvalidAsset, + FeesNotMet, + UnweighableMessage, + LocalExecutionIncomplete, + } + + #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] + pub struct TransactInfo { + pub target: H160, + pub call: Vec, + pub gas_limit: u64, + } + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(Weight::from_parts(100_000_000, 0))] + pub fn transact_to_ethereum( + origin: OriginFor, + target: H160, + call: Vec, + gas_limit: u64, + ) -> DispatchResult { + let origin = T::ExecuteXcmOrigin::ensure_origin(origin)?; + + let dest = T::Destination::get(); + + // construct the inner xcm of ExportMessage + let transact = TransactInfo { target, call, gas_limit }; + let mut message = Xcm(vec![Transact { + origin_kind: OriginKind::SovereignAccount, + require_weight_at_most: Weight::default(), + call: transact.encode().into(), + }]); + let message_clone = message.clone(); + // Add SetTopic for tracing + let _ = &message + .inner_mut() + .push(SetTopic(message_clone.using_encoded(sp_io::hashing::blake2_256))); + + Self::send_xcm(origin, dest, message, None)?; + + Ok(()) + } + + #[pallet::call_index(1)] + #[pallet::weight(Weight::from_parts(100_000_000, 0))] + pub fn transfer_to_ethereum( + origin: OriginFor, + beneficiary: H160, + asset: Box, + fee: Box, + ) -> DispatchResult { + let origin = T::ExecuteXcmOrigin::ensure_origin(origin)?; + + let beneficiary: Location = Location::new( + 0, + [Junction::AccountKey20 { network: None, key: beneficiary.into() }], + ); + + let dest = T::Destination::get(); + + let asset: Asset = (*asset).try_into().map_err(|()| Error::::BadVersion)?; + let fee: Asset = (*fee).try_into().map_err(|()| Error::::BadVersion)?; + + if let Fungible(x) = asset.fun { + // If fungible asset, ensure non-zero amount. + ensure!(x > 0, Error::::Empty); + } + + // Find transfer types for fee asset. + let asset_transfer_type = T::XcmExecutor::determine_for(&asset, &dest) + .map_err(|_| Error::::CannotDetermine)?; + + log::debug!( + target: "xcm::transfer_to_ethereum", + "origin {:?}, dest {:?}, beneficiary {:?}, asset {:?}, fee {:?}, transfer_type {:?}", + origin, dest, beneficiary, asset, fee, asset_transfer_type + ); + + let (local_xcm, remote_xcm) = Self::build_xcm_transfer( + origin.clone(), + dest.clone(), + beneficiary, + &asset, + asset_transfer_type, + &fee, + )?; + + Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm, fee) + } + } + + impl Pallet { + /// Withdraw given `assets` from the given `location` and pay as XCM fees. + /// + /// Fails if: + /// - the `assets` are not known on this chain; + /// - the `assets` cannot be withdrawn with that location as the Origin. + fn charge_fees(location: Location, assets: Assets) -> DispatchResult { + T::XcmExecutor::charge_fees(location.clone(), assets.clone()) + .map_err(|_| Error::::FeesNotMet)?; + Self::deposit_event(Event::FeesPaid { paying: location, fees: assets }); + Ok(()) + } + + fn send_xcm( + origin: Location, + dest: Location, + remote_xcm: Xcm<()>, + fee: Option, + ) -> DispatchResult { + let (ticket, delivery_fee) = + validate_send::(dest.clone(), remote_xcm.clone()) + .map_err(|_| Error::::InvalidXcm)?; + Self::charge_fees(origin.clone(), delivery_fee).map_err(|_| Error::::FeesNotMet)?; + + if let Some(execution_fee) = fee { + Self::charge_fees(origin.clone(), execution_fee.into()) + .map_err(|_| Error::::FeesNotMet)?; + } + + let message_id = T::XcmRouter::deliver(ticket).map_err(|_| Error::::SendFailure)?; + Self::deposit_event(Event::Sent { + origin, + destination: dest, + message: remote_xcm, + message_id, + }); + Ok(()) + } + + fn execute_xcm_transfer( + origin: Location, + dest: Location, + mut local_xcm: Xcm<::RuntimeCall>, + remote_xcm: Xcm<()>, + fee: Asset, + ) -> DispatchResult { + log::debug!( + target: "xcm::transfer_to_ethereum", + "origin {:?}, dest {:?}, local_xcm {:?}, remote_xcm {:?}", + origin, dest, local_xcm, remote_xcm, + ); + + let weight = + T::Weigher::weight(&mut local_xcm).map_err(|()| Error::::UnweighableMessage)?; + let mut hash = local_xcm.using_encoded(sp_io::hashing::blake2_256); + let outcome = T::XcmExecutor::prepare_and_execute( + origin.clone(), + local_xcm, + &mut hash, + weight, + weight, + ); + Self::deposit_event(Event::Attempted { outcome: outcome.clone() }); + outcome.ensure_complete().map_err(|_| Error::::LocalExecutionIncomplete)?; + + Self::send_xcm(origin, dest, remote_xcm, Some(fee))?; + + Ok(()) + } + + fn build_xcm_transfer( + origin: Location, + dest: Location, + beneficiary: Location, + asset: &Asset, + transfer_type: TransferType, + fee: &Asset, + ) -> Result<(Xcm<::RuntimeCall>, Xcm<()>), Error> { + let (local, remote) = match transfer_type { + TransferType::LocalReserve => { + let (local, remote) = Self::local_reserve_transfer_programs( + origin.clone(), + dest.clone(), + beneficiary, + asset, + fee, + )?; + Some((local, remote)) + }, + TransferType::DestinationReserve => { + let (local, remote) = Self::destination_reserve_transfer_programs( + origin.clone(), + dest.clone(), + beneficiary, + asset, + fee, + )?; + Some((local, remote)) + }, + _ => None, + } + .ok_or(Error::InvalidAsset)?; + Ok((local, remote)) + } + + fn local_reserve_transfer_programs( + _origin: Location, + dest: Location, + beneficiary: Location, + asset: &Asset, + fee: &Asset, + ) -> Result<(Xcm<::RuntimeCall>, Xcm<()>), Error> { + let assets: Assets = asset.clone().into(); + let context = T::UniversalLocation::get(); + + let mut reanchored_assets = assets.clone(); + reanchored_assets + .reanchor(&dest, &context) + .map_err(|_| Error::::CannotReanchor)?; + + let mut reanchored_fee = fee.clone(); + reanchored_fee = reanchored_fee + .reanchored(&dest, &context) + .map_err(|_| Error::::CannotReanchor)?; + + // XCM instructions to be executed on local chain + let local_execute_xcm = Xcm(vec![ + // locally move `assets` to `dest`s local sovereign account + TransferAsset { assets, beneficiary: dest.clone() }, + ]); + // XCM instructions to be executed on bridge hub + let xcm_on_dest = Xcm(vec![ + // let (dest) chain know assets are in its SA on reserve + ReserveAssetDeposited(reanchored_assets), + // following instructions are not exec'ed on behalf of origin chain anymore + ClearOrigin, + BuyExecution { fees: reanchored_fee, weight_limit: Unlimited }, + DepositAsset { assets: Wild(AllCounted(1)), beneficiary }, + ]); + + Ok((local_execute_xcm, xcm_on_dest)) + } + + fn destination_reserve_transfer_programs( + _origin: Location, + dest: Location, + beneficiary: Location, + asset: &Asset, + fee: &Asset, + ) -> Result<(Xcm<::RuntimeCall>, Xcm<()>), Error> { + let assets: Assets = asset.clone().into(); + let context = T::UniversalLocation::get(); + + let mut reanchored_assets = assets.clone(); + reanchored_assets + .reanchor(&dest, &context) + .map_err(|_| Error::::CannotReanchor)?; + + let mut reanchored_fee = fee.clone(); + reanchored_fee = reanchored_fee + .reanchored(&dest, &context) + .map_err(|_| Error::::CannotReanchor)?; + + // XCM instructions to be executed on local chain + let local_execute_xcm = Xcm(vec![ + // withdraw reserve-based assets + WithdrawAsset(assets.clone()), + // burn reserve-based assets + BurnAsset(assets), + ]); + // XCM instructions to be executed on bridge hub + let xcm_on_dest = Xcm(vec![ + // withdraw `assets` from origin chain's sovereign account + WithdrawAsset(reanchored_assets), + // following instructions are not exec'ed on behalf of origin chain anymore + ClearOrigin, + BuyExecution { fees: reanchored_fee, weight_limit: Unlimited }, + DepositAsset { assets: Wild(AllCounted(1)), beneficiary }, + ]); + + Ok((local_execute_xcm, xcm_on_dest)) + } + } +} diff --git a/bridges/snowbridge/pallets/xcm-helper/src/mock.rs b/bridges/snowbridge/pallets/xcm-helper/src/mock.rs new file mode 100644 index 0000000000000..c4829cb6ff8fa --- /dev/null +++ b/bridges/snowbridge/pallets/xcm-helper/src/mock.rs @@ -0,0 +1,600 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork + +use codec::Encode; +use frame_support::{ + construct_runtime, derive_impl, parameter_types, + traits::{ + AsEnsureOriginWithArg, ConstU128, ConstU32, Contains, Equals, Everything, EverythingBut, + Nothing, + }, + weights::Weight, +}; +use frame_system::EnsureRoot; +use polkadot_parachain_primitives::primitives::Id as ParaId; +use polkadot_runtime_parachains::origin; +use sp_core::H256; +use sp_runtime::{traits::IdentityLookup, AccountId32, BuildStorage}; +pub use sp_std::cell::RefCell; +use xcm::prelude::*; +use xcm_builder::{ + AccountId32Aliases, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, Case, + ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, + DescribeAllTerminal, FixedRateOfFungible, FixedWeightBounds, FrameTransactionalProcessor, + FungibleAdapter, FungiblesAdapter, HashedDescription, IsConcrete, MatchedConvertedConcreteId, + NoChecking, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, + TakeWeightCredit, XcmFeeManagerFromComponents, XcmFeeToAccount, +}; +use xcm_executor::{ + traits::{Identity, JustTry}, + XcmExecutor, +}; + +use crate::Config; + +pub type AccountId = AccountId32; +pub type Balance = u128; +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub enum Test + { + System: frame_system, + Balances: pallet_balances, + AssetsPallet: pallet_assets, + ParasOrigin: origin, + XcmHelper: crate, + } +); + +thread_local! { + pub static SENT_XCM: RefCell)>> = RefCell::new(Vec::new()); + pub static FAIL_SEND_XCM: RefCell = RefCell::new(false); +} +pub(crate) fn sent_xcm() -> Vec<(Location, Xcm<()>)> { + SENT_XCM.with(|q| (*q.borrow()).clone()) +} +pub(crate) fn take_sent_xcm() -> Vec<(Location, Xcm<()>)> { + SENT_XCM.with(|q| { + let mut r = Vec::new(); + std::mem::swap(&mut r, &mut *q.borrow_mut()); + r + }) +} +pub(crate) fn set_send_xcm_artificial_failure(should_fail: bool) { + FAIL_SEND_XCM.with(|q| *q.borrow_mut() = should_fail); +} +/// Sender that never returns error. +pub struct TestSendXcm; +impl SendXcm for TestSendXcm { + type Ticket = (Location, Xcm<()>); + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult<(Location, Xcm<()>)> { + if FAIL_SEND_XCM.with(|q| *q.borrow()) { + return Err(SendError::Transport("Intentional send failure used in tests")); + } + let pair = (dest.take().unwrap(), msg.take().unwrap()); + Ok((pair, Assets::new())) + } + fn deliver(pair: (Location, Xcm<()>)) -> Result { + let hash = fake_message_hash(&pair.1); + SENT_XCM.with(|q| q.borrow_mut().push(pair)); + Ok(hash) + } +} +/// Sender that returns error if `X8` junction and stops routing +pub struct TestSendXcmErrX8; +impl SendXcm for TestSendXcmErrX8 { + type Ticket = (Location, Xcm<()>); + fn validate( + dest: &mut Option, + _: &mut Option>, + ) -> SendResult<(Location, Xcm<()>)> { + if dest.as_ref().unwrap().len() == 8 { + dest.take(); + Err(SendError::Transport("Destination location full")) + } else { + Err(SendError::NotApplicable) + } + } + fn deliver(pair: (Location, Xcm<()>)) -> Result { + let hash = fake_message_hash(&pair.1); + SENT_XCM.with(|q| q.borrow_mut().push(pair)); + Ok(hash) + } +} + +parameter_types! { + pub Para3000: u32 = 3000; + pub Para3000Location: Location = Parachain(Para3000::get()).into(); + pub Para3000PaymentAmount: u128 = 1; + pub Para3000PaymentAssets: Assets = Assets::from(Asset::from((Here, Para3000PaymentAmount::get()))); +} +/// Sender only sends to `Parachain(3000)` destination requiring payment. +pub struct TestPaidForPara3000SendXcm; +impl SendXcm for TestPaidForPara3000SendXcm { + type Ticket = (Location, Xcm<()>); + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult<(Location, Xcm<()>)> { + if let Some(dest) = dest.as_ref() { + if !dest.eq(&Para3000Location::get()) { + return Err(SendError::NotApplicable) + } + } else { + return Err(SendError::NotApplicable) + } + + let pair = (dest.take().unwrap(), msg.take().unwrap()); + Ok((pair, Para3000PaymentAssets::get())) + } + fn deliver(pair: (Location, Xcm<()>)) -> Result { + let hash = fake_message_hash(&pair.1); + SENT_XCM.with(|q| q.borrow_mut().push(pair)); + Ok(hash) + } +} + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type BlockWeights = (); + type BlockLength = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type DbWeight = (); + type BaseCallFilter = Everything; + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_balances::Config for Test { + type MaxLocks = MaxLocks; + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<0>; +} + +#[cfg(feature = "runtime-benchmarks")] +/// Simple conversion of `u32` into an `AssetId` for use in benchmarking. +pub struct XcmBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { + fn create_asset_id_parameter(id: u32) -> Location { + Location::new(1, [Parachain(id)]) + } +} + +impl pallet_assets::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = Location; + type AssetIdParameter = Location; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = EnsureRoot; + type AssetDeposit = ConstU128<1>; + type AssetAccountDeposit = ConstU128<10>; + type MetadataDepositBase = ConstU128<1>; + type MetadataDepositPerByte = ConstU128<1>; + type ApprovalDeposit = ConstU128<1>; + type StringLimit = ConstU32<50>; + type Freezer = (); + type WeightInfo = (); + type CallbackHandle = (); + type Extra = (); + type RemoveItemsLimit = ConstU32<5>; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = XcmBenchmarkHelper; +} + +// This child parachain is a system parachain trusted to teleport native token. +pub const SOME_SYSTEM_PARA: u32 = 1001; + +// This child parachain acts as trusted reserve for its assets in tests. +// USDT allowed to teleport to/from here. +pub const FOREIGN_ASSET_RESERVE_PARA_ID: u32 = 2001; +// Inner junction of reserve asset on `FOREIGN_ASSET_RESERVE_PARA_ID`. +pub const FOREIGN_ASSET_INNER_JUNCTION: Junction = GeneralIndex(1234567); + +// This child parachain acts as trusted reserve for say.. USDC that can be used for fees. +pub const USDC_RESERVE_PARA_ID: u32 = 2002; +// Inner junction of reserve asset on `USDC_RESERVE_PARA_ID`. +pub const USDC_INNER_JUNCTION: Junction = PalletInstance(42); + +// This child parachain is a trusted teleporter for say.. USDT (T from Teleport :)). +// We'll use USDT in tests that teleport fees. +pub const USDT_PARA_ID: u32 = 2003; + +// This child parachain is not configured as trusted reserve or teleport location for any assets. +pub const OTHER_PARA_ID: u32 = 2009; + +// This child parachain is used for filtered/disallowed assets. +pub const FILTERED_PARA_ID: u32 = 2010; + +parameter_types! { + pub const RelayLocation: Location = Here.into_location(); + pub const NativeAsset: Asset = Asset { + fun: Fungible(10), + id: AssetId(Here.into_location()), + }; + pub SystemParachainLocation: Location = Location::new( + 0, + [Parachain(SOME_SYSTEM_PARA)] + ); + pub ForeignReserveLocation: Location = Location::new( + 0, + [Parachain(FOREIGN_ASSET_RESERVE_PARA_ID)] + ); + pub PaidParaForeignReserveLocation: Location = Location::new( + 0, + [Parachain(Para3000::get())] + ); + pub ForeignAsset: Asset = Asset { + fun: Fungible(10), + id: AssetId(Location::new( + 0, + [Parachain(FOREIGN_ASSET_RESERVE_PARA_ID), FOREIGN_ASSET_INNER_JUNCTION], + )), + }; + pub PaidParaForeignAsset: Asset = Asset { + fun: Fungible(10), + id: AssetId(Location::new( + 0, + [Parachain(Para3000::get())], + )), + }; + pub UsdcReserveLocation: Location = Location::new( + 0, + [Parachain(USDC_RESERVE_PARA_ID)] + ); + pub Usdc: Asset = Asset { + fun: Fungible(10), + id: AssetId(Location::new( + 0, + [Parachain(USDC_RESERVE_PARA_ID), USDC_INNER_JUNCTION], + )), + }; + pub UsdtTeleportLocation: Location = Location::new( + 0, + [Parachain(USDT_PARA_ID)] + ); + pub Usdt: Asset = Asset { + fun: Fungible(10), + id: AssetId(Location::new( + 0, + [Parachain(USDT_PARA_ID)], + )), + }; + pub FilteredTeleportLocation: Location = Location::new( + 0, + [Parachain(FILTERED_PARA_ID)] + ); + pub FilteredTeleportAsset: Asset = Asset { + fun: Fungible(10), + id: AssetId(Location::new( + 0, + [Parachain(FILTERED_PARA_ID)], + )), + }; + pub const AnyNetwork: Option = None; + pub UniversalLocation: InteriorLocation = Here; + pub UnitWeightCost: u64 = 1_000; + pub CheckingAccount: AccountId = AccountId::from([1u8; 32]); +} + +pub type SovereignAccountOf = ( + ChildParachainConvertsVia, + AccountId32Aliases, + HashedDescription, +); + +pub type ForeignAssetsConvertedConcreteId = MatchedConvertedConcreteId< + Location, + Balance, + // Excludes relay/parent chain currency + EverythingBut<(Equals,)>, + Identity, + JustTry, +>; + +pub type AssetTransactors = ( + FungibleAdapter, SovereignAccountOf, AccountId, ()>, + FungiblesAdapter< + AssetsPallet, + ForeignAssetsConvertedConcreteId, + SovereignAccountOf, + AccountId, + NoChecking, + CheckingAccount, + >, +); + +type LocalOriginConverter = ( + SovereignSignedViaLocation, + ChildParachainAsNative, + SignedAccountId32AsNative, + ChildSystemParachainAsSuperuser, +); + +parameter_types! { + pub const BaseXcmWeight: Weight = Weight::from_parts(1_000, 1_000); + pub CurrencyPerSecondPerByte: (AssetId, u128, u128) = (AssetId(RelayLocation::get()), 1, 1); + pub TrustedLocal: (AssetFilter, Location) = (All.into(), Here.into()); + pub TrustedSystemPara: (AssetFilter, Location) = (NativeAsset::get().into(), SystemParachainLocation::get()); + pub TrustedUsdt: (AssetFilter, Location) = (Usdt::get().into(), UsdtTeleportLocation::get()); + pub TrustedFilteredTeleport: (AssetFilter, Location) = (FilteredTeleportAsset::get().into(), FilteredTeleportLocation::get()); + pub TeleportUsdtToForeign: (AssetFilter, Location) = (Usdt::get().into(), ForeignReserveLocation::get()); + pub TrustedForeign: (AssetFilter, Location) = (ForeignAsset::get().into(), ForeignReserveLocation::get()); + pub TrustedPaidParaForeign: (AssetFilter, Location) = (PaidParaForeignAsset::get().into(), PaidParaForeignReserveLocation::get()); + + pub TrustedUsdc: (AssetFilter, Location) = (Usdc::get().into(), UsdcReserveLocation::get()); + pub const MaxInstructions: u32 = 100; + pub const MaxAssetsIntoHolding: u32 = 64; + pub XcmFeesTargetAccount: AccountId = AccountId::new([167u8; 32]); +} + +pub const XCM_FEES_NOT_WAIVED_USER_ACCOUNT: [u8; 32] = [37u8; 32]; + +pub struct XcmFeesNotWaivedLocations; +impl Contains for XcmFeesNotWaivedLocations { + fn contains(location: &Location) -> bool { + matches!( + location.unpack(), + (0, [Junction::AccountId32 { network: None, id: XCM_FEES_NOT_WAIVED_USER_ACCOUNT }]) + ) + } +} + +pub type Barrier = ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, + AllowSubscriptionsFrom, +); + +pub type XcmRouter = (TestPaidForPara3000SendXcm, TestSendXcmErrX8, TestSendXcm); + +pub struct XcmConfig; +impl xcm_executor::Config for XcmConfig { + type RuntimeCall = RuntimeCall; + type XcmSender = XcmRouter; + type AssetTransactor = AssetTransactors; + type OriginConverter = LocalOriginConverter; + type IsReserve = (Case, Case, Case); + type IsTeleporter = ( + Case, + Case, + Case, + Case, + Case, + ); + type UniversalLocation = UniversalLocation; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = FixedRateOfFungible; + type ResponseHandler = (); + type AssetTrap = (); + type AssetLocker = (); + type AssetExchanger = (); + type AssetClaims = (); + type SubscriptionService = (); + type PalletInstancesInfo = AllPalletsWithSystem; + type MaxAssetsIntoHolding = MaxAssetsIntoHolding; + type FeeManager = XcmFeeManagerFromComponents< + EverythingBut, + XcmFeeToAccount, + >; + type MessageExporter = (); + type UniversalAliases = Nothing; + type CallDispatcher = RuntimeCall; + type SafeCallFilter = Everything; + type Aliasers = Nothing; + type TransactionalProcessor = FrameTransactionalProcessor; + type HrmpNewChannelOpenRequestHandler = (); + type HrmpChannelAcceptedHandler = (); + type HrmpChannelClosingHandler = (); + type XcmRecorder = (); +} + +pub type LocalOriginToLocation = SignedToAccountId32; + +pub struct XcmTeleportFiltered; +impl Contains<(Location, Vec)> for XcmTeleportFiltered { + fn contains(t: &(Location, Vec)) -> bool { + let filtered = FilteredTeleportAsset::get(); + t.1.iter().any(|asset| asset == &filtered) + } +} + +parameter_types! { + pub EthereumLocation: Location = Location { + parents: 2, + interior: Junctions::from([GlobalConsensus(Ethereum { chain_id: 11155111 })]), + }; +} + +impl Config for Test { + type RuntimeEvent = RuntimeEvent; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = xcm_builder::EnsureXcmOrigin; + type XcmExecutor = XcmExecutor; + type Weigher = FixedWeightBounds; + type UniversalLocation = UniversalLocation; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Destination = EthereumLocation; +} + +impl origin::Config for Test {} + +#[cfg(feature = "runtime-benchmarks")] +pub struct TestDeliveryHelper; +#[cfg(feature = "runtime-benchmarks")] +impl xcm_builder::EnsureDelivery for TestDeliveryHelper { + fn ensure_successful_delivery( + origin_ref: &Location, + _dest: &Location, + _fee_reason: xcm_executor::traits::FeeReason, + ) -> (Option, Option) { + use xcm_executor::traits::ConvertLocation; + let account = SovereignAccountOf::convert_location(origin_ref).expect("Valid location"); + // Give the existential deposit at least + let balance = ExistentialDeposit::get(); + let _ = >::make_free_balance_be( + &account, balance, + ); + (None, None) + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl super::benchmarking::Config for Test { + type DeliveryHelper = TestDeliveryHelper; + + fn reachable_dest() -> Option { + Some(Parachain(1000).into()) + } + + fn teleportable_asset_and_dest() -> Option<(Asset, Location)> { + Some((NativeAsset::get(), SystemParachainLocation::get())) + } + + fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> { + Some(( + Asset { fun: Fungible(10), id: AssetId(Here.into_location()) }, + Parachain(OTHER_PARA_ID).into(), + )) + } + + fn set_up_complex_asset_transfer() -> Option<(Assets, u32, Location, Box)> { + use crate::tests::assets_transfer::{into_assets_checked, set_up_foreign_asset}; + // Transfer native asset (local reserve) to `USDT_PARA_ID`. Using teleport-trusted USDT for + // fees. + + let asset_amount = 10u128; + let fee_amount = 2u128; + + let existential_deposit = ExistentialDeposit::get(); + let caller = frame_benchmarking::whitelisted_caller(); + + // Give some multiple of the existential deposit + let balance = asset_amount + existential_deposit * 1000; + let _ = >::make_free_balance_be( + &caller, balance, + ); + // create sufficient foreign asset USDT + let usdt_initial_local_amount = fee_amount * 10; + let (usdt_chain, _, usdt_id_location) = set_up_foreign_asset( + USDT_PARA_ID, + None, + caller.clone(), + usdt_initial_local_amount, + true, + ); + + // native assets transfer destination is USDT chain (teleport trust only for USDT) + let dest = usdt_chain; + let (assets, fee_index, _, _) = into_assets_checked( + // USDT for fees (is sufficient on local chain too) - teleported + (usdt_id_location.clone(), fee_amount).into(), + // native asset to transfer (not used for fees) - local reserve + (Location::here(), asset_amount).into(), + ); + // verify initial balances + assert_eq!(Balances::free_balance(&caller), balance); + assert_eq!( + AssetsPallet::balance(usdt_id_location.clone(), &caller), + usdt_initial_local_amount + ); + + // verify transferred successfully + let verify = Box::new(move || { + // verify balances after transfer, decreased by transferred amounts + assert_eq!(Balances::free_balance(&caller), balance - asset_amount); + assert_eq!( + AssetsPallet::balance(usdt_id_location, &caller), + usdt_initial_local_amount - fee_amount + ); + }); + Some((assets, fee_index as u32, dest, verify)) + } + + fn get_asset() -> Asset { + Asset { id: AssetId(Location::here()), fun: Fungible(ExistentialDeposit::get()) } + } +} + +pub(crate) fn last_event() -> RuntimeEvent { + System::events().pop().expect("RuntimeEvent expected").event +} + +pub(crate) fn last_events(n: usize) -> Vec { + System::events().into_iter().map(|e| e.event).rev().take(n).rev().collect() +} + +pub(crate) fn buy_execution(fees: impl Into) -> Instruction { + use xcm::latest::prelude::*; + BuyExecution { fees: fees.into(), weight_limit: Unlimited } +} + +pub(crate) fn buy_limited_execution( + fees: impl Into, + weight_limit: WeightLimit, +) -> Instruction { + use xcm::latest::prelude::*; + BuyExecution { fees: fees.into(), weight_limit } +} + +pub(crate) fn new_test_ext_with_balances( + balances: Vec<(AccountId, Balance)>, +) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + + pallet_balances::GenesisConfig:: { balances } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +pub(crate) fn fake_message_hash(message: &Xcm) -> XcmHash { + message.using_encoded(sp_io::hashing::blake2_256) +} From 707b512fe18628148acc37da7159f14ff241202a Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 16 Jul 2024 19:25:53 +0800 Subject: [PATCH 02/15] Install the helper pallet in AssetHub runtime --- .../assets/asset-hub-rococo/Cargo.toml | 3 ++ .../assets/asset-hub-rococo/src/lib.rs | 4 ++- .../assets/asset-hub-rococo/src/xcm_config.rs | 29 +++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index a880730ddacfd..a57e4ef97872d 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -94,6 +94,8 @@ bp-asset-hub-westend = { path = "../../../../../bridges/chains/chain-asset-hub-w bp-bridge-hub-rococo = { path = "../../../../../bridges/chains/chain-bridge-hub-rococo", default-features = false } bp-bridge-hub-westend = { path = "../../../../../bridges/chains/chain-bridge-hub-westend", default-features = false } snowbridge-router-primitives = { path = "../../../../../bridges/snowbridge/primitives/router", default-features = false } +snowbridge-pallet-xcm-helper = { path = "../../../../../bridges/snowbridge/pallets/xcm-helper", default-features = false } + [dev-dependencies] asset-test-utils = { path = "../test-utils" } @@ -227,6 +229,7 @@ std = [ "primitive-types/std", "rococo-runtime-constants/std", "scale-info/std", + "snowbridge-pallet-xcm-helper/std", "snowbridge-router-primitives/std", "sp-api/std", "sp-block-builder/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs index e3a106c6ab9a6..6ca29ecbda7c2 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/lib.rs @@ -519,7 +519,8 @@ impl InstanceFilter for ProxyType { RuntimeCall::Utility { .. } | RuntimeCall::Multisig { .. } | RuntimeCall::NftFractionalization { .. } | - RuntimeCall::Nfts { .. } | RuntimeCall::Uniques { .. } + RuntimeCall::Nfts { .. } | + RuntimeCall::Uniques { .. } ) }, ProxyType::AssetOwner => matches!( @@ -957,6 +958,7 @@ construct_runtime!( // TODO: the pallet instance should be removed once all pools have migrated // to the new account IDs. AssetConversionMigration: pallet_asset_conversion_ops = 200, + SnowbridgeXcmHelper: snowbridge_pallet_xcm_helper = 201, } ); diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index 664d2b9c9dd59..abc9a1c9850f6 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -507,6 +507,35 @@ impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { } } +parameter_types! { + pub EthereumLocation: Location = Location { + parents: 2, + interior: Junctions::from([GlobalConsensus(Ethereum { chain_id: 11155111 })]), + }; +} + +impl snowbridge_pallet_xcm_helper::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmRouter = WithUniqueTopic< + SovereignPaidRemoteExporter< + bridging::EthereumNetworkExportTable, + XcmpQueue, + UniversalLocation, + >, + >; + type XcmExecutor = XcmExecutor; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Weigher = WeightInfoBounds< + crate::weights::xcm::AssetHubRococoXcmWeight, + RuntimeCall, + MaxInstructions, + >; + type UniversalLocation = UniversalLocation; + type Destination = EthereumLocation; +} + /// All configuration related to bridging pub mod bridging { use super::*; From ad79c51b0fc8e60021d253ae925bdbb0b5f99e0a Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 16 Jul 2024 19:27:17 +0800 Subject: [PATCH 03/15] Revamp integration test --- .../primitives/router/src/outbound/mod.rs | 8 ----- .../assets/asset-hub-rococo/src/lib.rs | 1 + .../bridge-hub-rococo/src/tests/snowbridge.rs | 33 +++++++------------ 3 files changed, 13 insertions(+), 29 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index ddc36ce8cb61b..32e302abed300 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -237,14 +237,6 @@ impl<'a, Call> XcmConverter<'a, Call> { ensure!(reserve_assets.len() == 1, TooManyAssets); let reserve_asset = reserve_assets.get(0).ok_or(AssetResolutionFailed)?; - // If there was a fee specified verify it. - if let Some(fee_asset) = fee_asset { - // The fee asset must be the same as the reserve asset. - if fee_asset.id != reserve_asset.id || fee_asset.fun > reserve_asset.fun { - return Err(InvalidFeeAsset) - } - } - let (token, amount) = match reserve_asset { Asset { id: AssetId(inner_location), fun: Fungible(amount) } => match inner_location.unpack() { diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs index 202d02b250bb2..e95f64ea0be92 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-rococo/src/lib.rs @@ -48,6 +48,7 @@ decl_test_parachains! { PoolAssets: asset_hub_rococo_runtime::PoolAssets, AssetConversion: asset_hub_rococo_runtime::AssetConversion, Balances: asset_hub_rococo_runtime::Balances, + SnowbridgeXcmHelper: asset_hub_rococo_runtime::SnowbridgeXcmHelper, } }, } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs index 8856fe8eca511..69ea7e12af465 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -42,6 +42,7 @@ const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e"); const INSUFFICIENT_XCM_FEE: u128 = 1000; const XCM_FEE: u128 = 4_000_000_000; +const ETHEREUM_EXECUTION_FEE: u128 = 2_750_872_500_000; #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] pub enum ControlCall { @@ -433,7 +434,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, ] ); - let assets = vec![Asset { + let asset = Asset { id: AssetId(Location::new( 2, [ @@ -442,30 +443,20 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { ], )), fun: Fungible(WETH_AMOUNT), - }]; - let multi_assets = VersionedAssets::V4(Assets::from(assets)); - - let destination = VersionedLocation::V4(Location::new( - 2, - [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })], - )); - - let beneficiary = VersionedLocation::V4(Location::new( - 0, - [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], - )); + }; let free_balance_before = ::Balances::free_balance( AssetHubRococoReceiver::get(), ); + + // Todo: Change fee asset to WETH and remove the exchange_rate config on BH + let fee_asset: Asset = (AssetId::from(Location::parent()), ETHEREUM_EXECUTION_FEE).into(); // Send the Weth back to Ethereum - ::PolkadotXcm::limited_reserve_transfer_assets( + ::SnowbridgeXcmHelper::transfer_to_ethereum( RuntimeOrigin::signed(AssetHubRococoReceiver::get()), - Box::new(destination), - Box::new(beneficiary), - Box::new(multi_assets), - 0, - Unlimited, + ETHEREUM_DESTINATION_ADDRESS.into(), + Box::new(VersionedAsset::V4(asset)), + Box::new(VersionedAsset::V4(fee_asset)), ) .unwrap(); let free_balance_after = ::Balances::free_balance( @@ -483,8 +474,8 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { assert_expected_events!( BridgeHubRococo, vec![ - RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued {..}) => {}, - ] + RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued +{..}) => {}, ] ); let events = BridgeHubRococo::events(); // Check that the local fee was credited to the Snowbridge sovereign account From 3304372b0906ab6ebbe0d7a5cafc8e9fe737b253 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 16 Jul 2024 20:40:16 +0800 Subject: [PATCH 04/15] Revamp DefaultBridgeHubEthereumBaseFee to only include the local_fee portion --- bridges/snowbridge/primitives/router/src/outbound/mod.rs | 2 +- .../bridges/bridge-hub-rococo/src/tests/snowbridge.rs | 9 --------- .../runtimes/assets/asset-hub-rococo/src/xcm_config.rs | 8 +++----- .../bridge-hubs/bridge-hub-rococo/src/xcm_config.rs | 9 --------- 4 files changed, 4 insertions(+), 24 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index 32e302abed300..ab7caedb71a3d 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -117,7 +117,7 @@ where })?; // convert fee to Asset - let fee = Asset::from((Location::parent(), fee.total())).into(); + let fee = Asset::from((Location::parent(), fee.local)).into(); Ok(((ticket.encode(), message_id), fee)) } diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs index 69ea7e12af465..832e6a1b94b00 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -487,15 +487,6 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { )), "Snowbridge sovereign takes local fee." ); - // Check that the remote fee was credited to the AssetHub sovereign account - assert!( - events.iter().any(|event| matches!( - event, - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) - if *who == assethub_sovereign && *amount == 2680000000000, - )), - "AssetHub sovereign takes remote fee." - ); }); } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index abc9a1c9850f6..c898c0e6cf60f 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -664,11 +664,9 @@ pub mod bridging { use super::*; parameter_types! { - /// User fee for ERC20 token transfer back to Ethereum. - /// (initially was calculated by test `OutboundQueue::calculate_fees` - ETH/ROC 1/400 and fee_per_gas 20 GWEI = 2200698000000 + *25%) - /// Needs to be more than fee calculated from DefaultFeeConfig FeeConfigRecord in snowbridge:parachain/pallets/outbound-queue/src/lib.rs - /// Polkadot uses 10 decimals, Kusama and Rococo 12 decimals. - pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000; + /// User fee for delivery cost on bridge hub. calculated by OutboundQueue::calculate_local_fee + /// The message will be delivered to bridge hub and cached into outbound queue + pub const DefaultBridgeHubEthereumBaseFee: Balance = 4_000_000_000; pub storage BridgeHubEthereumBaseFee: Balance = DefaultBridgeHubEthereumBaseFee::get(); pub SiblingBridgeHubWithEthereumInboundQueueInstance: Location = Location::new( 1, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index a0d2e91dffd2e..72ab7951cfca0 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -38,7 +38,6 @@ use parachains_common::{ }; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; -use snowbridge_runtime_common::XcmExportFeeToSibling; use sp_core::Get; use sp_runtime::traits::AccountIdConversion; use sp_std::marker::PhantomData; @@ -215,14 +214,6 @@ impl xcm_executor::Config for XcmConfig { crate::bridge_to_westend_config::BridgeHubWestendChainId, crate::bridge_to_westend_config::AssetHubRococoToAssetHubWestendMessagesLane, >, - XcmExportFeeToSibling< - bp_rococo::Balance, - AccountId, - TokenLocation, - EthereumNetwork, - Self::AssetTransactor, - crate::EthereumOutboundQueue, - >, XcmFeeToAccount, ), >; From a59203db26b6ea76e725a76907e7b2a28e26b1e2 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 16 Jul 2024 20:43:49 +0800 Subject: [PATCH 05/15] Remove SetTopic unnecessary --- bridges/snowbridge/pallets/xcm-helper/src/lib.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/bridges/snowbridge/pallets/xcm-helper/src/lib.rs b/bridges/snowbridge/pallets/xcm-helper/src/lib.rs index b3ea62be22d98..e69be27840a55 100644 --- a/bridges/snowbridge/pallets/xcm-helper/src/lib.rs +++ b/bridges/snowbridge/pallets/xcm-helper/src/lib.rs @@ -121,16 +121,11 @@ pub mod pallet { // construct the inner xcm of ExportMessage let transact = TransactInfo { target, call, gas_limit }; - let mut message = Xcm(vec![Transact { + let message = Xcm(vec![Transact { origin_kind: OriginKind::SovereignAccount, require_weight_at_most: Weight::default(), call: transact.encode().into(), }]); - let message_clone = message.clone(); - // Add SetTopic for tracing - let _ = &message - .inner_mut() - .push(SetTopic(message_clone.using_encoded(sp_io::hashing::blake2_256))); Self::send_xcm(origin, dest, message, None)?; From 2db6330b26d647c8737f3be73b02c05ce9360b0a Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 16 Jul 2024 22:22:48 +0800 Subject: [PATCH 06/15] More comments --- bridges/snowbridge/pallets/xcm-helper/src/lib.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/bridges/snowbridge/pallets/xcm-helper/src/lib.rs b/bridges/snowbridge/pallets/xcm-helper/src/lib.rs index e69be27840a55..7e42f47de6ff4 100644 --- a/bridges/snowbridge/pallets/xcm-helper/src/lib.rs +++ b/bridges/snowbridge/pallets/xcm-helper/src/lib.rs @@ -48,10 +48,13 @@ pub mod pallet { PostInfo = PostDispatchInfo, >; + /// Means of measuring the weight consumed by an XCM message locally. type Weigher: WeightBounds<::RuntimeCall>; + /// Universal location of this runtime. type UniversalLocation: Get; + /// Ethereum's location of this runtime. type Destination: Get; } @@ -193,18 +196,19 @@ pub mod pallet { Ok(()) } + /// Send xcm to bridge hub with designated fee charged fn send_xcm( origin: Location, dest: Location, remote_xcm: Xcm<()>, - fee: Option, + remote_fee: Option, ) -> DispatchResult { let (ticket, delivery_fee) = validate_send::(dest.clone(), remote_xcm.clone()) .map_err(|_| Error::::InvalidXcm)?; Self::charge_fees(origin.clone(), delivery_fee).map_err(|_| Error::::FeesNotMet)?; - if let Some(execution_fee) = fee { + if let Some(execution_fee) = remote_fee { Self::charge_fees(origin.clone(), execution_fee.into()) .map_err(|_| Error::::FeesNotMet)?; } @@ -219,6 +223,8 @@ pub mod pallet { Ok(()) } + /// Execute the transfer including the local xcm + /// and send the remote xcm to bridge hub fn execute_xcm_transfer( origin: Location, dest: Location, @@ -250,6 +256,7 @@ pub mod pallet { Ok(()) } + /// Build the Xcm, a local one and the remote one which will be sent to bridge hub fn build_xcm_transfer( origin: Location, dest: Location, @@ -285,6 +292,7 @@ pub mod pallet { Ok((local, remote)) } + /// Construct Xcm for Polkadot native asset fn local_reserve_transfer_programs( _origin: Location, dest: Location, @@ -323,6 +331,7 @@ pub mod pallet { Ok((local_execute_xcm, xcm_on_dest)) } + /// Construct Xcm for Ethereum native asset fn destination_reserve_transfer_programs( _origin: Location, dest: Location, From 68d905e8aa6c687079dc432c1fdcc0476f89d6c4 Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 16 Jul 2024 22:23:07 +0800 Subject: [PATCH 07/15] Fix ci breaking --- .../snowbridge/pallets/xcm-helper/Cargo.toml | 62 ++-- .../primitives/router/src/outbound/mod.rs | 1 - .../assets/asset-hub-rococo/Cargo.toml | 284 +++++++++--------- 3 files changed, 177 insertions(+), 170 deletions(-) diff --git a/bridges/snowbridge/pallets/xcm-helper/Cargo.toml b/bridges/snowbridge/pallets/xcm-helper/Cargo.toml index f261cddbc1192..b0d6714f58a15 100644 --- a/bridges/snowbridge/pallets/xcm-helper/Cargo.toml +++ b/bridges/snowbridge/pallets/xcm-helper/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.12", default-features = false, features = [ - "derive", + "derive", ] } scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../../../../substrate/frame/benchmarking", default-features = false, optional = true } @@ -46,35 +46,41 @@ polkadot-parachain-primitives = { path = "../../../../polkadot/parachain" } [features] default = ["std"] std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "log/std", - "scale-info/std", - "snowbridge-core/std", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", - "sp-std/std", - "xcm-executor/std", - "xcm/std", + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "snowbridge-core/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "xcm-executor/std", + "xcm/std", ] runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-message-queue/runtime-benchmarks", - "polkadot-primitives/runtime-benchmarks", - "snowbridge-core/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", - "xcm-executor/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks", + "polkadot-runtime-parachains/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", + "snowbridge-core/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", ] try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-balances/try-runtime", - "pallet-message-queue/try-runtime", - "sp-runtime/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-assets/try-runtime", + "pallet-balances/try-runtime", + "pallet-message-queue/try-runtime", + "polkadot-runtime-parachains/try-runtime", + "sp-runtime/try-runtime", ] diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index ab7caedb71a3d..ba38567ddb925 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -152,7 +152,6 @@ enum XcmConverterError { ZeroAssetTransfer, BeneficiaryResolutionFailed, AssetResolutionFailed, - InvalidFeeAsset, SetTopicExpected, } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index a57e4ef97872d..0524dec0b990e 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -106,151 +106,153 @@ substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder", [features] default = ["std"] runtime-benchmarks = [ - "assets-common/runtime-benchmarks", - "cumulus-pallet-parachain-system/runtime-benchmarks", - "cumulus-pallet-session-benchmarking/runtime-benchmarks", - "cumulus-pallet-xcmp-queue/runtime-benchmarks", - "cumulus-primitives-core/runtime-benchmarks", - "cumulus-primitives-utility/runtime-benchmarks", - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system-benchmarking/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-asset-conversion-ops/runtime-benchmarks", - "pallet-asset-conversion/runtime-benchmarks", - "pallet-assets/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-collator-selection/runtime-benchmarks", - "pallet-message-queue/runtime-benchmarks", - "pallet-multisig/runtime-benchmarks", - "pallet-nft-fractionalization/runtime-benchmarks", - "pallet-nfts/runtime-benchmarks", - "pallet-proxy/runtime-benchmarks", - "pallet-timestamp/runtime-benchmarks", - "pallet-uniques/runtime-benchmarks", - "pallet-utility/runtime-benchmarks", - "pallet-xcm-benchmarks/runtime-benchmarks", - "pallet-xcm-bridge-hub-router/runtime-benchmarks", - "pallet-xcm/runtime-benchmarks", - "parachains-common/runtime-benchmarks", - "polkadot-parachain-primitives/runtime-benchmarks", - "polkadot-runtime-common/runtime-benchmarks", - "snowbridge-router-primitives/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", - "xcm-executor/runtime-benchmarks", - "xcm-fee-payment-runtime-api/runtime-benchmarks", + "assets-common/runtime-benchmarks", + "cumulus-pallet-parachain-system/runtime-benchmarks", + "cumulus-pallet-session-benchmarking/runtime-benchmarks", + "cumulus-pallet-xcmp-queue/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", + "cumulus-primitives-utility/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-asset-conversion-ops/runtime-benchmarks", + "pallet-asset-conversion/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-collator-selection/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", + "pallet-multisig/runtime-benchmarks", + "pallet-nft-fractionalization/runtime-benchmarks", + "pallet-nfts/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-uniques/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "pallet-xcm-benchmarks/runtime-benchmarks", + "pallet-xcm-bridge-hub-router/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "parachains-common/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-runtime-common/runtime-benchmarks", + "snowbridge-router-primitives/runtime-benchmarks", + "snowbridge-pallet-xcm-helper/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ - "cumulus-pallet-aura-ext/try-runtime", - "cumulus-pallet-parachain-system/try-runtime", - "cumulus-pallet-xcm/try-runtime", - "cumulus-pallet-xcmp-queue/try-runtime", - "frame-executive/try-runtime", - "frame-support/try-runtime", - "frame-system/try-runtime", - "frame-try-runtime/try-runtime", - "pallet-asset-conversion-ops/try-runtime", - "pallet-asset-conversion-tx-payment/try-runtime", - "pallet-asset-conversion/try-runtime", - "pallet-assets/try-runtime", - "pallet-aura/try-runtime", - "pallet-authorship/try-runtime", - "pallet-balances/try-runtime", - "pallet-collator-selection/try-runtime", - "pallet-message-queue/try-runtime", - "pallet-multisig/try-runtime", - "pallet-nft-fractionalization/try-runtime", - "pallet-nfts/try-runtime", - "pallet-proxy/try-runtime", - "pallet-session/try-runtime", - "pallet-timestamp/try-runtime", - "pallet-transaction-payment/try-runtime", - "pallet-uniques/try-runtime", - "pallet-utility/try-runtime", - "pallet-xcm-bridge-hub-router/try-runtime", - "pallet-xcm/try-runtime", - "parachain-info/try-runtime", - "polkadot-runtime-common/try-runtime", - "sp-runtime/try-runtime", + "cumulus-pallet-aura-ext/try-runtime", + "cumulus-pallet-parachain-system/try-runtime", + "cumulus-pallet-xcm/try-runtime", + "cumulus-pallet-xcmp-queue/try-runtime", + "frame-executive/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "frame-try-runtime/try-runtime", + "pallet-asset-conversion-ops/try-runtime", + "pallet-asset-conversion-tx-payment/try-runtime", + "pallet-asset-conversion/try-runtime", + "pallet-assets/try-runtime", + "pallet-aura/try-runtime", + "pallet-authorship/try-runtime", + "pallet-balances/try-runtime", + "pallet-collator-selection/try-runtime", + "pallet-message-queue/try-runtime", + "pallet-multisig/try-runtime", + "pallet-nft-fractionalization/try-runtime", + "pallet-nfts/try-runtime", + "pallet-proxy/try-runtime", + "pallet-session/try-runtime", + "pallet-timestamp/try-runtime", + "pallet-transaction-payment/try-runtime", + "pallet-uniques/try-runtime", + "pallet-utility/try-runtime", + "pallet-xcm-bridge-hub-router/try-runtime", + "pallet-xcm/try-runtime", + "parachain-info/try-runtime", + "polkadot-runtime-common/try-runtime", + "sp-runtime/try-runtime", + "snowbridge-pallet-xcm-helper/try-runtime", ] std = [ - "assets-common/std", - "bp-asset-hub-rococo/std", - "bp-asset-hub-westend/std", - "bp-bridge-hub-rococo/std", - "bp-bridge-hub-westend/std", - "codec/std", - "cumulus-pallet-aura-ext/std", - "cumulus-pallet-parachain-system/std", - "cumulus-pallet-session-benchmarking/std", - "cumulus-pallet-xcm/std", - "cumulus-pallet-xcmp-queue/std", - "cumulus-primitives-aura/std", - "cumulus-primitives-core/std", - "cumulus-primitives-storage-weight-reclaim/std", - "cumulus-primitives-utility/std", - "frame-benchmarking?/std", - "frame-executive/std", - "frame-metadata-hash-extension/std", - "frame-support/std", - "frame-system-benchmarking?/std", - "frame-system-rpc-runtime-api/std", - "frame-system/std", - "frame-try-runtime?/std", - "log/std", - "pallet-asset-conversion-ops/std", - "pallet-asset-conversion-tx-payment/std", - "pallet-asset-conversion/std", - "pallet-assets/std", - "pallet-aura/std", - "pallet-authorship/std", - "pallet-balances/std", - "pallet-collator-selection/std", - "pallet-message-queue/std", - "pallet-multisig/std", - "pallet-nft-fractionalization/std", - "pallet-nfts-runtime-api/std", - "pallet-nfts/std", - "pallet-proxy/std", - "pallet-session/std", - "pallet-timestamp/std", - "pallet-transaction-payment-rpc-runtime-api/std", - "pallet-transaction-payment/std", - "pallet-uniques/std", - "pallet-utility/std", - "pallet-xcm-benchmarks?/std", - "pallet-xcm-bridge-hub-router/std", - "pallet-xcm/std", - "parachain-info/std", - "parachains-common/std", - "polkadot-parachain-primitives/std", - "polkadot-runtime-common/std", - "primitive-types/std", - "rococo-runtime-constants/std", - "scale-info/std", - "snowbridge-pallet-xcm-helper/std", - "snowbridge-router-primitives/std", - "sp-api/std", - "sp-block-builder/std", - "sp-consensus-aura/std", - "sp-core/std", - "sp-genesis-builder/std", - "sp-inherents/std", - "sp-offchain/std", - "sp-runtime/std", - "sp-session/std", - "sp-std/std", - "sp-storage/std", - "sp-transaction-pool/std", - "sp-version/std", - "sp-weights/std", - "substrate-wasm-builder", - "testnet-parachains-constants/std", - "xcm-builder/std", - "xcm-executor/std", - "xcm-fee-payment-runtime-api/std", - "xcm/std", + "assets-common/std", + "bp-asset-hub-rococo/std", + "bp-asset-hub-westend/std", + "bp-bridge-hub-rococo/std", + "bp-bridge-hub-westend/std", + "codec/std", + "cumulus-pallet-aura-ext/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-session-benchmarking/std", + "cumulus-pallet-xcm/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-aura/std", + "cumulus-primitives-core/std", + "cumulus-primitives-storage-weight-reclaim/std", + "cumulus-primitives-utility/std", + "frame-benchmarking?/std", + "frame-executive/std", + "frame-metadata-hash-extension/std", + "frame-support/std", + "frame-system-benchmarking?/std", + "frame-system-rpc-runtime-api/std", + "frame-system/std", + "frame-try-runtime?/std", + "log/std", + "pallet-asset-conversion-ops/std", + "pallet-asset-conversion-tx-payment/std", + "pallet-asset-conversion/std", + "pallet-assets/std", + "pallet-aura/std", + "pallet-authorship/std", + "pallet-balances/std", + "pallet-collator-selection/std", + "pallet-message-queue/std", + "pallet-multisig/std", + "pallet-nft-fractionalization/std", + "pallet-nfts-runtime-api/std", + "pallet-nfts/std", + "pallet-proxy/std", + "pallet-session/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + "pallet-uniques/std", + "pallet-utility/std", + "pallet-xcm-benchmarks?/std", + "pallet-xcm-bridge-hub-router/std", + "pallet-xcm/std", + "parachain-info/std", + "parachains-common/std", + "polkadot-parachain-primitives/std", + "polkadot-runtime-common/std", + "primitive-types/std", + "rococo-runtime-constants/std", + "scale-info/std", + "snowbridge-pallet-xcm-helper/std", + "snowbridge-router-primitives/std", + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-aura/std", + "sp-core/std", + "sp-genesis-builder/std", + "sp-inherents/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-storage/std", + "sp-transaction-pool/std", + "sp-version/std", + "sp-weights/std", + "substrate-wasm-builder", + "testnet-parachains-constants/std", + "xcm-builder/std", + "xcm-executor/std", + "xcm-fee-payment-runtime-api/std", + "xcm/std", ] # Enable the metadata hash generation in the wasm builder. From 04125bf632a94adee8051c8b1eb6a2dfae961def Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 16 Jul 2024 23:23:13 +0800 Subject: [PATCH 08/15] Fix ci --- .../snowbridge/pallets/xcm-helper/Cargo.toml | 68 ++--- .../snowbridge/pallets/xcm-helper/src/mock.rs | 98 ------ .../primitives/router/src/outbound/tests.rs | 61 ---- .../assets/asset-hub-rococo/Cargo.toml | 286 +++++++++--------- .../bridge-hub-rococo/tests/snowbridge.rs | 6 +- 5 files changed, 180 insertions(+), 339 deletions(-) diff --git a/bridges/snowbridge/pallets/xcm-helper/Cargo.toml b/bridges/snowbridge/pallets/xcm-helper/Cargo.toml index b0d6714f58a15..aa4f1b9df8a2a 100644 --- a/bridges/snowbridge/pallets/xcm-helper/Cargo.toml +++ b/bridges/snowbridge/pallets/xcm-helper/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.12", default-features = false, features = [ - "derive", + "derive", ] } scale-info = { version = "2.9.0", default-features = false, features = ["derive"] } frame-benchmarking = { path = "../../../../substrate/frame/benchmarking", default-features = false, optional = true } @@ -46,41 +46,41 @@ polkadot-parachain-primitives = { path = "../../../../polkadot/parachain" } [features] default = ["std"] std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "log/std", - "scale-info/std", - "snowbridge-core/std", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", - "sp-std/std", - "xcm-executor/std", - "xcm/std", + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "snowbridge-core/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "xcm-executor/std", + "xcm/std", ] runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-assets/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-message-queue/runtime-benchmarks", - "polkadot-primitives/runtime-benchmarks", - "polkadot-runtime-parachains/runtime-benchmarks", - "polkadot-parachain-primitives/runtime-benchmarks", - "snowbridge-core/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", - "xcm-executor/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks", + "polkadot-runtime-parachains/runtime-benchmarks", + "snowbridge-core/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", ] try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-assets/try-runtime", - "pallet-balances/try-runtime", - "pallet-message-queue/try-runtime", - "polkadot-runtime-parachains/try-runtime", - "sp-runtime/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-assets/try-runtime", + "pallet-balances/try-runtime", + "pallet-message-queue/try-runtime", + "polkadot-runtime-parachains/try-runtime", + "sp-runtime/try-runtime", ] diff --git a/bridges/snowbridge/pallets/xcm-helper/src/mock.rs b/bridges/snowbridge/pallets/xcm-helper/src/mock.rs index c4829cb6ff8fa..4a23bcf75a361 100644 --- a/bridges/snowbridge/pallets/xcm-helper/src/mock.rs +++ b/bridges/snowbridge/pallets/xcm-helper/src/mock.rs @@ -434,14 +434,6 @@ impl xcm_executor::Config for XcmConfig { pub type LocalOriginToLocation = SignedToAccountId32; -pub struct XcmTeleportFiltered; -impl Contains<(Location, Vec)> for XcmTeleportFiltered { - fn contains(t: &(Location, Vec)) -> bool { - let filtered = FilteredTeleportAsset::get(); - t.1.iter().any(|asset| asset == &filtered) - } -} - parameter_types! { pub EthereumLocation: Location = Location { parents: 2, @@ -483,83 +475,6 @@ impl xcm_builder::EnsureDelivery for TestDeliveryHelper { } } -#[cfg(feature = "runtime-benchmarks")] -impl super::benchmarking::Config for Test { - type DeliveryHelper = TestDeliveryHelper; - - fn reachable_dest() -> Option { - Some(Parachain(1000).into()) - } - - fn teleportable_asset_and_dest() -> Option<(Asset, Location)> { - Some((NativeAsset::get(), SystemParachainLocation::get())) - } - - fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> { - Some(( - Asset { fun: Fungible(10), id: AssetId(Here.into_location()) }, - Parachain(OTHER_PARA_ID).into(), - )) - } - - fn set_up_complex_asset_transfer() -> Option<(Assets, u32, Location, Box)> { - use crate::tests::assets_transfer::{into_assets_checked, set_up_foreign_asset}; - // Transfer native asset (local reserve) to `USDT_PARA_ID`. Using teleport-trusted USDT for - // fees. - - let asset_amount = 10u128; - let fee_amount = 2u128; - - let existential_deposit = ExistentialDeposit::get(); - let caller = frame_benchmarking::whitelisted_caller(); - - // Give some multiple of the existential deposit - let balance = asset_amount + existential_deposit * 1000; - let _ = >::make_free_balance_be( - &caller, balance, - ); - // create sufficient foreign asset USDT - let usdt_initial_local_amount = fee_amount * 10; - let (usdt_chain, _, usdt_id_location) = set_up_foreign_asset( - USDT_PARA_ID, - None, - caller.clone(), - usdt_initial_local_amount, - true, - ); - - // native assets transfer destination is USDT chain (teleport trust only for USDT) - let dest = usdt_chain; - let (assets, fee_index, _, _) = into_assets_checked( - // USDT for fees (is sufficient on local chain too) - teleported - (usdt_id_location.clone(), fee_amount).into(), - // native asset to transfer (not used for fees) - local reserve - (Location::here(), asset_amount).into(), - ); - // verify initial balances - assert_eq!(Balances::free_balance(&caller), balance); - assert_eq!( - AssetsPallet::balance(usdt_id_location.clone(), &caller), - usdt_initial_local_amount - ); - - // verify transferred successfully - let verify = Box::new(move || { - // verify balances after transfer, decreased by transferred amounts - assert_eq!(Balances::free_balance(&caller), balance - asset_amount); - assert_eq!( - AssetsPallet::balance(usdt_id_location, &caller), - usdt_initial_local_amount - fee_amount - ); - }); - Some((assets, fee_index as u32, dest, verify)) - } - - fn get_asset() -> Asset { - Asset { id: AssetId(Location::here()), fun: Fungible(ExistentialDeposit::get()) } - } -} - pub(crate) fn last_event() -> RuntimeEvent { System::events().pop().expect("RuntimeEvent expected").event } @@ -568,19 +483,6 @@ pub(crate) fn last_events(n: usize) -> Vec { System::events().into_iter().map(|e| e.event).rev().take(n).rev().collect() } -pub(crate) fn buy_execution(fees: impl Into) -> Instruction { - use xcm::latest::prelude::*; - BuyExecution { fees: fees.into(), weight_limit: Unlimited } -} - -pub(crate) fn buy_limited_execution( - fees: impl Into, - weight_limit: WeightLimit, -) -> Instruction { - use xcm::latest::prelude::*; - BuyExecution { fees: fees.into(), weight_limit } -} - pub(crate) fn new_test_ext_with_balances( balances: Vec<(AccountId, Balance)>, ) -> sp_io::TestExternalities { diff --git a/bridges/snowbridge/primitives/router/src/outbound/tests.rs b/bridges/snowbridge/primitives/router/src/outbound/tests.rs index 111243bb45a7e..ad788c0b2cc3d 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/tests.rs @@ -569,67 +569,6 @@ fn xcm_converter_convert_with_partial_message_yields_unexpected_end_of_xcm() { assert_eq!(result.err(), Some(XcmConverterError::UnexpectedEndOfXcm)); } -#[test] -fn xcm_converter_with_different_fee_asset_fails() { - let network = BridgedNetwork::get(); - - let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); - let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); - - let asset_location = [AccountKey20 { network: None, key: token_address }].into(); - let fee_asset = - Asset { id: AssetId(Location { parents: 0, interior: Here }), fun: Fungible(1000) }; - - let assets: Assets = vec![Asset { id: AssetId(asset_location), fun: Fungible(1000) }].into(); - - let filter: AssetFilter = assets.clone().into(); - - let message: Xcm<()> = vec![ - WithdrawAsset(assets.clone()), - ClearOrigin, - BuyExecution { fees: fee_asset, weight_limit: Unlimited }, - DepositAsset { - assets: filter, - beneficiary: AccountKey20 { network: None, key: beneficiary_address }.into(), - }, - SetTopic([0; 32]), - ] - .into(); - let mut converter = XcmConverter::new(&message, &network); - let result = converter.convert(); - assert_eq!(result.err(), Some(XcmConverterError::InvalidFeeAsset)); -} - -#[test] -fn xcm_converter_with_fees_greater_than_reserve_fails() { - let network = BridgedNetwork::get(); - - let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); - let beneficiary_address: [u8; 20] = hex!("2000000000000000000000000000000000000000"); - - let asset_location: Location = [AccountKey20 { network: None, key: token_address }].into(); - let fee_asset = Asset { id: AssetId(asset_location.clone()), fun: Fungible(1001) }; - - let assets: Assets = vec![Asset { id: AssetId(asset_location), fun: Fungible(1000) }].into(); - - let filter: AssetFilter = assets.clone().into(); - - let message: Xcm<()> = vec![ - WithdrawAsset(assets.clone()), - ClearOrigin, - BuyExecution { fees: fee_asset, weight_limit: Unlimited }, - DepositAsset { - assets: filter, - beneficiary: AccountKey20 { network: None, key: beneficiary_address }.into(), - }, - SetTopic([0; 32]), - ] - .into(); - let mut converter = XcmConverter::new(&message, &network); - let result = converter.convert(); - assert_eq!(result.err(), Some(XcmConverterError::InvalidFeeAsset)); -} - #[test] fn xcm_converter_convert_with_empty_xcm_yields_unexpected_end_of_xcm() { let network = BridgedNetwork::get(); diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index 0524dec0b990e..4669b6d516eeb 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -106,153 +106,153 @@ substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder", [features] default = ["std"] runtime-benchmarks = [ - "assets-common/runtime-benchmarks", - "cumulus-pallet-parachain-system/runtime-benchmarks", - "cumulus-pallet-session-benchmarking/runtime-benchmarks", - "cumulus-pallet-xcmp-queue/runtime-benchmarks", - "cumulus-primitives-core/runtime-benchmarks", - "cumulus-primitives-utility/runtime-benchmarks", - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system-benchmarking/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-asset-conversion-ops/runtime-benchmarks", - "pallet-asset-conversion/runtime-benchmarks", - "pallet-assets/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-collator-selection/runtime-benchmarks", - "pallet-message-queue/runtime-benchmarks", - "pallet-multisig/runtime-benchmarks", - "pallet-nft-fractionalization/runtime-benchmarks", - "pallet-nfts/runtime-benchmarks", - "pallet-proxy/runtime-benchmarks", - "pallet-timestamp/runtime-benchmarks", - "pallet-uniques/runtime-benchmarks", - "pallet-utility/runtime-benchmarks", - "pallet-xcm-benchmarks/runtime-benchmarks", - "pallet-xcm-bridge-hub-router/runtime-benchmarks", - "pallet-xcm/runtime-benchmarks", - "parachains-common/runtime-benchmarks", - "polkadot-parachain-primitives/runtime-benchmarks", - "polkadot-runtime-common/runtime-benchmarks", - "snowbridge-router-primitives/runtime-benchmarks", - "snowbridge-pallet-xcm-helper/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", - "xcm-executor/runtime-benchmarks", - "xcm-fee-payment-runtime-api/runtime-benchmarks", + "assets-common/runtime-benchmarks", + "cumulus-pallet-parachain-system/runtime-benchmarks", + "cumulus-pallet-session-benchmarking/runtime-benchmarks", + "cumulus-pallet-xcmp-queue/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", + "cumulus-primitives-utility/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system-benchmarking/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-asset-conversion-ops/runtime-benchmarks", + "pallet-asset-conversion/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-collator-selection/runtime-benchmarks", + "pallet-message-queue/runtime-benchmarks", + "pallet-multisig/runtime-benchmarks", + "pallet-nft-fractionalization/runtime-benchmarks", + "pallet-nfts/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "pallet-uniques/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "pallet-xcm-benchmarks/runtime-benchmarks", + "pallet-xcm-bridge-hub-router/runtime-benchmarks", + "pallet-xcm/runtime-benchmarks", + "parachains-common/runtime-benchmarks", + "polkadot-parachain-primitives/runtime-benchmarks", + "polkadot-runtime-common/runtime-benchmarks", + "snowbridge-pallet-xcm-helper/runtime-benchmarks", + "snowbridge-router-primitives/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", + "xcm-fee-payment-runtime-api/runtime-benchmarks", ] try-runtime = [ - "cumulus-pallet-aura-ext/try-runtime", - "cumulus-pallet-parachain-system/try-runtime", - "cumulus-pallet-xcm/try-runtime", - "cumulus-pallet-xcmp-queue/try-runtime", - "frame-executive/try-runtime", - "frame-support/try-runtime", - "frame-system/try-runtime", - "frame-try-runtime/try-runtime", - "pallet-asset-conversion-ops/try-runtime", - "pallet-asset-conversion-tx-payment/try-runtime", - "pallet-asset-conversion/try-runtime", - "pallet-assets/try-runtime", - "pallet-aura/try-runtime", - "pallet-authorship/try-runtime", - "pallet-balances/try-runtime", - "pallet-collator-selection/try-runtime", - "pallet-message-queue/try-runtime", - "pallet-multisig/try-runtime", - "pallet-nft-fractionalization/try-runtime", - "pallet-nfts/try-runtime", - "pallet-proxy/try-runtime", - "pallet-session/try-runtime", - "pallet-timestamp/try-runtime", - "pallet-transaction-payment/try-runtime", - "pallet-uniques/try-runtime", - "pallet-utility/try-runtime", - "pallet-xcm-bridge-hub-router/try-runtime", - "pallet-xcm/try-runtime", - "parachain-info/try-runtime", - "polkadot-runtime-common/try-runtime", - "sp-runtime/try-runtime", - "snowbridge-pallet-xcm-helper/try-runtime", + "cumulus-pallet-aura-ext/try-runtime", + "cumulus-pallet-parachain-system/try-runtime", + "cumulus-pallet-xcm/try-runtime", + "cumulus-pallet-xcmp-queue/try-runtime", + "frame-executive/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "frame-try-runtime/try-runtime", + "pallet-asset-conversion-ops/try-runtime", + "pallet-asset-conversion-tx-payment/try-runtime", + "pallet-asset-conversion/try-runtime", + "pallet-assets/try-runtime", + "pallet-aura/try-runtime", + "pallet-authorship/try-runtime", + "pallet-balances/try-runtime", + "pallet-collator-selection/try-runtime", + "pallet-message-queue/try-runtime", + "pallet-multisig/try-runtime", + "pallet-nft-fractionalization/try-runtime", + "pallet-nfts/try-runtime", + "pallet-proxy/try-runtime", + "pallet-session/try-runtime", + "pallet-timestamp/try-runtime", + "pallet-transaction-payment/try-runtime", + "pallet-uniques/try-runtime", + "pallet-utility/try-runtime", + "pallet-xcm-bridge-hub-router/try-runtime", + "pallet-xcm/try-runtime", + "parachain-info/try-runtime", + "polkadot-runtime-common/try-runtime", + "snowbridge-pallet-xcm-helper/try-runtime", + "sp-runtime/try-runtime", ] std = [ - "assets-common/std", - "bp-asset-hub-rococo/std", - "bp-asset-hub-westend/std", - "bp-bridge-hub-rococo/std", - "bp-bridge-hub-westend/std", - "codec/std", - "cumulus-pallet-aura-ext/std", - "cumulus-pallet-parachain-system/std", - "cumulus-pallet-session-benchmarking/std", - "cumulus-pallet-xcm/std", - "cumulus-pallet-xcmp-queue/std", - "cumulus-primitives-aura/std", - "cumulus-primitives-core/std", - "cumulus-primitives-storage-weight-reclaim/std", - "cumulus-primitives-utility/std", - "frame-benchmarking?/std", - "frame-executive/std", - "frame-metadata-hash-extension/std", - "frame-support/std", - "frame-system-benchmarking?/std", - "frame-system-rpc-runtime-api/std", - "frame-system/std", - "frame-try-runtime?/std", - "log/std", - "pallet-asset-conversion-ops/std", - "pallet-asset-conversion-tx-payment/std", - "pallet-asset-conversion/std", - "pallet-assets/std", - "pallet-aura/std", - "pallet-authorship/std", - "pallet-balances/std", - "pallet-collator-selection/std", - "pallet-message-queue/std", - "pallet-multisig/std", - "pallet-nft-fractionalization/std", - "pallet-nfts-runtime-api/std", - "pallet-nfts/std", - "pallet-proxy/std", - "pallet-session/std", - "pallet-timestamp/std", - "pallet-transaction-payment-rpc-runtime-api/std", - "pallet-transaction-payment/std", - "pallet-uniques/std", - "pallet-utility/std", - "pallet-xcm-benchmarks?/std", - "pallet-xcm-bridge-hub-router/std", - "pallet-xcm/std", - "parachain-info/std", - "parachains-common/std", - "polkadot-parachain-primitives/std", - "polkadot-runtime-common/std", - "primitive-types/std", - "rococo-runtime-constants/std", - "scale-info/std", - "snowbridge-pallet-xcm-helper/std", - "snowbridge-router-primitives/std", - "sp-api/std", - "sp-block-builder/std", - "sp-consensus-aura/std", - "sp-core/std", - "sp-genesis-builder/std", - "sp-inherents/std", - "sp-offchain/std", - "sp-runtime/std", - "sp-session/std", - "sp-std/std", - "sp-storage/std", - "sp-transaction-pool/std", - "sp-version/std", - "sp-weights/std", - "substrate-wasm-builder", - "testnet-parachains-constants/std", - "xcm-builder/std", - "xcm-executor/std", - "xcm-fee-payment-runtime-api/std", - "xcm/std", + "assets-common/std", + "bp-asset-hub-rococo/std", + "bp-asset-hub-westend/std", + "bp-bridge-hub-rococo/std", + "bp-bridge-hub-westend/std", + "codec/std", + "cumulus-pallet-aura-ext/std", + "cumulus-pallet-parachain-system/std", + "cumulus-pallet-session-benchmarking/std", + "cumulus-pallet-xcm/std", + "cumulus-pallet-xcmp-queue/std", + "cumulus-primitives-aura/std", + "cumulus-primitives-core/std", + "cumulus-primitives-storage-weight-reclaim/std", + "cumulus-primitives-utility/std", + "frame-benchmarking?/std", + "frame-executive/std", + "frame-metadata-hash-extension/std", + "frame-support/std", + "frame-system-benchmarking?/std", + "frame-system-rpc-runtime-api/std", + "frame-system/std", + "frame-try-runtime?/std", + "log/std", + "pallet-asset-conversion-ops/std", + "pallet-asset-conversion-tx-payment/std", + "pallet-asset-conversion/std", + "pallet-assets/std", + "pallet-aura/std", + "pallet-authorship/std", + "pallet-balances/std", + "pallet-collator-selection/std", + "pallet-message-queue/std", + "pallet-multisig/std", + "pallet-nft-fractionalization/std", + "pallet-nfts-runtime-api/std", + "pallet-nfts/std", + "pallet-proxy/std", + "pallet-session/std", + "pallet-timestamp/std", + "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-transaction-payment/std", + "pallet-uniques/std", + "pallet-utility/std", + "pallet-xcm-benchmarks?/std", + "pallet-xcm-bridge-hub-router/std", + "pallet-xcm/std", + "parachain-info/std", + "parachains-common/std", + "polkadot-parachain-primitives/std", + "polkadot-runtime-common/std", + "primitive-types/std", + "rococo-runtime-constants/std", + "scale-info/std", + "snowbridge-pallet-xcm-helper/std", + "snowbridge-router-primitives/std", + "sp-api/std", + "sp-block-builder/std", + "sp-consensus-aura/std", + "sp-core/std", + "sp-genesis-builder/std", + "sp-inherents/std", + "sp-offchain/std", + "sp-runtime/std", + "sp-session/std", + "sp-std/std", + "sp-storage/std", + "sp-transaction-pool/std", + "sp-version/std", + "sp-weights/std", + "substrate-wasm-builder", + "testnet-parachains-constants/std", + "xcm-builder/std", + "xcm-executor/std", + "xcm-fee-payment-runtime-api/std", + "xcm/std", ] # Enable the metadata hash generation in the wasm builder. diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs index 5960ab7b55054..733126c034613 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/tests/snowbridge.rs @@ -25,7 +25,7 @@ use bridge_hub_rococo_runtime::{ SignedExtra, UncheckedExtrinsic, }; use codec::{Decode, Encode}; -use cumulus_primitives_core::XcmError::{FailedToTransactAsset, NotHoldingFees}; +use cumulus_primitives_core::XcmError::{FailedToTransactAsset, TooExpensive}; use frame_support::parameter_types; use parachains_common::{AccountId, AuraId, Balance}; use snowbridge_pallet_ethereum_client::WeightInfo; @@ -90,8 +90,8 @@ pub fn transfer_token_to_ethereum_fee_not_enough() { H160::random(), H160::random(), // fee not enough - 1_000_000_000, - NotHoldingFees, + 4_000_000, + TooExpensive, ) } From 0fe04840599aa98798bf410232a35717cbc74886 Mon Sep 17 00:00:00 2001 From: ron Date: Wed, 17 Jul 2024 23:20:15 +0800 Subject: [PATCH 09/15] Use transfer_assets_using_type_and_then --- .../bridge-hub-rococo/src/tests/mod.rs | 1 + .../bridge-hub-rococo/src/tests/snowbridge.rs | 51 +++-- .../src/tests/snowbridge_transfer_v2.rs | 187 ++++++++++++++++++ 3 files changed, 219 insertions(+), 20 deletions(-) create mode 100644 cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge_transfer_v2.rs diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs index 88dad06434b0d..c05cdeb000a2f 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/mod.rs @@ -18,6 +18,7 @@ use crate::imports::*; mod asset_transfers; mod send_xcm; mod snowbridge; +mod snowbridge_transfer_v2; mod teleport; pub(crate) fn asset_hub_westend_location() -> Location { diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs index 832e6a1b94b00..9b5f0feeb0150 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -42,7 +42,6 @@ const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e"); const INSUFFICIENT_XCM_FEE: u128 = 1000; const XCM_FEE: u128 = 4_000_000_000; -const ETHEREUM_EXECUTION_FEE: u128 = 2_750_872_500_000; #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] pub enum ControlCall { @@ -434,29 +433,41 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, ] ); - let asset = Asset { - id: AssetId(Location::new( - 2, - [ - GlobalConsensus(Ethereum { chain_id: CHAIN_ID }), - AccountKey20 { network: None, key: WETH }, - ], - )), - fun: Fungible(WETH_AMOUNT), - }; + let weth_asset_id = AssetId(Location::new( + 2, + [ + GlobalConsensus(Ethereum { chain_id: CHAIN_ID }), + AccountKey20 { network: None, key: WETH }, + ], + )); + let assets = vec![Asset { id: weth_asset_id.clone(), fun: Fungible(WETH_AMOUNT) }]; + let multi_assets = VersionedAssets::V4(Assets::from(assets)); + + let destination = VersionedLocation::V4(Location::new( + 2, + [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })], + )); + + let beneficiary = Location::new( + 0, + [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], + ); let free_balance_before = ::Balances::free_balance( AssetHubRococoReceiver::get(), ); - - // Todo: Change fee asset to WETH and remove the exchange_rate config on BH - let fee_asset: Asset = (AssetId::from(Location::parent()), ETHEREUM_EXECUTION_FEE).into(); // Send the Weth back to Ethereum - ::SnowbridgeXcmHelper::transfer_to_ethereum( + let custom_xcm_on_dest = + Xcm::<()>(vec![DepositAsset { assets: Wild(AllCounted(1)), beneficiary }]); + ::PolkadotXcm::transfer_assets_using_type_and_then( RuntimeOrigin::signed(AssetHubRococoReceiver::get()), - ETHEREUM_DESTINATION_ADDRESS.into(), - Box::new(VersionedAsset::V4(asset)), - Box::new(VersionedAsset::V4(fee_asset)), + Box::new(destination), + Box::new(multi_assets), + bx!(TransferType::DestinationReserve), + bx!(VersionedAssetId::from(weth_asset_id)), + bx!(TransferType::DestinationReserve), + bx!(VersionedXcm::from(custom_xcm_on_dest)), + Unlimited, ) .unwrap(); let free_balance_after = ::Balances::free_balance( @@ -474,8 +485,8 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { assert_expected_events!( BridgeHubRococo, vec![ - RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued -{..}) => {}, ] + RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued {..}) => {}, + ] ); let events = BridgeHubRococo::events(); // Check that the local fee was credited to the Snowbridge sovereign account diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge_transfer_v2.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge_transfer_v2.rs new file mode 100644 index 0000000000000..9e03ddf370bcf --- /dev/null +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge_transfer_v2.rs @@ -0,0 +1,187 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use crate::imports::*; +use bridge_hub_rococo_runtime::{EthereumBeaconClient, EthereumInboundQueue, RuntimeOrigin}; +use codec::{Decode, Encode}; +use emulated_integration_tests_common::xcm_emulator::ConvertLocation; +use frame_support::pallet_prelude::TypeInfo; +use hex_literal::hex; +use rococo_system_emulated_network::penpal_emulated_chain::CustomizableAssetFromSystemAssetHub; +use rococo_westend_system_emulated_network::BridgeHubRococoParaSender as BridgeHubRococoSender; +use snowbridge_core::{inbound::InboundQueueFixture, outbound::OperatingMode}; +use snowbridge_pallet_inbound_queue_fixtures::{ + register_token::make_register_token_message, send_token::make_send_token_message, + send_token_to_penpal::make_send_token_to_penpal_message, +}; +use snowbridge_pallet_system; +use snowbridge_router_primitives::inbound::{ + Command, ConvertMessage, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, + VersionedMessage, +}; +use sp_core::H256; +use sp_runtime::{DispatchError::Token, TokenError::FundsUnavailable}; +use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; + +const INITIAL_FUND: u128 = 5_000_000_000 * ROCOCO_ED; +const CHAIN_ID: u64 = 11155111; +const TREASURY_ACCOUNT: [u8; 32] = + hex!("6d6f646c70792f74727372790000000000000000000000000000000000000000"); +const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); +const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e"); +const INSUFFICIENT_XCM_FEE: u128 = 1000; +const XCM_FEE: u128 = 4_000_000_000; +const ETHEREUM_EXECUTION_FEE: u128 = 2_750_872_500_000; + +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub enum ControlCall { + #[codec(index = 3)] + CreateAgent, + #[codec(index = 4)] + CreateChannel { mode: OperatingMode }, +} + +#[allow(clippy::large_enum_variant)] +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +pub enum SnowbridgeControl { + #[codec(index = 83)] + Control(ControlCall), +} + +pub fn send_inbound_message(fixture: InboundQueueFixture) -> DispatchResult { + EthereumBeaconClient::store_finalized_header( + fixture.finalized_header, + fixture.block_roots_root, + ) + .unwrap(); + EthereumInboundQueue::submit( + RuntimeOrigin::signed(BridgeHubRococoSender::get()), + fixture.message, + ) +} + +/// Tests the full cycle of token transfers: +/// - registering a token on AssetHub +/// - sending a token to AssetHub +/// - returning the token to Ethereum +#[test] +fn send_weth_asset_from_asset_hub_to_ethereum() { + use asset_hub_rococo_runtime::xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; + let assethub_location = BridgeHubRococo::sibling_location_of(AssetHubRococo::para_id()); + let assethub_sovereign = BridgeHubRococo::sovereign_account_id_of(assethub_location); + + AssetHubRococo::force_default_xcm_version(Some(XCM_VERSION)); + BridgeHubRococo::force_default_xcm_version(Some(XCM_VERSION)); + AssetHubRococo::force_xcm_version( + Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]), + XCM_VERSION, + ); + + BridgeHubRococo::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]); + AssetHubRococo::fund_accounts(vec![(AssetHubRococoReceiver::get(), INITIAL_FUND)]); + + const WETH_AMOUNT: u128 = 1_000_000_000; + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Construct RegisterToken message and sent to inbound queue + send_inbound_message(make_register_token_message()).unwrap(); + + // Check that the register token message was sent using xcm + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + + // Construct SendToken message and sent to inbound queue + send_inbound_message(make_send_token_message()).unwrap(); + + // Check that the send token message was sent using xcm + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, + ] + ); + }); + + AssetHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type RuntimeOrigin = ::RuntimeOrigin; + + // Check that AssetHub has issued the foreign asset + assert_expected_events!( + AssetHubRococo, + vec![ + RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, + ] + ); + let asset = Asset { + id: AssetId(Location::new( + 2, + [ + GlobalConsensus(Ethereum { chain_id: CHAIN_ID }), + AccountKey20 { network: None, key: WETH }, + ], + )), + fun: Fungible(WETH_AMOUNT), + }; + + let free_balance_before = ::Balances::free_balance( + AssetHubRococoReceiver::get(), + ); + + // Todo: Change fee asset to WETH and remove the exchange_rate config on BH + let fee_asset: Asset = (AssetId::from(Location::parent()), ETHEREUM_EXECUTION_FEE).into(); + // Send the Weth back to Ethereum + ::SnowbridgeXcmHelper::transfer_to_ethereum( + RuntimeOrigin::signed(AssetHubRococoReceiver::get()), + ETHEREUM_DESTINATION_ADDRESS.into(), + Box::new(VersionedAsset::V4(asset)), + Box::new(VersionedAsset::V4(fee_asset)), + ) + .unwrap(); + let free_balance_after = ::Balances::free_balance( + AssetHubRococoReceiver::get(), + ); + // Assert at least DefaultBridgeHubEthereumBaseFee charged from the sender + let free_balance_diff = free_balance_before - free_balance_after; + assert!(free_balance_diff > DefaultBridgeHubEthereumBaseFee::get()); + }); + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + // Check that the transfer token back to Ethereum message was queue in the Ethereum + // Outbound Queue + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued +{..}) => {}, ] + ); + let events = BridgeHubRococo::events(); + // Check that the local fee was credited to the Snowbridge sovereign account + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) + if *who == TREASURY_ACCOUNT.into() && *amount == 16903333 + )), + "Snowbridge sovereign takes local fee." + ); + }); +} From 14894a17bdbd130d3cb69a40d14dbececeda59fd Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 18 Jul 2024 07:48:20 +0800 Subject: [PATCH 10/15] Multiple assets --- .../tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs index 9b5f0feeb0150..25dd4aae08501 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -395,6 +395,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { AssetHubRococo::fund_accounts(vec![(AssetHubRococoReceiver::get(), INITIAL_FUND)]); const WETH_AMOUNT: u128 = 1_000_000_000; + const WETH_FEE_AMOUNT: u128 = 1_000_000_000; BridgeHubRococo::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; @@ -440,7 +441,10 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { AccountKey20 { network: None, key: WETH }, ], )); - let assets = vec![Asset { id: weth_asset_id.clone(), fun: Fungible(WETH_AMOUNT) }]; + let assets = vec![ + Asset { id: weth_asset_id.clone(), fun: Fungible(WETH_FEE_AMOUNT) }, + Asset { id: weth_asset_id.clone(), fun: Fungible(WETH_AMOUNT) }, + ]; let multi_assets = VersionedAssets::V4(Assets::from(assets)); let destination = VersionedLocation::V4(Location::new( From 1e5eae69118b55a6d28894fd9563eacd65470167 Mon Sep 17 00:00:00 2001 From: ron Date: Sun, 4 Aug 2024 11:46:54 +0800 Subject: [PATCH 11/15] Fix tests --- .../src/tests/snowbridge_transfer_v2.rs | 84 +++++-------------- .../assets/asset-hub-rococo/src/xcm_config.rs | 8 +- 2 files changed, 25 insertions(+), 67 deletions(-) diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge_transfer_v2.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge_transfer_v2.rs index 9e03ddf370bcf..fc0c062ed178d 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge_transfer_v2.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge_transfer_v2.rs @@ -13,25 +13,10 @@ // See the License for the specific language governing permissions and // limitations under the License. use crate::imports::*; -use bridge_hub_rococo_runtime::{EthereumBeaconClient, EthereumInboundQueue, RuntimeOrigin}; use codec::{Decode, Encode}; -use emulated_integration_tests_common::xcm_emulator::ConvertLocation; use frame_support::pallet_prelude::TypeInfo; use hex_literal::hex; -use rococo_system_emulated_network::penpal_emulated_chain::CustomizableAssetFromSystemAssetHub; -use rococo_westend_system_emulated_network::BridgeHubRococoParaSender as BridgeHubRococoSender; -use snowbridge_core::{inbound::InboundQueueFixture, outbound::OperatingMode}; -use snowbridge_pallet_inbound_queue_fixtures::{ - register_token::make_register_token_message, send_token::make_send_token_message, - send_token_to_penpal::make_send_token_to_penpal_message, -}; -use snowbridge_pallet_system; -use snowbridge_router_primitives::inbound::{ - Command, ConvertMessage, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, - VersionedMessage, -}; -use sp_core::H256; -use sp_runtime::{DispatchError::Token, TokenError::FundsUnavailable}; +use snowbridge_core::outbound::OperatingMode; use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; const INITIAL_FUND: u128 = 5_000_000_000 * ROCOCO_ED; @@ -40,8 +25,6 @@ const TREASURY_ACCOUNT: [u8; 32] = hex!("6d6f646c70792f74727372790000000000000000000000000000000000000000"); const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e"); -const INSUFFICIENT_XCM_FEE: u128 = 1000; -const XCM_FEE: u128 = 4_000_000_000; const ETHEREUM_EXECUTION_FEE: u128 = 2_750_872_500_000; #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] @@ -59,18 +42,6 @@ pub enum SnowbridgeControl { Control(ControlCall), } -pub fn send_inbound_message(fixture: InboundQueueFixture) -> DispatchResult { - EthereumBeaconClient::store_finalized_header( - fixture.finalized_header, - fixture.block_roots_root, - ) - .unwrap(); - EthereumInboundQueue::submit( - RuntimeOrigin::signed(BridgeHubRococoSender::get()), - fixture.message, - ) -} - /// Tests the full cycle of token transfers: /// - registering a token on AssetHub /// - sending a token to AssetHub @@ -93,43 +64,29 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { const WETH_AMOUNT: u128 = 1_000_000_000; - BridgeHubRococo::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - - // Construct RegisterToken message and sent to inbound queue - send_inbound_message(make_register_token_message()).unwrap(); - - // Check that the register token message was sent using xcm - assert_expected_events!( - BridgeHubRococo, - vec![ - RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, - ] - ); - - // Construct SendToken message and sent to inbound queue - send_inbound_message(make_send_token_message()).unwrap(); - - // Check that the send token message was sent using xcm - assert_expected_events!( - BridgeHubRococo, - vec![ - RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {}, - ] - ); - }); + let weth_asset_location: Location = Location::new( + 2, + [EthereumNetwork::get().into(), AccountKey20 { network: None, key: WETH }], + ); AssetHubRococo::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; type RuntimeOrigin = ::RuntimeOrigin; - // Check that AssetHub has issued the foreign asset - assert_expected_events!( - AssetHubRococo, - vec![ - RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {}, - ] - ); + // Register WETH and Mint some to AssetHubRococoReceiver + assert_ok!(::ForeignAssets::force_create( + RuntimeOrigin::root(), + weth_asset_location.clone().try_into().unwrap(), + AssetHubRococoReceiver::get().into(), + false, + 1, + )); + assert_ok!(::ForeignAssets::mint( + RuntimeOrigin::signed(AssetHubRococoReceiver::get()), + weth_asset_location.clone().try_into().unwrap(), + AssetHubRococoReceiver::get().into(), + WETH_AMOUNT, + )); + let asset = Asset { id: AssetId(Location::new( 2, @@ -145,7 +102,6 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { AssetHubRococoReceiver::get(), ); - // Todo: Change fee asset to WETH and remove the exchange_rate config on BH let fee_asset: Asset = (AssetId::from(Location::parent()), ETHEREUM_EXECUTION_FEE).into(); // Send the Weth back to Ethereum ::SnowbridgeXcmHelper::transfer_to_ethereum( diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index bc6b1137a0396..85dc7321e816d 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -656,9 +656,11 @@ pub mod bridging { use super::*; parameter_types! { - /// User fee for delivery cost on bridge hub. calculated by OutboundQueue::calculate_local_fee - /// The message will be delivered to bridge hub and cached into outbound queue - pub const DefaultBridgeHubEthereumBaseFee: Balance = 4_000_000_000; + /// User fee for ERC20 token transfer back to Ethereum. + /// (initially was calculated by test `OutboundQueue::calculate_fees` - ETH/ROC 1/400 and fee_per_gas 20 GWEI = 2200698000000 + *25%) + /// Needs to be more than fee calculated from DefaultFeeConfig FeeConfigRecord in snowbridge:parachain/pallets/outbound-queue/src/lib.rs + /// Polkadot uses 10 decimals, Kusama and Rococo 12 decimals. + pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000; pub storage BridgeHubEthereumBaseFee: Balance = DefaultBridgeHubEthereumBaseFee::get(); pub SiblingBridgeHubWithEthereumInboundQueueInstance: Location = Location::new( 1, From 0e7fce02c2ae78b4c208a011ffbb3012336b7fbe Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 5 Aug 2024 08:57:19 +0800 Subject: [PATCH 12/15] Revamp outbound router --- .../snowbridge/pallets/xcm-helper/src/lib.rs | 84 +++--- .../snowbridge/pallets/xcm-helper/src/mock.rs | 12 +- .../primitives/router/src/outbound/mod.rs | 259 +++++++++++++++++- .../bridge-hub-rococo/src/tests/snowbridge.rs | 6 +- .../assets/asset-hub-rococo/src/xcm_config.rs | 12 +- .../src/bridge_to_ethereum_config.rs | 12 +- .../bridge-hub-rococo/src/xcm_config.rs | 33 ++- 7 files changed, 360 insertions(+), 58 deletions(-) diff --git a/bridges/snowbridge/pallets/xcm-helper/src/lib.rs b/bridges/snowbridge/pallets/xcm-helper/src/lib.rs index 7e42f47de6ff4..7a8d98b54f627 100644 --- a/bridges/snowbridge/pallets/xcm-helper/src/lib.rs +++ b/bridges/snowbridge/pallets/xcm-helper/src/lib.rs @@ -14,7 +14,7 @@ pub mod pallet { dispatch::{GetDispatchInfo, PostDispatchInfo}, pallet_prelude::*, }; - use frame_system::pallet_prelude::*; + use frame_system::{pallet_prelude::*, unique}; use sp_core::H160; use sp_runtime::traits::Dispatchable; use sp_std::{boxed::Box, vec, vec::Vec}; @@ -56,6 +56,12 @@ pub mod pallet { /// Ethereum's location of this runtime. type Destination: Get; + + /// DeliveryFee for the execution cost on BH + type DeliveryFee: Get; + + /// The location of BH + type Forwarder: Get; } #[pallet::pallet] @@ -96,6 +102,8 @@ pub mod pallet { FeesNotMet, UnweighableMessage, LocalExecutionIncomplete, + InvalidNetwork, + Unroutable, } #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] @@ -120,8 +128,6 @@ pub mod pallet { ) -> DispatchResult { let origin = T::ExecuteXcmOrigin::ensure_origin(origin)?; - let dest = T::Destination::get(); - // construct the inner xcm of ExportMessage let transact = TransactInfo { target, call, gas_limit }; let message = Xcm(vec![Transact { @@ -130,7 +136,7 @@ pub mod pallet { call: transact.encode().into(), }]); - Self::send_xcm(origin, dest, message, None)?; + Self::send_xcm(origin, T::Forwarder::get(), message)?; Ok(()) } @@ -179,7 +185,7 @@ pub mod pallet { &fee, )?; - Self::execute_xcm_transfer(origin, dest, local_xcm, remote_xcm, fee) + Self::execute_xcm_transfer(origin, T::Forwarder::get(), local_xcm, remote_xcm) } } @@ -197,22 +203,12 @@ pub mod pallet { } /// Send xcm to bridge hub with designated fee charged - fn send_xcm( - origin: Location, - dest: Location, - remote_xcm: Xcm<()>, - remote_fee: Option, - ) -> DispatchResult { + fn send_xcm(origin: Location, dest: Location, remote_xcm: Xcm<()>) -> DispatchResult { let (ticket, delivery_fee) = validate_send::(dest.clone(), remote_xcm.clone()) .map_err(|_| Error::::InvalidXcm)?; Self::charge_fees(origin.clone(), delivery_fee).map_err(|_| Error::::FeesNotMet)?; - if let Some(execution_fee) = remote_fee { - Self::charge_fees(origin.clone(), execution_fee.into()) - .map_err(|_| Error::::FeesNotMet)?; - } - let message_id = T::XcmRouter::deliver(ticket).map_err(|_| Error::::SendFailure)?; Self::deposit_event(Event::Sent { origin, @@ -230,7 +226,6 @@ pub mod pallet { dest: Location, mut local_xcm: Xcm<::RuntimeCall>, remote_xcm: Xcm<()>, - fee: Asset, ) -> DispatchResult { log::debug!( target: "xcm::transfer_to_ethereum", @@ -251,7 +246,7 @@ pub mod pallet { Self::deposit_event(Event::Attempted { outcome: outcome.clone() }); outcome.ensure_complete().map_err(|_| Error::::LocalExecutionIncomplete)?; - Self::send_xcm(origin, dest, remote_xcm, Some(fee))?; + Self::send_xcm(origin, dest, remote_xcm)?; Ok(()) } @@ -300,7 +295,8 @@ pub mod pallet { asset: &Asset, fee: &Asset, ) -> Result<(Xcm<::RuntimeCall>, Xcm<()>), Error> { - let assets: Assets = asset.clone().into(); + let assets: Assets = vec![asset.clone()].into(); + let burn_assets: Assets = vec![fee.clone(), T::DeliveryFee::get()].into(); let context = T::UniversalLocation::get(); let mut reanchored_assets = assets.clone(); @@ -317,6 +313,10 @@ pub mod pallet { let local_execute_xcm = Xcm(vec![ // locally move `assets` to `dest`s local sovereign account TransferAsset { assets, beneficiary: dest.clone() }, + // withdraw reserve-based assets + WithdrawAsset(burn_assets.clone()), + // burn reserve-based assets + BurnAsset(burn_assets), ]); // XCM instructions to be executed on bridge hub let xcm_on_dest = Xcm(vec![ @@ -333,24 +333,15 @@ pub mod pallet { /// Construct Xcm for Ethereum native asset fn destination_reserve_transfer_programs( - _origin: Location, + origin: Location, dest: Location, beneficiary: Location, asset: &Asset, fee: &Asset, ) -> Result<(Xcm<::RuntimeCall>, Xcm<()>), Error> { - let assets: Assets = asset.clone().into(); - let context = T::UniversalLocation::get(); + let assets: Assets = vec![asset.clone(), fee.clone(), T::DeliveryFee::get()].into(); - let mut reanchored_assets = assets.clone(); - reanchored_assets - .reanchor(&dest, &context) - .map_err(|_| Error::::CannotReanchor)?; - - let mut reanchored_fee = fee.clone(); - reanchored_fee = reanchored_fee - .reanchored(&dest, &context) - .map_err(|_| Error::::CannotReanchor)?; + let transfeable_assets: Assets = vec![asset.clone(), fee.clone()].into(); // XCM instructions to be executed on local chain let local_execute_xcm = Xcm(vec![ @@ -359,15 +350,38 @@ pub mod pallet { // burn reserve-based assets BurnAsset(assets), ]); - // XCM instructions to be executed on bridge hub - let xcm_on_dest = Xcm(vec![ + + let network: NetworkId = match T::Destination::get() { + Location { parents: 2, interior: Junctions::X1(junction) } => + match junction.first() { + Some(&GlobalConsensus(network_id)) => Ok(network_id), + _ => Err(Error::::InvalidNetwork), + }, + _ => Err(Error::::InvalidNetwork), + }?; + + let mut inner_xcm = Xcm(vec![ // withdraw `assets` from origin chain's sovereign account - WithdrawAsset(reanchored_assets), + WithdrawAsset(transfeable_assets), // following instructions are not exec'ed on behalf of origin chain anymore ClearOrigin, - BuyExecution { fees: reanchored_fee, weight_limit: Unlimited }, + BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, DepositAsset { assets: Wild(AllCounted(1)), beneficiary }, ]); + let unique_id = unique(&inner_xcm); + inner_xcm.0.push(SetTopic(unique_id)); + + // XCM instructions to be executed on bridge hub + let xcm_on_dest = Xcm(vec![ + DescendOrigin(origin.clone().interior), + ReceiveTeleportedAsset(vec![T::DeliveryFee::get()].into()), + BuyExecution { fees: T::DeliveryFee::get().into(), weight_limit: Unlimited }, + SetAppendix(Xcm(vec![DepositAsset { + assets: AllCounted(1).into(), + beneficiary: origin.clone(), + }])), + ExportMessage { network, destination: dest.interior, xcm: inner_xcm }, + ]); Ok((local_execute_xcm, xcm_on_dest)) } diff --git a/bridges/snowbridge/pallets/xcm-helper/src/mock.rs b/bridges/snowbridge/pallets/xcm-helper/src/mock.rs index 4a23bcf75a361..b8d498f77d38a 100644 --- a/bridges/snowbridge/pallets/xcm-helper/src/mock.rs +++ b/bridges/snowbridge/pallets/xcm-helper/src/mock.rs @@ -22,8 +22,8 @@ use xcm_builder::{ ChildParachainAsNative, ChildParachainConvertsVia, ChildSystemParachainAsSuperuser, DescribeAllTerminal, FixedRateOfFungible, FixedWeightBounds, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, HashedDescription, IsConcrete, MatchedConvertedConcreteId, - NoChecking, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, - TakeWeightCredit, XcmFeeManagerFromComponents, XcmFeeToAccount, + NoChecking, SendXcmFeeToAccount, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, TakeWeightCredit, XcmFeeManagerFromComponents, }; use xcm_executor::{ traits::{Identity, JustTry}, @@ -367,7 +367,7 @@ parameter_types! { pub TrustedUsdc: (AssetFilter, Location) = (Usdc::get().into(), UsdcReserveLocation::get()); pub const MaxInstructions: u32 = 100; pub const MaxAssetsIntoHolding: u32 = 64; - pub XcmFeesTargetAccount: AccountId = AccountId::new([167u8; 32]); + pub TreasuryAccount: AccountId = AccountId::new([167u8; 32]); } pub const XCM_FEES_NOT_WAIVED_USER_ACCOUNT: [u8; 32] = [37u8; 32]; @@ -418,7 +418,7 @@ impl xcm_executor::Config for XcmConfig { type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type FeeManager = XcmFeeManagerFromComponents< EverythingBut, - XcmFeeToAccount, + SendXcmFeeToAccount, >; type MessageExporter = (); type UniversalAliases = Nothing; @@ -439,6 +439,8 @@ parameter_types! { parents: 2, interior: Junctions::from([GlobalConsensus(Ethereum { chain_id: 11155111 })]), }; + pub BridgeHub: Location = Location { parents:1, interior: Junctions::from([Parachain(1013)])}; + pub DeliveryFee: Asset = Asset::from((Location::parent(),48_000_000)); } impl Config for Test { @@ -451,6 +453,8 @@ impl Config for Test { type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type Destination = EthereumLocation; + type DeliveryFee = DeliveryFee; + type Forwarder = BridgeHub; } impl origin::Config for Test {} diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index ba38567ddb925..5ed3cb56c13b1 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -51,7 +51,7 @@ where return Err(SendError::NotApplicable) } - let dest = destination.take().ok_or(SendError::MissingArgument)?; + let dest = destination.clone().take().ok_or(SendError::MissingArgument)?; if dest != Here { log::trace!(target: "xcm::ethereum_blob_exporter", "skipped due to unmatched remote destination {dest:?}."); return Err(SendError::NotApplicable) @@ -117,7 +117,7 @@ where })?; // convert fee to Asset - let fee = Asset::from((Location::parent(), fee.local)).into(); + let fee = Asset::from((Location::parent(), fee.total())).into(); Ok(((ticket.encode(), message_id), fee)) } @@ -153,6 +153,8 @@ enum XcmConverterError { BeneficiaryResolutionFailed, AssetResolutionFailed, SetTopicExpected, + FeeAssetExpected, + FeeAssetInvalid, } macro_rules! match_expression { @@ -272,3 +274,256 @@ impl<'a, Call> XcmConverter<'a, Call> { } } } + +pub mod v2 { + use super::*; + const TARGET: &str = "ethereum_blob_exporter_v2"; + pub struct EthereumBlobExporter< + UniversalLocation, + EthereumNetwork, + OutboundQueue, + AgentHashedDescription, + >(PhantomData<(UniversalLocation, EthereumNetwork, OutboundQueue, AgentHashedDescription)>); + + impl ExportXcm + for EthereumBlobExporter< + UniversalLocation, + EthereumNetwork, + OutboundQueue, + AgentHashedDescription, + > + where + UniversalLocation: Get, + EthereumNetwork: Get, + OutboundQueue: SendMessage, + AgentHashedDescription: ConvertLocation, + { + type Ticket = (Vec, XcmHash); + + fn validate( + network: NetworkId, + _channel: u32, + universal_source: &mut Option, + destination: &mut Option, + message: &mut Option>, + ) -> SendResult { + let expected_network = EthereumNetwork::get(); + let universal_location = UniversalLocation::get(); + + if network != expected_network { + log::trace!(target: TARGET, "skipped due to unmatched bridge network {network:?}."); + return Err(SendError::NotApplicable) + } + + let destination = destination.clone(); + + let dest = destination.ok_or(SendError::MissingArgument)?; + if dest != Junctions::from([GlobalConsensus(network)]) { + log::trace!(target: TARGET, "skipped due to unmatched remote destination {dest:?}."); + return Err(SendError::NotApplicable) + } + + let (local_net, local_sub) = universal_source + .take() + .ok_or_else(|| { + log::error!(target: TARGET, "universal source not provided."); + SendError::MissingArgument + })? + .split_global() + .map_err(|()| { + log::error!(target: TARGET, "could not get global consensus from universal source '{universal_source:?}'."); + SendError::Unroutable + })?; + + if Ok(local_net) != universal_location.global_consensus() { + log::trace!(target: TARGET, "skipped due to unmatched relay network {local_net:?}."); + return Err(SendError::NotApplicable) + } + + let para_id = match local_sub.as_slice() { + [Parachain(para_id), AccountId32 { .. }] => *para_id, + _ => { + log::error!(target: TARGET, "could not get parachain id from universal source '{local_sub:?}'."); + return Err(SendError::MissingArgument) + }, + }; + + let message = message.take().ok_or_else(|| { + log::error!(target: TARGET, "xcm message not provided."); + SendError::MissingArgument + })?; + + let mut converter = XcmConverter::new(&message, &expected_network); + let (agent_execute_command, message_id) = converter.convert().map_err(|err| { + log::error!(target: TARGET, "unroutable due to pattern matching error '{err:?}'."); + SendError::Unroutable + })?; + + let source_location = Location::new(1, local_sub.clone()); + let agent_id = match AgentHashedDescription::convert_location(&source_location) { + Some(id) => id, + None => { + log::error!(target: TARGET, "unroutable due to not being able to create agent id. '{source_location:?}'"); + return Err(SendError::Unroutable) + }, + }; + + let channel_id: ChannelId = ParaId::from(para_id).into(); + + let outbound_message = Message { + id: Some(message_id.into()), + channel_id, + command: Command::AgentExecute { agent_id, command: agent_execute_command }, + }; + + // validate the message + let (ticket, fee) = OutboundQueue::validate(&outbound_message).map_err(|err| { + log::error!(target: TARGET, "OutboundQueue validation of message failed. {err:?}"); + SendError::Unroutable + })?; + + // convert fee to Asset + let fee = Asset::from((Location::parent(), fee.local)).into(); + + Ok(((ticket.encode(), message_id), fee)) + } + + fn deliver(blob: (Vec, XcmHash)) -> Result { + let ticket: OutboundQueue::Ticket = OutboundQueue::Ticket::decode(&mut blob.0.as_ref()) + .map_err(|_| { + log::trace!(target: TARGET, "undeliverable due to decoding error"); + SendError::NotApplicable + })?; + + let message_id = OutboundQueue::deliver(ticket).map_err(|_| { + log::error!(target: TARGET, "OutboundQueue submit of message failed"); + SendError::Transport("other transport error") + })?; + + log::info!(target: TARGET, "message delivered {message_id:#?}."); + Ok(message_id.into()) + } + } + + struct XcmConverter<'a, Call> { + iter: Peekable>>, + ethereum_network: &'a NetworkId, + } + impl<'a, Call> XcmConverter<'a, Call> { + fn new(message: &'a Xcm, ethereum_network: &'a NetworkId) -> Self { + Self { iter: message.inner().iter().peekable(), ethereum_network } + } + + fn convert(&mut self) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { + // Get withdraw/deposit and make native tokens create message. + let result = self.native_tokens_unlock_message()?; + + // All xcm instructions must be consumed before exit. + if self.next().is_ok() { + return Err(XcmConverterError::EndOfXcmMessageExpected) + } + + Ok(result) + } + + fn native_tokens_unlock_message( + &mut self, + ) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { + use XcmConverterError::*; + + // Get the reserve assets from WithdrawAsset. + let reserve_assets = + match_expression!(self.next()?, WithdrawAsset(reserve_assets), reserve_assets) + .ok_or(WithdrawAssetExpected)?; + + // Check if clear origin exists and skip over it. + if match_expression!(self.peek(), Ok(ClearOrigin), ()).is_some() { + let _ = self.next(); + } + + // Get the fee asset item from BuyExecution. + let fee_asset = match_expression!(self.next()?, BuyExecution { fees, .. }, fees) + .ok_or(FeeAssetExpected)?; + ensure!(fee_asset.clone().id == AssetId::from(Location::parent()), FeeAssetInvalid); + let fee_amount = match fee_asset.clone().fun { + Fungible(fee_amount) => Ok(fee_amount), + _ => Err(FeeAssetInvalid), + }?; + let reserve_fee_asset = reserve_assets.get(0).ok_or(AssetResolutionFailed)?; + ensure!( + reserve_fee_asset.clone().id == AssetId::from(Location::parent()), + FeeAssetInvalid + ); + let reserve_fee_amount = match reserve_fee_asset.clone().fun { + Fungible(fee_amount) => Ok(fee_amount), + _ => Err(FeeAssetInvalid), + }?; + ensure!(fee_amount <= reserve_fee_amount, FeeAssetInvalid); + + let (deposit_assets, beneficiary) = match_expression!( + self.next()?, + DepositAsset { assets, beneficiary }, + (assets, beneficiary) + ) + .ok_or(DepositAssetExpected)?; + + // assert that the beneficiary is AccountKey20. + let recipient = match_expression!( + beneficiary.unpack(), + (0, [AccountKey20 { network, key }]) + if self.network_matches(network), + H160(*key) + ) + .ok_or(BeneficiaryResolutionFailed)?; + + // Make sure there are reserved assets. + if reserve_assets.len() == 0 { + return Err(NoReserveAssets) + } + + // Check the the deposit asset filter matches what was reserved. + if reserve_assets.inner().iter().any(|asset| !deposit_assets.matches(asset)) { + return Err(FilterDoesNotConsumeAllAssets) + } + + let reserve_asset = reserve_assets.get(1).ok_or(AssetResolutionFailed)?; + + let (token, amount) = match reserve_asset { + Asset { id: AssetId(inner_location), fun: Fungible(amount) } => + match inner_location.unpack() { + (2, [GlobalConsensus(network), AccountKey20 { key, .. }]) + if self.network_matches(&Some(network.clone())) => + Some((H160(*key), *amount)), + _ => None, + }, + _ => None, + } + .ok_or(AssetResolutionFailed)?; + + // transfer amount must be greater than 0. + ensure!(amount > 0, ZeroAssetTransfer); + + // Check if there is a SetTopic and skip over it if found. + let topic_id = + match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; + + Ok((AgentExecuteCommand::TransferToken { token, recipient, amount }, *topic_id)) + } + + fn next(&mut self) -> Result<&'a Instruction, XcmConverterError> { + self.iter.next().ok_or(XcmConverterError::UnexpectedEndOfXcm) + } + + fn peek(&mut self) -> Result<&&'a Instruction, XcmConverterError> { + self.iter.peek().ok_or(XcmConverterError::UnexpectedEndOfXcm) + } + + fn network_matches(&self, network: &Option) -> bool { + if let Some(network) = network { + network == self.ethereum_network + } else { + true + } + } + } +} diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs index 9208ce023bec9..f6648fd63886a 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -34,8 +34,6 @@ use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; const INITIAL_FUND: u128 = 5_000_000_000 * ROCOCO_ED; pub const CHAIN_ID: u64 = 11155111; -const TREASURY_ACCOUNT: [u8; 32] = - hex!("6d6f646c70792f74727372790000000000000000000000000000000000000000"); pub const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d"); const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e"); const INSUFFICIENT_XCM_FEE: u128 = 1000; @@ -495,8 +493,8 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { assert!( events.iter().any(|event| matches!( event, - RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount }) - if *who == TREASURY_ACCOUNT.into() && *amount == 16903333 + RuntimeEvent::Balances(pallet_balances::Event::Burned { who, amount }) + if *who == assethub_sovereign.clone().into() && *amount == DefaultBridgeHubEthereumBaseFee::get() )), "Snowbridge sovereign takes local fee." ); diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index 85dc7321e816d..b7cb67e796fe8 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -513,18 +513,14 @@ parameter_types! { parents: 2, interior: Junctions::from([GlobalConsensus(Ethereum { chain_id: 11155111 })]), }; + pub BridgeHub: Location = Location { parents:1, interior: Junctions::from([Parachain(1013)])}; + pub DeliveryFee: Asset = Asset::from((Location::parent(),60_000_000)); } impl snowbridge_pallet_xcm_helper::Config for Runtime { type RuntimeEvent = RuntimeEvent; type ExecuteXcmOrigin = EnsureXcmOrigin; - type XcmRouter = WithUniqueTopic< - SovereignPaidRemoteExporter< - bridging::EthereumNetworkExportTable, - XcmpQueue, - UniversalLocation, - >, - >; + type XcmRouter = WithUniqueTopic; type XcmExecutor = XcmExecutor; type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; @@ -535,6 +531,8 @@ impl snowbridge_pallet_xcm_helper::Config for Runtime { >; type UniversalLocation = UniversalLocation; type Destination = EthereumLocation; + type Forwarder = BridgeHub; + type DeliveryFee = DeliveryFee; } /// All configuration related to bridging diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index a5d798835ac89..8140468547072 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -24,7 +24,10 @@ use crate::{ use parachains_common::{AccountId, Balance}; use snowbridge_beacon_primitives::{Fork, ForkVersions}; use snowbridge_core::{gwei, meth, AllowSiblingsOnly, PricingParameters, Rewards}; -use snowbridge_router_primitives::{inbound::MessageToXcm, outbound::EthereumBlobExporter}; +use snowbridge_router_primitives::{ + inbound::MessageToXcm, + outbound::{v2::EthereumBlobExporter as EthereumBlobExporterV2, EthereumBlobExporter}, +}; use sp_core::H160; use testnet_parachains_constants::rococo::{ currency::*, @@ -49,6 +52,13 @@ pub type SnowbridgeExporter = EthereumBlobExporter< snowbridge_core::AgentIdOf, >; +pub type SnowbridgeExporterV2 = EthereumBlobExporterV2< + UniversalLocation, + EthereumNetwork, + snowbridge_pallet_outbound_queue::Pallet, + snowbridge_core::AgentIdOf, +>; + // Ethereum Bridge parameter_types! { pub storage EthereumGatewayAddress: H160 = H160(hex_literal::hex!("EDa338E4dC46038493b885327842fD3E301CaB39")); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index 0902106e9bf62..f59778aad3a45 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -23,21 +23,23 @@ use bp_messages::LaneId; use bp_relayers::{PayRewardFromAccount, RewardsAccountOwner, RewardsAccountParams}; use bp_runtime::ChainId; use core::marker::PhantomData; +use cumulus_primitives_core::ParaId; use frame_support::{ parameter_types, - traits::{tokens::imbalance::ResolveTo, ConstU32, Contains, Equals, Everything, Nothing}, + traits::{ + tokens::imbalance::ResolveTo, ConstU32, Contains, ContainsPair, Equals, Everything, Nothing, + }, }; use frame_system::EnsureRoot; use pallet_collator_selection::StakingPotAccountId; use pallet_xcm::XcmPassthrough; use parachains_common::{ xcm_config::{ - AllSiblingSystemParachains, ConcreteAssetFromSystem, ParentRelayOrSiblingParachains, - RelayOrOtherSystemParachains, + AllSiblingSystemParachains, ParentRelayOrSiblingParachains, RelayOrOtherSystemParachains, }, TREASURY_PALLET_ID, }; -use polkadot_parachain_primitives::primitives::Sibling; +use polkadot_parachain_primitives::primitives::{IsSystem, Sibling}; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use sp_core::Get; use sp_runtime::traits::AccountIdConversion; @@ -168,9 +170,29 @@ pub type WaivedLocations = ( Equals, ); +pub struct ConcreteAssetFromUserOriginOfSystemChain(PhantomData); +impl> ContainsPair + for ConcreteAssetFromUserOriginOfSystemChain +{ + fn contains(asset: &Asset, origin: &Location) -> bool { + log::trace!(target: "xcm::contains", "ConcreteAssetFromSystem asset: {:?}, origin: {:?}", asset, origin); + let is_system = match origin.unpack() { + // The Relay Chain + (1, []) => true, + // System parachain + (1, [Parachain(id)]) => ParaId::from(*id).is_system(), + // User from system chain + (1, [Parachain(id), AccountId32 { .. }]) => ParaId::from(*id).is_system(), + // Others + _ => false, + }; + asset.id.0 == AssetLocation::get() && is_system + } +} + /// Cases where a remote origin is accepted as trusted Teleporter for a given asset: /// - NativeToken with the parent Relay Chain and sibling parachains. -pub type TrustedTeleporters = ConcreteAssetFromSystem; +pub type TrustedTeleporters = ConcreteAssetFromUserOriginOfSystemChain; pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { @@ -221,6 +243,7 @@ impl xcm_executor::Config for XcmConfig { crate::bridge_to_westend_config::ToBridgeHubWestendHaulBlobExporter, crate::bridge_to_bulletin_config::ToRococoBulletinHaulBlobExporter, crate::bridge_to_ethereum_config::SnowbridgeExporter, + crate::bridge_to_ethereum_config::SnowbridgeExporterV2, ); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; From c38d9be7aaafd451697603dbbcf0e7b6a1719507 Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 5 Aug 2024 09:17:23 +0800 Subject: [PATCH 13/15] For compatible --- .../primitives/router/src/outbound/mod.rs | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index 5ed3cb56c13b1..9b2423a3f1483 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -152,9 +152,8 @@ enum XcmConverterError { ZeroAssetTransfer, BeneficiaryResolutionFailed, AssetResolutionFailed, + InvalidFeeAsset, SetTopicExpected, - FeeAssetExpected, - FeeAssetInvalid, } macro_rules! match_expression { @@ -238,6 +237,14 @@ impl<'a, Call> XcmConverter<'a, Call> { ensure!(reserve_assets.len() == 1, TooManyAssets); let reserve_asset = reserve_assets.get(0).ok_or(AssetResolutionFailed)?; + // If there was a fee specified verify it. + if let Some(fee_asset) = fee_asset { + // The fee asset must be the same as the reserve asset. + if fee_asset.id != reserve_asset.id || fee_asset.fun > reserve_asset.fun { + return Err(InvalidFeeAsset) + } + } + let (token, amount) = match reserve_asset { Asset { id: AssetId(inner_location), fun: Fungible(amount) } => match inner_location.unpack() { @@ -443,22 +450,22 @@ pub mod v2 { // Get the fee asset item from BuyExecution. let fee_asset = match_expression!(self.next()?, BuyExecution { fees, .. }, fees) - .ok_or(FeeAssetExpected)?; - ensure!(fee_asset.clone().id == AssetId::from(Location::parent()), FeeAssetInvalid); + .ok_or(InvalidFeeAsset)?; + ensure!(fee_asset.clone().id == AssetId::from(Location::parent()), InvalidFeeAsset); let fee_amount = match fee_asset.clone().fun { Fungible(fee_amount) => Ok(fee_amount), - _ => Err(FeeAssetInvalid), + _ => Err(InvalidFeeAsset), }?; let reserve_fee_asset = reserve_assets.get(0).ok_or(AssetResolutionFailed)?; ensure!( reserve_fee_asset.clone().id == AssetId::from(Location::parent()), - FeeAssetInvalid + InvalidFeeAsset ); let reserve_fee_amount = match reserve_fee_asset.clone().fun { Fungible(fee_amount) => Ok(fee_amount), - _ => Err(FeeAssetInvalid), + _ => Err(InvalidFeeAsset), }?; - ensure!(fee_amount <= reserve_fee_amount, FeeAssetInvalid); + ensure!(fee_amount <= reserve_fee_amount, InvalidFeeAsset); let (deposit_assets, beneficiary) = match_expression!( self.next()?, From 6e044d951b2f3d532005d4c8ae6a5d917cb4bc36 Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 5 Aug 2024 10:30:03 +0800 Subject: [PATCH 14/15] Fix clippy --- bridges/snowbridge/primitives/router/src/outbound/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index 9b2423a3f1483..623b45b9b5fe6 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -499,7 +499,7 @@ pub mod v2 { Asset { id: AssetId(inner_location), fun: Fungible(amount) } => match inner_location.unpack() { (2, [GlobalConsensus(network), AccountKey20 { key, .. }]) - if self.network_matches(&Some(network.clone())) => + if self.network_matches(&Some(*network)) => Some((H160(*key), *amount)), _ => None, }, From 43c85cc14fe3b9778495755f94c2a6e6c000db22 Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 5 Aug 2024 12:16:14 +0800 Subject: [PATCH 15/15] Revamp router --- .../snowbridge/pallets/xcm-helper/src/lib.rs | 94 +++++++------------ .../primitives/router/src/outbound/mod.rs | 14 +-- 2 files changed, 36 insertions(+), 72 deletions(-) diff --git a/bridges/snowbridge/pallets/xcm-helper/src/lib.rs b/bridges/snowbridge/pallets/xcm-helper/src/lib.rs index 7a8d98b54f627..a28a023784700 100644 --- a/bridges/snowbridge/pallets/xcm-helper/src/lib.rs +++ b/bridges/snowbridge/pallets/xcm-helper/src/lib.rs @@ -17,7 +17,7 @@ pub mod pallet { use frame_system::{pallet_prelude::*, unique}; use sp_core::H160; use sp_runtime::traits::Dispatchable; - use sp_std::{boxed::Box, vec, vec::Vec}; + use sp_std::{boxed::Box, vec}; use xcm::prelude::*; use xcm_executor::traits::{TransferType, WeightBounds, XcmAssetTransfers}; @@ -103,14 +103,6 @@ pub mod pallet { UnweighableMessage, LocalExecutionIncomplete, InvalidNetwork, - Unroutable, - } - - #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] - pub struct TransactInfo { - pub target: H160, - pub call: Vec, - pub gas_limit: u64, } #[pallet::hooks] @@ -119,30 +111,7 @@ pub mod pallet { #[pallet::call] impl Pallet { #[pallet::call_index(0)] - #[pallet::weight(Weight::from_parts(100_000_000, 0))] - pub fn transact_to_ethereum( - origin: OriginFor, - target: H160, - call: Vec, - gas_limit: u64, - ) -> DispatchResult { - let origin = T::ExecuteXcmOrigin::ensure_origin(origin)?; - - // construct the inner xcm of ExportMessage - let transact = TransactInfo { target, call, gas_limit }; - let message = Xcm(vec![Transact { - origin_kind: OriginKind::SovereignAccount, - require_weight_at_most: Weight::default(), - call: transact.encode().into(), - }]); - - Self::send_xcm(origin, T::Forwarder::get(), message)?; - - Ok(()) - } - - #[pallet::call_index(1)] - #[pallet::weight(Weight::from_parts(100_000_000, 0))] + #[pallet::weight(Weight::from_parts(4_000_000_000, 0))] pub fn transfer_to_ethereum( origin: OriginFor, beneficiary: H160, @@ -158,11 +127,9 @@ pub mod pallet { let dest = T::Destination::get(); + // If fungible asset, ensure non-zero amount. let asset: Asset = (*asset).try_into().map_err(|()| Error::::BadVersion)?; - let fee: Asset = (*fee).try_into().map_err(|()| Error::::BadVersion)?; - if let Fungible(x) = asset.fun { - // If fungible asset, ensure non-zero amount. ensure!(x > 0, Error::::Empty); } @@ -170,6 +137,7 @@ pub mod pallet { let asset_transfer_type = T::XcmExecutor::determine_for(&asset, &dest) .map_err(|_| Error::::CannotDetermine)?; + let fee: Asset = (*fee).try_into().map_err(|()| Error::::BadVersion)?; log::debug!( target: "xcm::transfer_to_ethereum", "origin {:?}, dest {:?}, beneficiary {:?}, asset {:?}, fee {:?}, transfer_type {:?}", @@ -289,7 +257,7 @@ pub mod pallet { /// Construct Xcm for Polkadot native asset fn local_reserve_transfer_programs( - _origin: Location, + origin: Location, dest: Location, beneficiary: Location, asset: &Asset, @@ -297,36 +265,46 @@ pub mod pallet { ) -> Result<(Xcm<::RuntimeCall>, Xcm<()>), Error> { let assets: Assets = vec![asset.clone()].into(); let burn_assets: Assets = vec![fee.clone(), T::DeliveryFee::get()].into(); - let context = T::UniversalLocation::get(); - - let mut reanchored_assets = assets.clone(); - reanchored_assets - .reanchor(&dest, &context) - .map_err(|_| Error::::CannotReanchor)?; - - let mut reanchored_fee = fee.clone(); - reanchored_fee = reanchored_fee - .reanchored(&dest, &context) - .map_err(|_| Error::::CannotReanchor)?; // XCM instructions to be executed on local chain let local_execute_xcm = Xcm(vec![ // locally move `assets` to `dest`s local sovereign account - TransferAsset { assets, beneficiary: dest.clone() }, + TransferAsset { assets: assets.clone(), beneficiary: dest.clone() }, // withdraw reserve-based assets WithdrawAsset(burn_assets.clone()), // burn reserve-based assets BurnAsset(burn_assets), ]); - // XCM instructions to be executed on bridge hub - let xcm_on_dest = Xcm(vec![ - // let (dest) chain know assets are in its SA on reserve - ReserveAssetDeposited(reanchored_assets), - // following instructions are not exec'ed on behalf of origin chain anymore + + let network: NetworkId = match T::Destination::get() { + Location { parents: 2, interior: Junctions::X1(junction) } => + match junction.first() { + Some(&GlobalConsensus(network_id)) => Ok(network_id), + _ => Err(Error::::InvalidNetwork), + }, + _ => Err(Error::::InvalidNetwork), + }?; + + let mut inner_xcm = Xcm(vec![ + ReserveAssetDeposited(assets), ClearOrigin, - BuyExecution { fees: reanchored_fee, weight_limit: Unlimited }, + BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, DepositAsset { assets: Wild(AllCounted(1)), beneficiary }, ]); + let unique_id = unique(&inner_xcm); + inner_xcm.0.push(SetTopic(unique_id)); + + // XCM instructions to be executed on bridge hub + let xcm_on_dest = Xcm(vec![ + DescendOrigin(origin.clone().interior), + ReceiveTeleportedAsset(vec![T::DeliveryFee::get()].into()), + BuyExecution { fees: T::DeliveryFee::get().into(), weight_limit: Unlimited }, + SetAppendix(Xcm(vec![DepositAsset { + assets: AllCounted(1).into(), + beneficiary: origin.clone(), + }])), + ExportMessage { network, destination: dest.interior, xcm: inner_xcm }, + ]); Ok((local_execute_xcm, xcm_on_dest)) } @@ -341,8 +319,6 @@ pub mod pallet { ) -> Result<(Xcm<::RuntimeCall>, Xcm<()>), Error> { let assets: Assets = vec![asset.clone(), fee.clone(), T::DeliveryFee::get()].into(); - let transfeable_assets: Assets = vec![asset.clone(), fee.clone()].into(); - // XCM instructions to be executed on local chain let local_execute_xcm = Xcm(vec![ // withdraw reserve-based assets @@ -361,9 +337,7 @@ pub mod pallet { }?; let mut inner_xcm = Xcm(vec![ - // withdraw `assets` from origin chain's sovereign account - WithdrawAsset(transfeable_assets), - // following instructions are not exec'ed on behalf of origin chain anymore + WithdrawAsset(vec![asset.clone()].into()), ClearOrigin, BuyExecution { fees: fee.clone(), weight_limit: Unlimited }, DepositAsset { assets: Wild(AllCounted(1)), beneficiary }, diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index 623b45b9b5fe6..9d493591860ba 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -452,20 +452,10 @@ pub mod v2 { let fee_asset = match_expression!(self.next()?, BuyExecution { fees, .. }, fees) .ok_or(InvalidFeeAsset)?; ensure!(fee_asset.clone().id == AssetId::from(Location::parent()), InvalidFeeAsset); - let fee_amount = match fee_asset.clone().fun { + let _fee_amount = match fee_asset.clone().fun { Fungible(fee_amount) => Ok(fee_amount), _ => Err(InvalidFeeAsset), }?; - let reserve_fee_asset = reserve_assets.get(0).ok_or(AssetResolutionFailed)?; - ensure!( - reserve_fee_asset.clone().id == AssetId::from(Location::parent()), - InvalidFeeAsset - ); - let reserve_fee_amount = match reserve_fee_asset.clone().fun { - Fungible(fee_amount) => Ok(fee_amount), - _ => Err(InvalidFeeAsset), - }?; - ensure!(fee_amount <= reserve_fee_amount, InvalidFeeAsset); let (deposit_assets, beneficiary) = match_expression!( self.next()?, @@ -493,7 +483,7 @@ pub mod v2 { return Err(FilterDoesNotConsumeAllAssets) } - let reserve_asset = reserve_assets.get(1).ok_or(AssetResolutionFailed)?; + let reserve_asset = reserve_assets.get(0).ok_or(AssetResolutionFailed)?; let (token, amount) = match reserve_asset { Asset { id: AssetId(inner_location), fun: Fungible(amount) } =>