diff --git a/Cargo.lock b/Cargo.lock index 17d92e732d60..1005704e4e49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12552,6 +12552,7 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", + "sp-io", "xcm-procedural", ] diff --git a/bridges/bin/rialto-parachain/runtime/src/lib.rs b/bridges/bin/rialto-parachain/runtime/src/lib.rs index 7660597892b0..50caa47ccf21 100644 --- a/bridges/bin/rialto-parachain/runtime/src/lib.rs +++ b/bridges/bin/rialto-parachain/runtime/src/lib.rs @@ -349,7 +349,7 @@ parameter_types! { pub const MaxAuthorities: u32 = 100_000; } -match_type! { +match_types! { pub type ParentOrParentsUnitPlurality: impl Contains = { MultiLocation { parents: 1, interior: Here } | MultiLocation { parents: 1, interior: X1(Plurality { id: BodyId::Unit, .. }) } diff --git a/runtime/common/src/xcm_sender.rs b/runtime/common/src/xcm_sender.rs index f93fa4944dfa..1502c5ec72a3 100644 --- a/runtime/common/src/xcm_sender.rs +++ b/runtime/common/src/xcm_sender.rs @@ -59,8 +59,10 @@ impl SendXcm fn deliver( (config, para, blob): (HostConfiguration, ParaId, Vec), - ) -> Result<(), SendError> { + ) -> Result { + let hash = sp_io::hashing::blake2_256(&blob[..]); >::queue_downward_message(&config, para, blob) + .map(|()| hash) .map_err(|_| SendError::Transport(&"Error placing into DMP queue")) } } diff --git a/runtime/parachains/src/ump.rs b/runtime/parachains/src/ump.rs index 3ccb51a3329c..38e977828499 100644 --- a/runtime/parachains/src/ump.rs +++ b/runtime/parachains/src/ump.rs @@ -133,7 +133,7 @@ impl, C: Config> UmpSink for XcmSi }, Ok((Ok(xcm_message), weight_used)) => { let xcm_junction = Junction::Parachain(origin.into()); - let outcome = XcmExecutor::execute_xcm(xcm_junction, xcm_message, max_weight); + let outcome = XcmExecutor::execute_xcm(xcm_junction, xcm_message, id, max_weight); match outcome { Outcome::Error(XcmError::WeightLimitReached(required)) => Err((id, required)), outcome => { diff --git a/runtime/test-runtime/src/xcm_config.rs b/runtime/test-runtime/src/xcm_config.rs index c6643bc7aeab..cd0ac5137bd0 100644 --- a/runtime/test-runtime/src/xcm_config.rs +++ b/runtime/test-runtime/src/xcm_config.rs @@ -45,8 +45,8 @@ impl SendXcm for DoNothingRouter { fn validate(_dest: &mut Option, _msg: &mut Option>) -> SendResult<()> { Ok(((), MultiAssets::new())) } - fn deliver(_: ()) -> Result<(), SendError> { - Ok(()) + fn deliver(_: ()) -> Result { + Ok([0; 32]) } } @@ -54,11 +54,15 @@ pub type Barrier = AllowUnpaidExecutionFrom; pub struct DummyAssetTransactor; impl TransactAsset for DummyAssetTransactor { - fn deposit_asset(_what: &MultiAsset, _who: &MultiLocation) -> XcmResult { + fn deposit_asset(_what: &MultiAsset, _who: &MultiLocation, _context: &XcmContext) -> XcmResult { Ok(()) } - fn withdraw_asset(_what: &MultiAsset, _who: &MultiLocation) -> Result { + fn withdraw_asset( + _what: &MultiAsset, + _who: &MultiLocation, + _maybe_context: Option<&XcmContext>, + ) -> Result { let asset: MultiAsset = (Parent, 100_000).into(); Ok(asset.into()) } diff --git a/runtime/westend/src/weights/xcm/mod.rs b/runtime/westend/src/weights/xcm/mod.rs index 226328992e0f..85bcd7f60472 100644 --- a/runtime/westend/src/weights/xcm/mod.rs +++ b/runtime/westend/src/weights/xcm/mod.rs @@ -238,4 +238,10 @@ impl XcmWeightInfo for WestendXcmWeight { fn set_fees_mode(_: &bool) -> Weight { Weight::MAX // todo fix } + fn set_topic(_topic: &[u8; 32]) -> Weight { + XcmGeneric::::set_topic() + } + fn clear_topic() -> Weight { + XcmGeneric::::clear_topic() + } } diff --git a/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs b/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs index bd881fd41a81..0c5ab409be77 100644 --- a/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs +++ b/runtime/westend/src/weights/xcm/pallet_xcm_benchmarks_generic.rs @@ -17,11 +17,11 @@ //! Autogenerated weights for `pallet_xcm_benchmarks::generic` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2021-12-20, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 128 +//! DATE: 2022-02-05, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("westend-dev"), DB CACHE: 1024 // Executed Command: -// target/release/polkadot +// target/production/polkadot // benchmark // --chain=westend-dev // --steps=50 @@ -51,38 +51,38 @@ impl WeightInfo { // Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) // Storage: Dmp DownwardMessageQueues (r:1 w:1) pub(crate) fn report_holding() -> Weight { - (35_446_000 as Weight) + (24_000_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } pub(crate) fn buy_execution() -> Weight { - (5_228_000 as Weight) + (3_377_000 as Weight) } // Storage: XcmPallet Queries (r:1 w:0) pub(crate) fn query_response() -> Weight { - (18_708_000 as Weight) + (12_418_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) } pub(crate) fn transact() -> Weight { - (20_401_000 as Weight) + (12_795_000 as Weight) } pub(crate) fn refund_surplus() -> Weight { - (5_238_000 as Weight) + (3_485_000 as Weight) } pub(crate) fn set_error_handler() -> Weight { - (5_104_000 as Weight) + (3_314_000 as Weight) } pub(crate) fn set_appendix() -> Weight { - (5_095_000 as Weight) + (3_334_000 as Weight) } pub(crate) fn clear_error() -> Weight { - (5_010_000 as Weight) + (3_240_000 as Weight) } pub(crate) fn descend_origin() -> Weight { - (6_368_000 as Weight) + (4_339_000 as Weight) } pub(crate) fn clear_origin() -> Weight { - (5_011_000 as Weight) + (3_338_000 as Weight) } // Storage: XcmPallet SupportedVersion (r:1 w:0) // Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1) @@ -90,18 +90,18 @@ impl WeightInfo { // Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) // Storage: Dmp DownwardMessageQueues (r:1 w:1) pub(crate) fn report_error() -> Weight { - (27_823_000 as Weight) + (20_469_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } // Storage: XcmPallet AssetTraps (r:1 w:1) pub(crate) fn claim_asset() -> Weight { - (12_402_000 as Weight) + (7_749_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } pub(crate) fn trap() -> Weight { - (5_022_000 as Weight) + (3_271_000 as Weight) } // Storage: XcmPallet VersionNotifyTargets (r:1 w:1) // Storage: XcmPallet SupportedVersion (r:1 w:0) @@ -110,13 +110,13 @@ impl WeightInfo { // Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) // Storage: Dmp DownwardMessageQueues (r:1 w:1) pub(crate) fn subscribe_version() -> Weight { - (32_492_000 as Weight) + (22_263_000 as Weight) .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } // Storage: XcmPallet VersionNotifyTargets (r:0 w:1) pub(crate) fn unsubscribe_version() -> Weight { - (7_777_000 as Weight) + (5_366_000 as Weight) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } // Storage: XcmPallet SupportedVersion (r:1 w:0) @@ -125,21 +125,21 @@ impl WeightInfo { // Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) // Storage: Dmp DownwardMessageQueues (r:1 w:1) pub(crate) fn initiate_reserve_withdraw() -> Weight { - (37_066_000 as Weight) + (23_659_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } pub(crate) fn burn_asset() -> Weight { - (7_935_000 as Weight) + (4_910_000 as Weight) } pub(crate) fn expect_asset() -> Weight { - (5_237_000 as Weight) + (3_488_000 as Weight) } pub(crate) fn expect_origin() -> Weight { - (5_245_000 as Weight) + (3_400_000 as Weight) } pub(crate) fn expect_error() -> Weight { - (5_062_000 as Weight) + (3_358_000 as Weight) } // Storage: XcmPallet SupportedVersion (r:1 w:0) // Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1) @@ -147,12 +147,12 @@ impl WeightInfo { // Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) // Storage: Dmp DownwardMessageQueues (r:1 w:1) pub(crate) fn query_pallet() -> Weight { - (28_876_000 as Weight) + (21_841_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } pub(crate) fn expect_pallet() -> Weight { - (5_526_000 as Weight) + (3_716_000 as Weight) } // Storage: XcmPallet SupportedVersion (r:1 w:0) // Storage: XcmPallet VersionDiscoveryQueue (r:1 w:1) @@ -160,11 +160,17 @@ impl WeightInfo { // Storage: Dmp DownwardMessageQueueHeads (r:1 w:1) // Storage: Dmp DownwardMessageQueues (r:1 w:1) pub(crate) fn report_transact_status() -> Weight { - (27_889_000 as Weight) + (20_503_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } pub(crate) fn clear_transact_status() -> Weight { - (5_100_000 as Weight) + (3_270_000 as Weight) + } + pub(crate) fn set_topic() -> Weight { + (3_269_000 as Weight) + } + pub(crate) fn clear_topic() -> Weight { + (3_268_000 as Weight) } } diff --git a/xcm/Cargo.toml b/xcm/Cargo.toml index 9cc64e0f0980..96caab669cc5 100644 --- a/xcm/Cargo.toml +++ b/xcm/Cargo.toml @@ -7,8 +7,9 @@ edition = "2021" [dependencies] impl-trait-for-tuples = "0.2.2" -parity-scale-codec = { version = "3.0.0", default-features = false, features = [ "derive" ] } +parity-scale-codec = { version = "3.0.0", default-features = false, features = [ "derive", "max-encoded-len" ] } scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } derivative = {version = "2.2.0", default-features = false, features = [ "use_core" ] } log = { version = "0.4.14", default-features = false } xcm-procedural = { path = "procedural" } @@ -20,4 +21,5 @@ runtime-benchmarks = [] std = [ "parity-scale-codec/std", "scale-info/std", + "sp-io/std" ] diff --git a/xcm/pallet-xcm-benchmarks/Cargo.toml b/xcm/pallet-xcm-benchmarks/Cargo.toml index bfb7d5641570..2007ce1efefb 100644 --- a/xcm/pallet-xcm-benchmarks/Cargo.toml +++ b/xcm/pallet-xcm-benchmarks/Cargo.toml @@ -24,7 +24,6 @@ log = "0.4.0" pallet-balances = { branch = "master", git = "https://github.com/paritytech/substrate" } pallet-assets = { branch = "master", git = "https://github.com/paritytech/substrate" } sp-core = { branch = "master", git = "https://github.com/paritytech/substrate" } -sp-io = { branch = "master", git = "https://github.com/paritytech/substrate" } sp-tracing = { branch = "master", git = "https://github.com/paritytech/substrate" } xcm-builder = { path = "../xcm-builder" } xcm = { path = ".." } @@ -41,6 +40,8 @@ std = [ "frame-benchmarking/std", "frame-support/std", "frame-system/std", + "sp-io/std", "sp-runtime/std", - "sp-std/std" + "sp-std/std", + "xcm-executor/std" ] diff --git a/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs b/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs index 916da48c4a96..110f4993c369 100644 --- a/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs +++ b/xcm/pallet-xcm-benchmarks/src/fungible/benchmarking.rs @@ -44,7 +44,15 @@ benchmarks_instance_pallet! { let worst_case_holding = T::worst_case_holding(0); let asset = T::get_multi_asset(); - >::deposit_asset(&asset, &sender_location).unwrap(); + >::deposit_asset( + &asset, + &sender_location, + &XcmContext { + origin: Some(sender_location.clone()), + message_hash: [0; 32], + topic: None, + }, + ).unwrap(); // check the assets of origin. assert!(!T::TransactAsset::balance(&sender_account).is_zero()); @@ -53,7 +61,7 @@ benchmarks_instance_pallet! { let instruction = Instruction::>::WithdrawAsset(vec![asset.clone()].into()); let xcm = Xcm(vec![instruction]); }: { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { // check one of the assets of origin. assert!(T::TransactAsset::balance(&sender_account).is_zero()); @@ -69,14 +77,22 @@ benchmarks_instance_pallet! { let dest_location = T::valid_destination()?; let dest_account = T::AccountIdConverter::convert(dest_location.clone()).unwrap(); - >::deposit_asset(&asset, &sender_location).unwrap(); + >::deposit_asset( + &asset, + &sender_location, + &XcmContext { + origin: Some(sender_location.clone()), + message_hash: [0; 32], + topic: None, + }, + ).unwrap(); assert!(T::TransactAsset::balance(&dest_account).is_zero()); let mut executor = new_executor::(sender_location); let instruction = Instruction::TransferAsset { assets, beneficiary: dest_location }; let xcm = Xcm(vec![instruction]); }: { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { assert!(T::TransactAsset::balance(&sender_account).is_zero()); assert!(!T::TransactAsset::balance(&dest_account).is_zero()); @@ -88,7 +104,15 @@ benchmarks_instance_pallet! { let dest_account = T::AccountIdConverter::convert(dest_location.clone()).unwrap(); let asset = T::get_multi_asset(); - >::deposit_asset(&asset, &sender_location).unwrap(); + >::deposit_asset( + &asset, + &sender_location, + &XcmContext { + origin: Some(sender_location.clone()), + message_hash: [0; 32], + topic: None, + }, + ).unwrap(); let assets: MultiAssets = vec![ asset ].into(); assert!(T::TransactAsset::balance(&dest_account).is_zero()); @@ -100,7 +124,7 @@ benchmarks_instance_pallet! { }; let xcm = Xcm(vec![instruction]); }: { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { assert!(T::TransactAsset::balance(&sender_account).is_zero()); assert!(!T::TransactAsset::balance(&dest_account).is_zero()); @@ -129,7 +153,7 @@ benchmarks_instance_pallet! { let instruction = Instruction::ReceiveTeleportedAsset(assets.clone()); let xcm = Xcm(vec![instruction]); }: { - executor.execute(xcm).map_err(|_| { + executor.bench_process(xcm).map_err(|_| { BenchmarkError::Override( BenchmarkResult::from_weight(T::BlockWeights::get().max_block) ) @@ -158,7 +182,7 @@ benchmarks_instance_pallet! { }; let xcm = Xcm(vec![instruction]); }: { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { // dest should have received some asset. assert!(!T::TransactAsset::balance(&dest_account).is_zero()) @@ -185,7 +209,7 @@ benchmarks_instance_pallet! { }; let xcm = Xcm(vec![instruction]); }: { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { // dest should have received some asset. assert!(!T::TransactAsset::balance(&dest_account).is_zero()) @@ -210,7 +234,7 @@ benchmarks_instance_pallet! { }; let xcm = Xcm(vec![instruction]); }: { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { if let Some(checked_account) = T::CheckedAccount::get() { // teleport checked account should have received some asset. diff --git a/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs b/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs index 2d3b4fe6ff91..88743c0347ef 100644 --- a/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs +++ b/xcm/pallet-xcm-benchmarks/src/generic/benchmarking.rs @@ -47,7 +47,7 @@ benchmarks! { let xcm = Xcm(vec![instruction]); } : { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { // The completion of execution above is enough to validate this is completed. } @@ -69,7 +69,7 @@ benchmarks! { let xcm = Xcm(vec![instruction]); } : { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { } @@ -89,7 +89,7 @@ benchmarks! { let instruction = Instruction::ReserveAssetDeposited(multiassets.clone()); let xcm = Xcm(vec![instruction]); }: { - executor.execute(xcm).map_err(|_| BenchmarkError::Skip)?; + executor.bench_process(xcm).map_err(|_| BenchmarkError::Skip)?; } verify { assert_eq!(executor.holding(), &multiassets.into()); } @@ -102,7 +102,7 @@ benchmarks! { let instruction = Instruction::QueryResponse { query_id, response, max_weight, querier }; let xcm = Xcm(vec![instruction]); }: { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { // The assert above is enough to show this XCM succeeded } @@ -127,7 +127,7 @@ benchmarks! { let num_events = frame_system::Pallet::::events().len(); }: { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { // TODO make better assertion? #4426 let num_events2 = frame_system::Pallet::::events().len(); @@ -144,7 +144,7 @@ benchmarks! { let instruction = Instruction::>::RefundSurplus; let xcm = Xcm(vec![instruction]); } : { - let result = executor.execute(xcm)?; + let result = executor.bench_process(xcm)?; } verify { assert_eq!(executor.total_surplus(), &1337); assert_eq!(executor.total_refunded(), &1337); @@ -155,7 +155,7 @@ benchmarks! { let instruction = Instruction::>::SetErrorHandler(Xcm(vec![])); let xcm = Xcm(vec![instruction]); } : { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { assert_eq!(executor.error_handler(), &Xcm(vec![])); } @@ -166,7 +166,7 @@ benchmarks! { let instruction = Instruction::>::SetAppendix(appendix); let xcm = Xcm(vec![instruction]); } : { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { assert_eq!(executor.appendix(), &Xcm(vec![])); } @@ -177,7 +177,7 @@ benchmarks! { let instruction = Instruction::>::ClearError; let xcm = Xcm(vec![instruction]); } : { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { assert!(executor.error().is_none()) } @@ -188,7 +188,7 @@ benchmarks! { let instruction = Instruction::DescendOrigin(who.clone()); let xcm = Xcm(vec![instruction]); } : { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { assert_eq!( executor.origin(), @@ -204,7 +204,7 @@ benchmarks! { let instruction = Instruction::ClearOrigin; let xcm = Xcm(vec![instruction]); } : { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { assert_eq!(executor.origin(), &None); } @@ -221,7 +221,7 @@ benchmarks! { }); let xcm = Xcm(vec![instruction]); }: { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { // the execution succeeding is all we need to verify this xcm was successful } @@ -235,6 +235,11 @@ benchmarks! { ::AssetTrap::drop_assets( &origin, assets.clone().into(), + &XcmContext { + origin: Some(origin.clone()), + message_hash: [0; 32], + topic: None, + }, ); // Assets should be in the trap now. @@ -243,7 +248,7 @@ benchmarks! { let instruction = Instruction::ClaimAsset { assets: assets.clone(), ticket }; let xcm = Xcm(vec![instruction]); } :{ - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { assert!(executor.holding().ensure_contains(&assets).is_ok()); } @@ -255,7 +260,7 @@ benchmarks! { // In order to access result in the verification below, it needs to be defined here. let mut _result = Ok(()); } : { - _result = executor.execute(xcm); + _result = executor.bench_process(xcm); } verify { assert!(matches!(_result, Err(ExecutorError { xcm_error: XcmError::Trap(10), @@ -272,7 +277,7 @@ benchmarks! { let instruction = Instruction::SubscribeVersion { query_id, max_response_weight }; let xcm = Xcm(vec![instruction]); } : { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { assert!(::SubscriptionService::is_subscribed(&origin)); } @@ -286,7 +291,12 @@ benchmarks! { ::SubscriptionService::start( &origin, query_id, - max_response_weight + max_response_weight, + &XcmContext { + origin: Some(origin.clone()), + message_hash: [0; 32], + topic: None, + }, ).map_err(|_| "Could not start subscription")?; assert!(::SubscriptionService::is_subscribed(&origin)); @@ -294,7 +304,7 @@ benchmarks! { let instruction = Instruction::UnsubscribeVersion; let xcm = Xcm(vec![instruction]); } : { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { assert!(!::SubscriptionService::is_subscribed(&origin)); } @@ -308,7 +318,7 @@ benchmarks! { let instruction = Instruction::InitiateReserveWithdraw { assets: assets_filter, reserve, xcm: Xcm(vec![]) }; let xcm = Xcm(vec![instruction]); }: { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { // The execute completing successfully is as good as we can check. // TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426 @@ -324,7 +334,7 @@ benchmarks! { let instruction = Instruction::BurnAsset(assets.into()); let xcm = Xcm(vec![instruction]); }: { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { assert!(executor.holding().is_empty()); } @@ -339,7 +349,7 @@ benchmarks! { let instruction = Instruction::ExpectAsset(assets.into()); let xcm = Xcm(vec![instruction]); }: { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { // `execute` completing successfully is as good as we can check. } @@ -352,7 +362,7 @@ benchmarks! { let xcm = Xcm(vec![instruction]); let mut _result = Ok(()); }: { - _result = executor.execute(xcm); + _result = executor.bench_process(xcm); } verify { assert!(matches!(_result, Err(ExecutorError { xcm_error: XcmError::ExpectationFalse, @@ -368,7 +378,7 @@ benchmarks! { let xcm = Xcm(vec![instruction]); let mut _result = Ok(()); }: { - _result = executor.execute(xcm); + _result = executor.bench_process(xcm); } verify { assert!(matches!(_result, Err(ExecutorError { xcm_error: XcmError::ExpectationFalse, @@ -388,7 +398,7 @@ benchmarks! { }; let xcm = Xcm(vec![instruction]); }: { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { // TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426 } @@ -405,7 +415,7 @@ benchmarks! { }; let xcm = Xcm(vec![instruction]); }: { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { // the execution succeeding is all we need to verify this xcm was successful } @@ -425,7 +435,7 @@ benchmarks! { }); let xcm = Xcm(vec![instruction]); }: { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { // TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426 } @@ -437,11 +447,34 @@ benchmarks! { let instruction = Instruction::ClearTransactStatus; let xcm = Xcm(vec![instruction]); }: { - executor.execute(xcm)?; + executor.bench_process(xcm)?; } verify { assert_eq!(executor.transact_status(), &MaybeErrorCode::Success); } + set_topic { + let mut executor = new_executor::(Default::default()); + + let instruction = Instruction::SetTopic([1; 32]); + let xcm = Xcm(vec![instruction]); + }: { + executor.bench_process(xcm)?; + } verify { + assert_eq!(executor.topic(), &Some([1; 32])); + } + + clear_topic { + let mut executor = new_executor::(Default::default()); + executor.set_topic(Some([2; 32])); + + let instruction = Instruction::ClearTopic; + let xcm = Xcm(vec![instruction]); + }: { + executor.bench_process(xcm)?; + } verify { + assert_eq!(executor.topic(), &None); + } + impl_benchmark_test_suite!( Pallet, crate::generic::mock::new_test_ext(), diff --git a/xcm/pallet-xcm-benchmarks/src/generic/mock.rs b/xcm/pallet-xcm-benchmarks/src/generic/mock.rs index 052714204989..52f6e4528c58 100644 --- a/xcm/pallet-xcm-benchmarks/src/generic/mock.rs +++ b/xcm/pallet-xcm-benchmarks/src/generic/mock.rs @@ -84,11 +84,15 @@ impl frame_system::Config for Test { /// The benchmarks in this pallet should never need an asset transactor to begin with. pub struct NoAssetTransactor; impl xcm_executor::traits::TransactAsset for NoAssetTransactor { - fn deposit_asset(_: &MultiAsset, _: &MultiLocation) -> Result<(), XcmError> { + fn deposit_asset(_: &MultiAsset, _: &MultiLocation, _: &XcmContext) -> Result<(), XcmError> { unreachable!(); } - fn withdraw_asset(_: &MultiAsset, _: &MultiLocation) -> Result { + fn withdraw_asset( + _: &MultiAsset, + _: &MultiLocation, + _: Option<&XcmContext>, + ) -> Result { unreachable!(); } } diff --git a/xcm/pallet-xcm-benchmarks/src/lib.rs b/xcm/pallet-xcm-benchmarks/src/lib.rs index 8db2496e0b7d..4759199c7d63 100644 --- a/xcm/pallet-xcm-benchmarks/src/lib.rs +++ b/xcm/pallet-xcm-benchmarks/src/lib.rs @@ -89,7 +89,7 @@ pub fn asset_instance_from(x: u32) -> AssetInstance { } pub fn new_executor(origin: MultiLocation) -> ExecutorOf { - ExecutorOf::::new(origin) + ExecutorOf::::new(origin, [0; 32]) } /// Build a multi-location from an account id. diff --git a/xcm/pallet-xcm-benchmarks/src/mock.rs b/xcm/pallet-xcm-benchmarks/src/mock.rs index e2940ff83c4e..84fc9fb9e7b4 100644 --- a/xcm/pallet-xcm-benchmarks/src/mock.rs +++ b/xcm/pallet-xcm-benchmarks/src/mock.rs @@ -24,8 +24,8 @@ impl xcm::opaque::latest::SendXcm for DevNull { fn validate(_: &mut Option, _: &mut Option>) -> SendResult<()> { Ok(((), MultiAssets::new())) } - fn deliver(_: ()) -> Result<(), SendError> { - Ok(()) + fn deliver(_: ()) -> Result { + Ok([0; 32]) } } @@ -39,6 +39,7 @@ impl xcm_executor::traits::OnResponse for DevNull { _: Option<&MultiLocation>, _: Response, _: Weight, + _: &XcmContext, ) -> Weight { 0 } diff --git a/xcm/pallet-xcm/Cargo.toml b/xcm/pallet-xcm/Cargo.toml index df3d5c8ccadc..23e5febf00cc 100644 --- a/xcm/pallet-xcm/Cargo.toml +++ b/xcm/pallet-xcm/Cargo.toml @@ -11,12 +11,12 @@ serde = { version = "1.0.136", optional = true, features = ["derive"] } log = { version = "0.4.14", default-features = false } impl-trait-for-tuples = "0.2.2" -sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } -sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } xcm = { path = "..", default-features = false } xcm-executor = { path = "../xcm-executor", default-features = false } @@ -24,8 +24,8 @@ xcm-executor = { path = "../xcm-executor", default-features = false } [dev-dependencies] pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } polkadot-runtime-parachains = { path = "../../runtime/parachains" } -xcm-builder = { path = "../xcm-builder" } polkadot-parachain = { path = "../../parachain" } +xcm-builder = { path = "../xcm-builder" } [features] default = ["std"] @@ -34,8 +34,8 @@ std = [ "scale-info/std", "serde", "sp-std/std", - "sp-io/std", "sp-core/std", + "sp-io/std", "sp-runtime/std", "frame-support/std", "frame-system/std", diff --git a/xcm/pallet-xcm/src/lib.rs b/xcm/pallet-xcm/src/lib.rs index c31bcc7c7c07..5a388982c788 100644 --- a/xcm/pallet-xcm/src/lib.rs +++ b/xcm/pallet-xcm/src/lib.rs @@ -768,6 +768,7 @@ pub mod pallet { max_weight: Weight, ) -> DispatchResultWithPostInfo { let origin_location = T::ExecuteXcmOrigin::ensure_origin(origin)?; + let hash = message.using_encoded(sp_io::hashing::blake2_256); let message = (*message).try_into().map_err(|()| Error::::BadVersion)?; let value = (origin_location, message); ensure!(T::XcmExecuteFilter::contains(&value), Error::::Filtered); @@ -775,6 +776,7 @@ pub mod pallet { let outcome = T::XcmExecutor::execute_xcm_in_credit( origin_location, message, + hash, max_weight, max_weight, ); @@ -1014,8 +1016,9 @@ impl Pallet { let mut message = Xcm(vec![TransferReserveAsset { assets, dest, xcm }]); let weight = T::Weigher::weight(&mut message).map_err(|()| Error::::UnweighableMessage)?; + let hash = message.using_encoded(sp_io::hashing::blake2_256); let outcome = - T::XcmExecutor::execute_xcm_in_credit(origin_location, message, weight, weight); + T::XcmExecutor::execute_xcm_in_credit(origin_location, message, hash, weight, weight); Self::deposit_event(Event::Attempted(outcome)); Ok(()) } @@ -1072,8 +1075,9 @@ impl Pallet { Xcm(vec![WithdrawAsset(assets), InitiateTeleport { assets: Wild(All), dest, xcm }]); let weight = T::Weigher::weight(&mut message).map_err(|()| Error::::UnweighableMessage)?; + let hash = message.using_encoded(sp_io::hashing::blake2_256); let outcome = - T::XcmExecutor::execute_xcm_in_credit(origin_location, message, weight, weight); + T::XcmExecutor::execute_xcm_in_credit(origin_location, message, hash, weight, weight); Self::deposit_event(Event::Attempted(outcome)); Ok(()) } @@ -1152,7 +1156,7 @@ impl Pallet { let message = Xcm(vec![QueryResponse { query_id, response, max_weight, querier: None }]); let event = match send_xcm::(new_key.clone(), message) { - Ok(cost) => { + Ok((_hash, cost)) => { let value = (query_id, max_weight, xcm_version); VersionNotifyTargets::::insert(XCM_VERSION, key, value); Event::VersionChangeNotified(new_key, xcm_version, cost) @@ -1201,7 +1205,7 @@ impl Pallet { querier: None, }]); let event = match send_xcm::(new_key.clone(), message) { - Ok(cost) => { + Ok((_hash, cost)) => { VersionNotifyTargets::::insert( XCM_VERSION, versioned_key, @@ -1236,7 +1240,7 @@ impl Pallet { }); // TODO #3735: Correct weight. let instruction = SubscribeVersion { query_id, max_response_weight: 0 }; - let cost = send_xcm::(dest.clone(), Xcm(vec![instruction]))?; + let (_hash, cost) = send_xcm::(dest.clone(), Xcm(vec![instruction]))?; Self::deposit_event(Event::VersionNotifyRequested(dest, cost)); VersionNotifiers::::insert(XCM_VERSION, &versioned_dest, query_id); let query_status = @@ -1251,7 +1255,7 @@ impl Pallet { let versioned_dest = LatestVersionedMultiLocation(&dest); let query_id = VersionNotifiers::::take(XCM_VERSION, versioned_dest) .ok_or(XcmError::InvalidLocation)?; - let cost = send_xcm::(dest.clone(), Xcm(vec![UnsubscribeVersion]))?; + let (_hash, cost) = send_xcm::(dest.clone(), Xcm(vec![UnsubscribeVersion]))?; Self::deposit_event(Event::VersionNotifyUnrequested(dest, cost)); Queries::::remove(query_id); Ok(()) @@ -1264,7 +1268,7 @@ impl Pallet { interior: impl Into, dest: impl Into, mut message: Xcm<()>, - ) -> Result<(), SendError> { + ) -> Result { let interior = interior.into(); let dest = dest.into(); let maybe_fee_payer = if interior != Junctions::Here { @@ -1278,8 +1282,7 @@ impl Pallet { if let Some(fee_payer) = maybe_fee_payer { Self::charge_fees(fee_payer, price).map_err(|_| SendError::Fees)?; } - T::XcmRouter::deliver(ticket)?; - Ok(()) + T::XcmRouter::deliver(ticket) } pub fn check_account() -> T::AccountId { @@ -1663,7 +1666,12 @@ impl VersionChangeNotifier for Pallet { /// /// If the `location` has an ongoing notification and when this function is called, then an /// error should be returned. - fn start(dest: &MultiLocation, query_id: QueryId, max_weight: u64) -> XcmResult { + fn start( + dest: &MultiLocation, + query_id: QueryId, + max_weight: u64, + _context: &XcmContext, + ) -> XcmResult { let versioned_dest = LatestVersionedMultiLocation(dest); let already = VersionNotifyTargets::::contains_key(XCM_VERSION, versioned_dest); ensure!(!already, XcmError::InvalidLocation); @@ -1671,7 +1679,7 @@ impl VersionChangeNotifier for Pallet { let xcm_version = T::AdvertisedXcmVersion::get(); let response = Response::Version(xcm_version); let instruction = QueryResponse { query_id, response, max_weight, querier: None }; - let cost = send_xcm::(dest.clone(), Xcm(vec![instruction]))?; + let (_hash, cost) = send_xcm::(dest.clone(), Xcm(vec![instruction]))?; Self::deposit_event(Event::::VersionNotifyStarted(dest.clone(), cost)); let value = (query_id, max_weight, xcm_version); @@ -1681,7 +1689,7 @@ impl VersionChangeNotifier for Pallet { /// Stop notifying `location` should the XCM change. This is a no-op if there was never a /// subscription. - fn stop(dest: &MultiLocation) -> XcmResult { + fn stop(dest: &MultiLocation, _context: &XcmContext) -> XcmResult { VersionNotifyTargets::::remove(XCM_VERSION, LatestVersionedMultiLocation(dest)); Ok(()) } @@ -1694,7 +1702,7 @@ impl VersionChangeNotifier for Pallet { } impl DropAssets for Pallet { - fn drop_assets(origin: &MultiLocation, assets: Assets) -> Weight { + fn drop_assets(origin: &MultiLocation, assets: Assets, _context: &XcmContext) -> Weight { if assets.is_empty() { return 0 } @@ -1708,7 +1716,12 @@ impl DropAssets for Pallet { } impl ClaimAssets for Pallet { - fn claim_assets(origin: &MultiLocation, ticket: &MultiLocation, assets: &MultiAssets) -> bool { + fn claim_assets( + origin: &MultiLocation, + ticket: &MultiLocation, + assets: &MultiAssets, + _context: &XcmContext, + ) -> bool { let mut versioned = VersionedMultiAssets::from(assets.clone()); match (ticket.parents, &ticket.interior) { (0, X1(GeneralIndex(i))) => @@ -1755,6 +1768,7 @@ impl OnResponse for Pallet { querier: Option<&MultiLocation>, response: Response, max_weight: Weight, + _context: &XcmContext, ) -> Weight { match (response, Queries::::get(query_id)) { ( diff --git a/xcm/pallet-xcm/src/mock.rs b/xcm/pallet-xcm/src/mock.rs index 1c394fe963d3..0e86483d6792 100644 --- a/xcm/pallet-xcm/src/mock.rs +++ b/xcm/pallet-xcm/src/mock.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . +use codec::Encode; use frame_support::{ construct_runtime, parameter_types, traits::{Everything, Nothing}, @@ -24,7 +25,7 @@ use polkadot_runtime_parachains::origin; use sp_core::H256; use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; pub use sp_std::{cell::RefCell, fmt::Debug, marker::PhantomData}; -use xcm::latest::prelude::*; +use xcm::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, Case, ChildParachainAsNative, ChildParachainConvertsVia, @@ -164,9 +165,10 @@ impl SendXcm for TestSendXcm { let pair = (dest.take().unwrap(), msg.take().unwrap()); Ok((pair, MultiAssets::new())) } - fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result<(), SendError> { + fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result { + let hash = fake_message_hash(&pair.1); SENT_XCM.with(|q| q.borrow_mut().push(pair)); - Ok(()) + Ok(hash) } } /// Sender that returns error if `X8` junction and stops routing @@ -184,9 +186,10 @@ impl SendXcm for TestSendXcmErrX8 { Ok(((dest, msg), MultiAssets::new())) } } - fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result<(), SendError> { + fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result { + let hash = fake_message_hash(&pair.1); SENT_XCM.with(|q| q.borrow_mut().push(pair)); - Ok(()) + Ok(hash) } } @@ -372,3 +375,7 @@ pub(crate) fn new_test_ext_with_balances( 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) +} diff --git a/xcm/pallet-xcm/src/tests.rs b/xcm/pallet-xcm/src/tests.rs index 5739f57f0a97..69b804fb9804 100644 --- a/xcm/pallet-xcm/src/tests.rs +++ b/xcm/pallet-xcm/src/tests.rs @@ -77,16 +77,15 @@ fn report_outcome_notify_works() { }; assert_eq!(crate::Queries::::iter().collect::>(), vec![(0, status)]); - let r = XcmExecutor::::execute_xcm( - Parachain(PARA_ID), - Xcm(vec![QueryResponse { - query_id: 0, - response: Response::ExecutionResult(None), - max_weight: 1_000_000, - querier: Some(querier), - }]), - 1_000_000_000, - ); + let message = Xcm(vec![QueryResponse { + query_id: 0, + response: Response::ExecutionResult(None), + max_weight: 1_000_000, + querier: Some(querier), + }]); + let hash = fake_message_hash(&message); + let r = + XcmExecutor::::execute_xcm(Parachain(PARA_ID), message, hash, 1_000_000_000); assert_eq!(r, Outcome::Complete(1_000)); assert_eq!( last_events(2), @@ -134,16 +133,15 @@ fn report_outcome_works() { }; assert_eq!(crate::Queries::::iter().collect::>(), vec![(0, status)]); - let r = XcmExecutor::::execute_xcm( - Parachain(PARA_ID), - Xcm(vec![QueryResponse { - query_id: 0, - response: Response::ExecutionResult(None), - max_weight: 0, - querier: Some(querier), - }]), - 1_000_000_000, - ); + let message = Xcm(vec![QueryResponse { + query_id: 0, + response: Response::ExecutionResult(None), + max_weight: 0, + querier: Some(querier), + }]); + let hash = fake_message_hash(&message); + let r = + XcmExecutor::::execute_xcm(Parachain(PARA_ID), message, hash, 1_000_000_000); assert_eq!(r, Outcome::Complete(1_000)); assert_eq!( last_event(), @@ -174,14 +172,17 @@ fn custom_querier_works() { assert_eq!(crate::Queries::::iter().collect::>(), vec![(0, status)]); // Supplying no querier when one is expected will fail + let message = Xcm(vec![QueryResponse { + query_id: 0, + response: Response::ExecutionResult(None), + max_weight: 0, + querier: None, + }]); + let hash = fake_message_hash(&message); let r = XcmExecutor::::execute_xcm_in_credit( AccountId32 { network: None, id: ALICE.into() }, - Xcm(vec![QueryResponse { - query_id: 0, - response: Response::ExecutionResult(None), - max_weight: 0, - querier: None, - }]), + message, + hash, 1_000_000_000, 1_000, ); @@ -197,14 +198,17 @@ fn custom_querier_works() { ); // Supplying the wrong querier will also fail + let message = Xcm(vec![QueryResponse { + query_id: 0, + response: Response::ExecutionResult(None), + max_weight: 0, + querier: Some(MultiLocation::here()), + }]); + let hash = fake_message_hash(&message); let r = XcmExecutor::::execute_xcm_in_credit( AccountId32 { network: None, id: ALICE.into() }, - Xcm(vec![QueryResponse { - query_id: 0, - response: Response::ExecutionResult(None), - max_weight: 0, - querier: Some(MultiLocation::here()), - }]), + message, + hash, 1_000_000_000, 1_000, ); @@ -220,14 +224,17 @@ fn custom_querier_works() { ); // Multiple failures should not have changed the query state + let message = Xcm(vec![QueryResponse { + query_id: 0, + response: Response::ExecutionResult(None), + max_weight: 0, + querier: Some(querier), + }]); + let hash = fake_message_hash(&message); let r = XcmExecutor::::execute_xcm( AccountId32 { network: None, id: ALICE.into() }, - Xcm(vec![QueryResponse { - query_id: 0, - response: Response::ExecutionResult(None), - max_weight: 0, - querier: Some(querier), - }]), + message, + hash, 1_000_000_000, ); assert_eq!(r, Outcome::Complete(1_000)); @@ -815,7 +822,8 @@ fn subscription_side_works() { let remote: MultiLocation = Parachain(1000).into(); let weight = BaseXcmWeight::get(); let message = Xcm(vec![SubscribeVersion { query_id: 0, max_response_weight: 0 }]); - let r = XcmExecutor::::execute_xcm(remote.clone(), message, weight); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(remote.clone(), message, hash, weight); assert_eq!(r, Outcome::Complete(weight)); let instr = QueryResponse { @@ -949,7 +957,8 @@ fn subscriber_side_subscription_works() { querier: None, }, ]); - let r = XcmExecutor::::execute_xcm(remote.clone(), message, weight); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(remote.clone(), message, hash, weight); assert_eq!(r, Outcome::Complete(weight)); assert_eq!(take_sent_xcm(), vec![]); @@ -966,7 +975,8 @@ fn subscriber_side_subscription_works() { querier: None, }, ]); - let r = XcmExecutor::::execute_xcm(remote.clone(), message, weight); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(remote.clone(), message, hash, weight); assert_eq!(r, Outcome::Complete(weight)); // This message can now be sent to remote as it's v2. @@ -1026,7 +1036,8 @@ fn auto_subscription_works() { querier: None, }, ]); - let r = XcmExecutor::::execute_xcm(remote0.clone(), message, weight); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(remote0.clone(), message, hash, weight); assert_eq!(r, Outcome::Complete(weight)); // This message can now be sent to remote0 as it's v2. @@ -1056,7 +1067,8 @@ fn auto_subscription_works() { querier: None, }, ]); - let r = XcmExecutor::::execute_xcm(remote1.clone(), message, weight); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(remote1.clone(), message, hash, weight); assert_eq!(r, Outcome::Complete(weight)); // v2 messages cannot be sent to remote1... diff --git a/xcm/src/v3/mod.rs b/xcm/src/v3/mod.rs index 1e92ea4d79a1..f8dc413a858f 100644 --- a/xcm/src/v3/mod.rs +++ b/xcm/src/v3/mod.rs @@ -45,7 +45,7 @@ pub use multilocation::{ }; pub use traits::{ send_xcm, validate_send, Error, ExecuteXcm, Outcome, PreparedMessage, Result, SendError, - SendResult, SendXcm, Unwrappable, Weight, + SendResult, SendXcm, Unwrappable, Weight, XcmHash, }; // These parts of XCM v2 are unchanged in XCM v3, and are re-imported here. pub use super::v2::{OriginKind, WeightLimit}; @@ -184,7 +184,7 @@ pub mod prelude { WeightLimit::{self, *}, WildFungibility::{self, Fungible as WildFungible, NonFungible as WildNonFungible}, WildMultiAsset::{self, *}, - XcmWeightInfo, VERSION as XCM_VERSION, + XcmContext, XcmHash, XcmWeightInfo, VERSION as XCM_VERSION, }; } pub use super::{Instruction, Xcm}; @@ -329,6 +329,25 @@ pub struct QueryResponseInfo { pub max_weight: Weight, } +/// Contextual data pertaining to a specific list of XCM instructions. +#[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)] +pub struct XcmContext { + /// The `MultiLocation` origin of the corresponding XCM. + pub origin: Option, + /// The hash of the XCM. + pub message_hash: XcmHash, + /// The topic of the XCM. + pub topic: Option<[u8; 32]>, +} + +impl XcmContext { + /// Constructor which sets the message hash to the supplied parameter and leaves the origin and + /// topic unset. + pub fn with_message_hash(message_hash: XcmHash) -> XcmContext { + XcmContext { origin: None, message_hash, topic: None } + } +} + /// Cross-Consensus Message: A message from one consensus system to another. /// /// Consensus systems that may send and receive messages include blockchains and smart contracts. @@ -944,6 +963,22 @@ pub enum Instruction { /// /// Errors: SetFeesMode { jit_withdraw: bool }, + + /// Set the Topic Register. + /// + /// Safety: No concerns. + /// + /// Kind: *Instruction* + /// + /// Errors: + SetTopic([u8; 32]), + + /// Clear the Topic Register. + /// + /// Kind: *Instruction* + /// + /// Errors: None. + ClearTopic, } impl Xcm { @@ -1015,6 +1050,8 @@ impl Instruction { NoteUnlockable { asset, owner } => NoteUnlockable { asset, owner }, RequestUnlock { asset, locker } => RequestUnlock { asset, locker }, SetFeesMode { jit_withdraw } => SetFeesMode { jit_withdraw }, + SetTopic(topic) => SetTopic(topic), + ClearTopic => ClearTopic, } } } @@ -1078,6 +1115,8 @@ impl> GetWeight for Instruction { NoteUnlockable { asset, owner } => W::note_unlockable(asset, owner), RequestUnlock { asset, locker } => W::request_unlock(asset, locker), SetFeesMode { jit_withdraw } => W::set_fees_mode(jit_withdraw), + SetTopic(topic) => W::set_topic(topic), + ClearTopic => W::clear_topic(), } } } diff --git a/xcm/src/v3/traits.rs b/xcm/src/v3/traits.rs index 357ebfc391ce..b5b3f6adaff2 100644 --- a/xcm/src/v3/traits.rs +++ b/xcm/src/v3/traits.rs @@ -246,15 +246,17 @@ pub trait ExecuteXcm { fn execute( origin: impl Into, pre: Self::Prepared, + hash: XcmHash, weight_credit: Weight, ) -> Outcome; - /// Execute some XCM `message` from `origin` using no more than `weight_limit` weight. The weight limit is - /// a basic hard-limit and the implementation may place further restrictions or requirements on weight and - /// other aspects. + /// Execute some XCM `message` with the message `hash` from `origin` using no more than `weight_limit` weight. + /// The weight limit is a basic hard-limit and the implementation may place further restrictions or requirements + /// on weight and other aspects. fn execute_xcm( origin: impl Into, message: Xcm, + hash: XcmHash, weight_limit: Weight, ) -> Outcome { let origin = origin.into(); @@ -265,16 +267,17 @@ pub trait ExecuteXcm { message, weight_limit, ); - Self::execute_xcm_in_credit(origin, message, weight_limit, 0) + Self::execute_xcm_in_credit(origin, message, hash, weight_limit, 0) } - /// Execute some XCM `message` from `origin` using no more than `weight_limit` weight. + /// Execute some XCM `message` with the message `hash` from `origin` using no more than `weight_limit` weight. /// /// Some amount of `weight_credit` may be provided which, depending on the implementation, may allow /// execution without associated payment. fn execute_xcm_in_credit( origin: impl Into, message: Xcm, + hash: XcmHash, weight_limit: Weight, weight_credit: Weight, ) -> Outcome { @@ -286,7 +289,7 @@ pub trait ExecuteXcm { if xcm_weight > weight_limit { return Outcome::Error(Error::WeightLimitReached(xcm_weight)) } - Self::execute(origin, pre, weight_credit) + Self::execute(origin, pre, hash, weight_credit) } /// Deduct some `fees` to the sovereign account of the given `location` and place them as per @@ -306,7 +309,7 @@ impl ExecuteXcm for () { fn prepare(message: Xcm) -> result::Result> { Err(message) } - fn execute(_: impl Into, _: Self::Prepared, _: Weight) -> Outcome { + fn execute(_: impl Into, _: Self::Prepared, _: XcmHash, _: Weight) -> Outcome { unreachable!() } fn charge_fees(_location: impl Into, _fees: MultiAssets) -> Result { @@ -340,6 +343,9 @@ pub enum SendError { Fees, } +/// A hash type for identifying messages. +pub type XcmHash = [u8; 32]; + /// Result value when attempting to send an XCM message. pub type SendResult = result::Result<(T, MultiAssets), SendError>; @@ -372,8 +378,9 @@ impl Unwrappable for Option { /// /// # Example /// ```rust -/// # use xcm::v3::prelude::*; /// # use parity_scale_codec::Encode; +/// # use xcm::v3::prelude::*; +/// # use xcm::VersionedXcm; /// # use std::convert::Infallible; /// /// /// A sender that only passes the message through and does nothing. @@ -383,7 +390,7 @@ impl Unwrappable for Option { /// fn validate(_: &mut Option, _: &mut Option>) -> SendResult { /// Err(SendError::NotApplicable) /// } -/// fn deliver(_: Infallible) -> Result<(), SendError> { +/// fn deliver(_: Infallible) -> Result { /// unreachable!() /// } /// } @@ -398,8 +405,8 @@ impl Unwrappable for Option { /// _ => Err(SendError::Unroutable), /// } /// } -/// fn deliver(_: ()) -> Result<(), SendError> { -/// Ok(()) +/// fn deliver(_: ()) -> Result { +/// Ok([0; 32]) /// } /// } /// @@ -413,8 +420,8 @@ impl Unwrappable for Option { /// _ => Err(SendError::NotApplicable), /// } /// } -/// fn deliver(_: ()) -> Result<(), SendError> { -/// Ok(()) +/// fn deliver(_: ()) -> Result { +/// Ok([0; 32]) /// } /// } /// @@ -426,6 +433,7 @@ impl Unwrappable for Option { /// require_weight_at_most: 0, /// call: call.into(), /// }]); +/// let message_hash = message.using_encoded(sp_io::hashing::blake2_256); /// /// // Sender2 will block this. /// assert!(send_xcm::<(Sender1, Sender2, Sender3)>(Parent.into(), message.clone()).is_err()); @@ -453,7 +461,7 @@ pub trait SendXcm { ) -> SendResult; /// Actually carry out the delivery operation for a previously validated message sending. - fn deliver(ticket: Self::Ticket) -> result::Result<(), SendError>; + fn deliver(ticket: Self::Ticket) -> result::Result; } #[impl_trait_for_tuples::impl_for_tuples(30)] @@ -486,7 +494,7 @@ impl SendXcm for Tuple { } } - fn deliver(one_ticket: Self::Ticket) -> result::Result<(), SendError> { + fn deliver(one_ticket: Self::Ticket) -> result::Result { for_tuples!( #( if let Some(validated) = one_ticket.Tuple { return Tuple::deliver(validated); @@ -513,8 +521,8 @@ pub fn validate_send(dest: MultiLocation, msg: Xcm<()>) -> SendResul pub fn send_xcm( dest: MultiLocation, msg: Xcm<()>, -) -> result::Result { - let (ticket, price) = T::validate(&mut Some(dest), &mut Some(msg))?; - T::deliver(ticket)?; - Ok(price) +) -> result::Result<(XcmHash, MultiAssets), SendError> { + let (ticket, price) = T::validate(&mut Some(dest), &mut Some(msg.clone()))?; + let hash = T::deliver(ticket)?; + Ok((hash, price)) } diff --git a/xcm/xcm-builder/Cargo.toml b/xcm/xcm-builder/Cargo.toml index 93fce90aadbe..1eee8c45a5a8 100644 --- a/xcm/xcm-builder/Cargo.toml +++ b/xcm/xcm-builder/Cargo.toml @@ -44,6 +44,7 @@ std = [ "sp-io/std", "sp-runtime/std", "frame-support/std", + "frame-system/std", "polkadot-parachain/std", "pallet-transaction-payment/std", ] diff --git a/xcm/xcm-builder/src/currency_adapter.rs b/xcm/xcm-builder/src/currency_adapter.rs index fb616a961bdf..d85f371bd346 100644 --- a/xcm/xcm-builder/src/currency_adapter.rs +++ b/xcm/xcm-builder/src/currency_adapter.rs @@ -19,7 +19,7 @@ use frame_support::traits::{ExistenceRequirement::AllowDeath, Get, WithdrawReasons}; use sp_runtime::traits::{CheckedSub, SaturatedConversion}; use sp_std::{convert::TryInto, marker::PhantomData, result}; -use xcm::latest::{Error as XcmError, MultiAsset, MultiLocation, Result}; +use xcm::latest::{Error as XcmError, MultiAsset, MultiLocation, Result, XcmContext}; use xcm_executor::{ traits::{Convert, MatchesFungible, TransactAsset}, Assets, @@ -101,7 +101,7 @@ impl< > TransactAsset for CurrencyAdapter { - fn can_check_in(_origin: &MultiLocation, what: &MultiAsset) -> Result { + fn can_check_in(_origin: &MultiLocation, what: &MultiAsset, _context: &XcmContext) -> Result { log::trace!(target: "xcm::currency_adapter", "can_check_in origin: {:?}, what: {:?}", _origin, what); // Check we handle this asset. let amount: Currency::Balance = @@ -121,7 +121,7 @@ impl< Ok(()) } - fn check_in(_origin: &MultiLocation, what: &MultiAsset) { + fn check_in(_origin: &MultiLocation, what: &MultiAsset, _context: &XcmContext) { log::trace!(target: "xcm::currency_adapter", "check_in origin: {:?}, what: {:?}", _origin, what); if let Some(amount) = Matcher::matches_fungible(what) { if let Some(checked_account) = CheckedAccount::get() { @@ -140,7 +140,7 @@ impl< } } - fn check_out(_dest: &MultiLocation, what: &MultiAsset) { + fn check_out(_dest: &MultiLocation, what: &MultiAsset, _context: &XcmContext) { log::trace!(target: "xcm::currency_adapter", "check_out dest: {:?}, what: {:?}", _dest, what); if let Some(amount) = Matcher::matches_fungible(what) { if let Some(checked_account) = CheckedAccount::get() { @@ -149,7 +149,7 @@ impl< } } - fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> Result { + fn deposit_asset(what: &MultiAsset, who: &MultiLocation, _context: &XcmContext) -> Result { log::trace!(target: "xcm::currency_adapter", "deposit_asset what: {:?}, who: {:?}", what, who); // Check we handle this asset. let amount: u128 = @@ -162,7 +162,11 @@ impl< Ok(()) } - fn withdraw_asset(what: &MultiAsset, who: &MultiLocation) -> result::Result { + fn withdraw_asset( + what: &MultiAsset, + who: &MultiLocation, + _maybe_context: Option<&XcmContext>, + ) -> result::Result { log::trace!(target: "xcm::currency_adapter", "withdraw_asset what: {:?}, who: {:?}", what, who); // Check we handle this asset. let amount: u128 = diff --git a/xcm/xcm-builder/src/fungibles_adapter.rs b/xcm/xcm-builder/src/fungibles_adapter.rs index c5e17bbdc2c2..be8e9d27cfaa 100644 --- a/xcm/xcm-builder/src/fungibles_adapter.rs +++ b/xcm/xcm-builder/src/fungibles_adapter.rs @@ -35,6 +35,7 @@ impl< what: &MultiAsset, from: &MultiLocation, to: &MultiLocation, + _context: &XcmContext, ) -> result::Result { log::trace!( target: "xcm::fungibles_adapter", @@ -78,7 +79,11 @@ impl< CheckingAccount, > { - fn can_check_in(_origin: &MultiLocation, what: &MultiAsset) -> XcmResult { + fn can_check_in( + _origin: &MultiLocation, + what: &MultiAsset, + _context: &XcmContext, + ) -> XcmResult { log::trace!( target: "xcm::fungibles_adapter", "can_check_in origin: {:?}, what: {:?}", @@ -96,7 +101,7 @@ impl< Ok(()) } - fn check_in(_origin: &MultiLocation, what: &MultiAsset) { + fn check_in(_origin: &MultiLocation, what: &MultiAsset, _context: &XcmContext) { log::trace!( target: "xcm::fungibles_adapter", "check_in origin: {:?}, what: {:?}", @@ -114,7 +119,7 @@ impl< } } - fn check_out(_dest: &MultiLocation, what: &MultiAsset) { + fn check_out(_dest: &MultiLocation, what: &MultiAsset, _context: &XcmContext) { log::trace!( target: "xcm::fungibles_adapter", "check_out dest: {:?}, what: {:?}", @@ -129,7 +134,7 @@ impl< } } - fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> XcmResult { + fn deposit_asset(what: &MultiAsset, who: &MultiLocation, _context: &XcmContext) -> XcmResult { log::trace!( target: "xcm::fungibles_adapter", "deposit_asset what: {:?}, who: {:?}", @@ -146,6 +151,7 @@ impl< fn withdraw_asset( what: &MultiAsset, who: &MultiLocation, + _maybe_context: Option<&XcmContext>, ) -> result::Result { log::trace!( target: "xcm::fungibles_adapter", @@ -180,7 +186,7 @@ impl< > TransactAsset for FungiblesAdapter { - fn can_check_in(origin: &MultiLocation, what: &MultiAsset) -> XcmResult { + fn can_check_in(origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) -> XcmResult { FungiblesMutateAdapter::< Assets, Matcher, @@ -188,10 +194,10 @@ impl< AccountId, CheckAsset, CheckingAccount, - >::can_check_in(origin, what) + >::can_check_in(origin, what, context) } - fn check_in(origin: &MultiLocation, what: &MultiAsset) { + fn check_in(origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) { FungiblesMutateAdapter::< Assets, Matcher, @@ -199,10 +205,10 @@ impl< AccountId, CheckAsset, CheckingAccount, - >::check_in(origin, what) + >::check_in(origin, what, context) } - fn check_out(dest: &MultiLocation, what: &MultiAsset) { + fn check_out(dest: &MultiLocation, what: &MultiAsset, context: &XcmContext) { FungiblesMutateAdapter::< Assets, Matcher, @@ -210,10 +216,10 @@ impl< AccountId, CheckAsset, CheckingAccount, - >::check_out(dest, what) + >::check_out(dest, what, context) } - fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> XcmResult { + fn deposit_asset(what: &MultiAsset, who: &MultiLocation, context: &XcmContext) -> XcmResult { FungiblesMutateAdapter::< Assets, Matcher, @@ -221,12 +227,13 @@ impl< AccountId, CheckAsset, CheckingAccount, - >::deposit_asset(what, who) + >::deposit_asset(what, who, context) } fn withdraw_asset( what: &MultiAsset, who: &MultiLocation, + maybe_context: Option<&XcmContext>, ) -> result::Result { FungiblesMutateAdapter::< Assets, @@ -235,16 +242,17 @@ impl< AccountId, CheckAsset, CheckingAccount, - >::withdraw_asset(what, who) + >::withdraw_asset(what, who, maybe_context) } fn transfer_asset( what: &MultiAsset, from: &MultiLocation, to: &MultiLocation, + context: &XcmContext, ) -> result::Result { FungiblesTransferAdapter::::transfer_asset( - what, from, to, + what, from, to, context, ) } } diff --git a/xcm/xcm-builder/src/nonfungibles_adapter.rs b/xcm/xcm-builder/src/nonfungibles_adapter.rs index 14a749022be0..d717145cc4fc 100644 --- a/xcm/xcm-builder/src/nonfungibles_adapter.rs +++ b/xcm/xcm-builder/src/nonfungibles_adapter.rs @@ -38,11 +38,12 @@ impl< what: &MultiAsset, from: &MultiLocation, to: &MultiLocation, + context: &XcmContext, ) -> result::Result { log::trace!( target: "xcm::non_fungibles_adapter", - "transfer_asset what: {:?}, from: {:?}, to: {:?}", - what, from, to + "transfer_asset what: {:?}, from: {:?}, to: {:?}, context: {:?}", + what, from, to, context, ); // Check we handle this asset. let (class, instance) = Matcher::matches_nonfungibles(what)?; @@ -79,11 +80,11 @@ impl< CheckingAccount, > { - fn can_check_in(_origin: &MultiLocation, what: &MultiAsset) -> XcmResult { + fn can_check_in(_origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) -> XcmResult { log::trace!( target: "xcm::fungibles_adapter", - "can_check_in origin: {:?}, what: {:?}", - _origin, what + "can_check_in origin: {:?}, what: {:?}, context: {:?}", + _origin, what, context, ); // Check we handle this asset. let (class, instance) = Matcher::matches_nonfungibles(what)?; @@ -98,11 +99,11 @@ impl< Ok(()) } - fn check_in(_origin: &MultiLocation, what: &MultiAsset) { + fn check_in(_origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) { log::trace!( target: "xcm::fungibles_adapter", - "check_in origin: {:?}, what: {:?}", - _origin, what + "check_in origin: {:?}, what: {:?}, context: {:?}", + _origin, what, context, ); if let Ok((class, instance)) = Matcher::matches_nonfungibles(what) { if CheckAsset::contains(&class) { @@ -115,11 +116,11 @@ impl< } } - fn check_out(_dest: &MultiLocation, what: &MultiAsset) { + fn check_out(_dest: &MultiLocation, what: &MultiAsset, context: &XcmContext) { log::trace!( target: "xcm::fungibles_adapter", - "check_out dest: {:?}, what: {:?}", - _dest, what + "check_out dest: {:?}, what: {:?}, context: {:?}", + _dest, what, context, ); if let Ok((class, instance)) = Matcher::matches_nonfungibles(what) { if CheckAsset::contains(&class) { @@ -131,11 +132,11 @@ impl< } } - fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> XcmResult { + fn deposit_asset(what: &MultiAsset, who: &MultiLocation, context: &XcmContext) -> XcmResult { log::trace!( target: "xcm::fungibles_adapter", - "deposit_asset what: {:?}, who: {:?}", - what, who, + "deposit_asset what: {:?}, who: {:?}, context: {:?}", + what, who, context, ); // Check we handle this asset. let (class, instance) = Matcher::matches_nonfungibles(what)?; @@ -148,11 +149,12 @@ impl< fn withdraw_asset( what: &MultiAsset, who: &MultiLocation, + maybe_context: Option<&XcmContext>, ) -> result::Result { log::trace!( target: "xcm::fungibles_adapter", - "withdraw_asset what: {:?}, who: {:?}", - what, who, + "withdraw_asset what: {:?}, who: {:?}, maybe_context: {:?}", + what, who, maybe_context, ); // Check we handle this asset. let who = AccountIdConverter::convert_ref(who) @@ -182,7 +184,7 @@ impl< > TransactAsset for NonFungiblesAdapter { - fn can_check_in(origin: &MultiLocation, what: &MultiAsset) -> XcmResult { + fn can_check_in(origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) -> XcmResult { NonFungiblesMutateAdapter::< Assets, Matcher, @@ -190,10 +192,10 @@ impl< AccountId, CheckAsset, CheckingAccount, - >::can_check_in(origin, what) + >::can_check_in(origin, what, context) } - fn check_in(origin: &MultiLocation, what: &MultiAsset) { + fn check_in(origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) { NonFungiblesMutateAdapter::< Assets, Matcher, @@ -201,10 +203,10 @@ impl< AccountId, CheckAsset, CheckingAccount, - >::check_in(origin, what) + >::check_in(origin, what, context) } - fn check_out(dest: &MultiLocation, what: &MultiAsset) { + fn check_out(dest: &MultiLocation, what: &MultiAsset, context: &XcmContext) { NonFungiblesMutateAdapter::< Assets, Matcher, @@ -212,10 +214,10 @@ impl< AccountId, CheckAsset, CheckingAccount, - >::check_out(dest, what) + >::check_out(dest, what, context) } - fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> XcmResult { + fn deposit_asset(what: &MultiAsset, who: &MultiLocation, context: &XcmContext) -> XcmResult { NonFungiblesMutateAdapter::< Assets, Matcher, @@ -223,12 +225,13 @@ impl< AccountId, CheckAsset, CheckingAccount, - >::deposit_asset(what, who) + >::deposit_asset(what, who, context) } fn withdraw_asset( what: &MultiAsset, who: &MultiLocation, + maybe_context: Option<&XcmContext>, ) -> result::Result { NonFungiblesMutateAdapter::< Assets, @@ -237,16 +240,17 @@ impl< AccountId, CheckAsset, CheckingAccount, - >::withdraw_asset(what, who) + >::withdraw_asset(what, who, maybe_context) } fn transfer_asset( what: &MultiAsset, from: &MultiLocation, to: &MultiLocation, + context: &XcmContext, ) -> result::Result { NonFungiblesTransferAdapter::::transfer_asset( - what, from, to, + what, from, to, context, ) } } diff --git a/xcm/xcm-builder/src/test_utils.rs b/xcm/xcm-builder/src/test_utils.rs index ceb41aa409ac..f75edc47661c 100644 --- a/xcm/xcm-builder/src/test_utils.rs +++ b/xcm/xcm-builder/src/test_utils.rs @@ -37,13 +37,18 @@ parameter_types! { pub struct TestSubscriptionService; impl VersionChangeNotifier for TestSubscriptionService { - fn start(location: &MultiLocation, query_id: QueryId, max_weight: u64) -> XcmResult { + fn start( + location: &MultiLocation, + query_id: QueryId, + max_weight: u64, + _context: &XcmContext, + ) -> XcmResult { let mut r = SubscriptionRequests::get(); r.push((location.clone(), Some((query_id, max_weight)))); SubscriptionRequests::set(r); Ok(()) } - fn stop(location: &MultiLocation) -> XcmResult { + fn stop(location: &MultiLocation, _context: &XcmContext) -> XcmResult { let mut r = SubscriptionRequests::get(); r.retain(|(l, _q)| l != location); r.push((location.clone(), None)); @@ -63,7 +68,7 @@ parameter_types! { pub struct TestAssetTrap; impl DropAssets for TestAssetTrap { - fn drop_assets(origin: &MultiLocation, assets: Assets) -> Weight { + fn drop_assets(origin: &MultiLocation, assets: Assets, _context: &XcmContext) -> Weight { let mut t: Vec<(MultiLocation, MultiAssets)> = TrappedAssets::get(); t.push((origin.clone(), assets.into())); TrappedAssets::set(t); @@ -72,7 +77,12 @@ impl DropAssets for TestAssetTrap { } impl ClaimAssets for TestAssetTrap { - fn claim_assets(origin: &MultiLocation, ticket: &MultiLocation, what: &MultiAssets) -> bool { + fn claim_assets( + origin: &MultiLocation, + ticket: &MultiLocation, + what: &MultiAssets, + _context: &XcmContext, + ) -> bool { let mut t: Vec<(MultiLocation, MultiAssets)> = TrappedAssets::get(); if let (0, X1(GeneralIndex(i))) = (ticket.parents, &ticket.interior) { if let Some((l, a)) = t.get(*i as usize) { diff --git a/xcm/xcm-builder/src/tests/assets.rs b/xcm/xcm-builder/src/tests/assets.rs index afec771cb0a8..e78047988e04 100644 --- a/xcm/xcm-builder/src/tests/assets.rs +++ b/xcm/xcm-builder/src/tests/assets.rs @@ -21,22 +21,19 @@ fn exchange_asset_should_work() { AllowUnpaidFrom::set(vec![Parent.into()]); add_asset(Parent, (Parent, 1000u128)); set_exchange_assets(vec![(Here, 100u128).into()]); - let r = XcmExecutor::::execute_xcm( - Parent, - Xcm(vec![ - WithdrawAsset((Parent, 100u128).into()), - SetAppendix( - vec![DepositAsset { assets: AllCounted(2).into(), beneficiary: Parent.into() }] - .into(), - ), - ExchangeAsset { - give: Definite((Parent, 50u128).into()), - want: (Here, 50u128).into(), - maximal: true, - }, - ]), - 50, - ); + let message = Xcm(vec![ + WithdrawAsset((Parent, 100u128).into()), + SetAppendix( + vec![DepositAsset { assets: AllCounted(2).into(), beneficiary: Parent.into() }].into(), + ), + ExchangeAsset { + give: Definite((Parent, 50u128).into()), + want: (Here, 50u128).into(), + maximal: true, + }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parent, message, hash, 50); assert_eq!(r, Outcome::Complete(40)); assert_eq!(asset_list(Parent), vec![(Here, 100u128).into(), (Parent, 950u128).into()]); assert_eq!(exchange_assets(), vec![(Parent, 50u128).into()].into()); @@ -45,24 +42,21 @@ fn exchange_asset_should_work() { #[test] fn exchange_asset_without_maximal_should_work() { AllowUnpaidFrom::set(vec![Parent.into()]); - add_asset(Parent, (Parent, 1000)); + add_asset(Parent, (Parent, 1000u128)); set_exchange_assets(vec![(Here, 100u128).into()]); - let r = XcmExecutor::::execute_xcm( - Parent, - Xcm(vec![ - WithdrawAsset((Parent, 100u128).into()), - SetAppendix( - vec![DepositAsset { assets: AllCounted(2).into(), beneficiary: Parent.into() }] - .into(), - ), - ExchangeAsset { - give: Definite((Parent, 50u128).into()), - want: (Here, 50u128).into(), - maximal: false, - }, - ]), - 50, - ); + let message = Xcm(vec![ + WithdrawAsset((Parent, 100u128).into()), + SetAppendix( + vec![DepositAsset { assets: AllCounted(2).into(), beneficiary: Parent.into() }].into(), + ), + ExchangeAsset { + give: Definite((Parent, 50).into()), + want: (Here, 50u128).into(), + maximal: false, + }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parent, message, hash, 50); assert_eq!(r, Outcome::Complete(40)); assert_eq!(asset_list(Parent), vec![(Here, 50u128).into(), (Parent, 950u128).into()]); assert_eq!(exchange_assets(), vec![(Here, 50u128).into(), (Parent, 50u128).into()].into()); @@ -71,24 +65,21 @@ fn exchange_asset_without_maximal_should_work() { #[test] fn exchange_asset_should_fail_when_no_deal_possible() { AllowUnpaidFrom::set(vec![Parent.into()]); - add_asset(Parent, (Parent, 1000)); + add_asset(Parent, (Parent, 1000u128)); set_exchange_assets(vec![(Here, 100u128).into()]); - let r = XcmExecutor::::execute_xcm( - Parent, - Xcm(vec![ - WithdrawAsset((Parent, 150u128).into()), - SetAppendix( - vec![DepositAsset { assets: AllCounted(2).into(), beneficiary: Parent.into() }] - .into(), - ), - ExchangeAsset { - give: Definite((Parent, 150u128).into()), - want: (Here, 150u128).into(), - maximal: false, - }, - ]), - 50, - ); + let message = Xcm(vec![ + WithdrawAsset((Parent, 150u128).into()), + SetAppendix( + vec![DepositAsset { assets: AllCounted(2).into(), beneficiary: Parent.into() }].into(), + ), + ExchangeAsset { + give: Definite((Parent, 150u128).into()), + want: (Here, 150u128).into(), + maximal: false, + }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parent, message, hash, 50); assert_eq!(r, Outcome::Incomplete(40, XcmError::NoDeal)); assert_eq!(asset_list(Parent), vec![(Parent, 1000u128).into()]); assert_eq!(exchange_assets(), vec![(Here, 100u128).into()].into()); @@ -106,8 +97,9 @@ fn paying_reserve_deposit_should_work() { BuyExecution { fees, weight_limit: Limited(30) }, DepositAsset { assets: AllCounted(1).into(), beneficiary: Here.into() }, ]); + let hash = fake_message_hash(&message); let weight_limit = 50; - let r = XcmExecutor::::execute_xcm(Parent, message, weight_limit); + let r = XcmExecutor::::execute_xcm(Parent, message, hash, weight_limit); assert_eq!(r, Outcome::Complete(30)); assert_eq!(asset_list(Here), vec![(Parent, 70u128).into()]); } @@ -119,14 +111,12 @@ fn transfer_should_work() { // Child parachain #1 owns 1000 tokens held by us in reserve. add_asset(Parachain(1), (Here, 1000)); // They want to transfer 100 of them to their sibling parachain #2 - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![TransferAsset { - assets: (Here, 100u128).into(), - beneficiary: X1(AccountIndex64 { index: 3, network: None }).into(), - }]), - 50, - ); + let message = Xcm(vec![TransferAsset { + assets: (Here, 100u128).into(), + beneficiary: X1(AccountIndex64 { index: 3, network: None }).into(), + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Complete(10)); assert_eq!( asset_list(AccountIndex64 { index: 3, network: None }), @@ -146,32 +136,26 @@ fn reserve_transfer_should_work() { // They want to transfer 100 of our native asset from sovereign account of parachain #1 into #2 // and let them know to hand it to account #3. - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![TransferReserveAsset { - assets: (Here, 100u128).into(), - dest: Parachain(2).into(), - xcm: Xcm::<()>(vec![DepositAsset { - assets: AllCounted(1).into(), - beneficiary: three.clone(), - }]), + let message = Xcm(vec![TransferReserveAsset { + assets: (Here, 100u128).into(), + dest: Parachain(2).into(), + xcm: Xcm::<()>(vec![DepositAsset { + assets: AllCounted(1).into(), + beneficiary: three.clone(), }]), - 50, - ); + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Complete(10)); - assert_eq!(asset_list(Parachain(2)), vec![(Here, 100u128).into()]); - assert_eq!( - sent_xcm(), - vec![( - Parachain(2).into(), - Xcm::<()>(vec![ - ReserveAssetDeposited((Parent, 100u128).into()), - ClearOrigin, - DepositAsset { assets: AllCounted(1).into(), beneficiary: three }, - ]), - )] - ); + let expected_msg = Xcm::<()>(vec![ + ReserveAssetDeposited((Parent, 100u128).into()), + ClearOrigin, + DepositAsset { assets: AllCounted(1).into(), beneficiary: three }, + ]); + let expected_hash = fake_message_hash(&expected_msg); + assert_eq!(asset_list(Parachain(2)), vec![(Here, 100).into()]); + assert_eq!(sent_xcm(), vec![(Parachain(2).into(), expected_msg, expected_hash)]); } #[test] @@ -181,29 +165,25 @@ fn burn_should_work() { // Child parachain #1 owns 1000 tokens held by us in reserve. add_asset(Parachain(1), (Here, 1000)); // They want to burn 100 of them - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ - WithdrawAsset((Here, 1000u128).into()), - BurnAsset((Here, 100u128).into()), - DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Parachain(1).into() }, - ]), - 50, - ); + let message = Xcm(vec![ + WithdrawAsset((Here, 1000u128).into()), + BurnAsset((Here, 100u128).into()), + DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Parachain(1).into() }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Complete(30)); assert_eq!(asset_list(Parachain(1)), vec![(Here, 900u128).into()]); assert_eq!(sent_xcm(), vec![]); // Now they want to burn 1000 of them, which will actually only burn 900. - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ - WithdrawAsset((Here, 900u128).into()), - BurnAsset((Here, 1000u128).into()), - DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Parachain(1).into() }, - ]), - 50, - ); + let message = Xcm(vec![ + WithdrawAsset((Here, 900u128).into()), + BurnAsset((Here, 1000u128).into()), + DepositAsset { assets: Wild(AllCounted(1)), beneficiary: Parachain(1).into() }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Complete(30)); assert_eq!(asset_list(Parachain(1)), vec![]); assert_eq!(sent_xcm(), vec![]); @@ -217,86 +197,76 @@ fn basic_asset_trap_should_work() { // Child parachain #1 owns 1000 tokens held by us in reserve. add_asset(Parachain(1), (Here, 1000)); // They want to transfer 100 of them to their sibling parachain #2 but have a problem - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ - WithdrawAsset((Here, 100u128).into()), - DepositAsset { - assets: Wild(AllCounted(0)), // <<< 0 is an error. - beneficiary: AccountIndex64 { index: 3, network: None }.into(), - }, - ]), - 20, - ); + let message = Xcm(vec![ + WithdrawAsset((Here, 100u128).into()), + DepositAsset { + assets: Wild(AllCounted(0)), // <<< 0 is an error. + beneficiary: AccountIndex64 { index: 3, network: None }.into(), + }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 20); assert_eq!(r, Outcome::Complete(25)); assert_eq!(asset_list(Parachain(1)), vec![(Here, 900u128).into()]); assert_eq!(asset_list(AccountIndex64 { index: 3, network: None }), vec![]); // Incorrect ticket doesn't work. + let message = Xcm(vec![ + ClaimAsset { assets: (Here, 100u128).into(), ticket: GeneralIndex(1).into() }, + DepositAsset { + assets: Wild(AllCounted(1)), + beneficiary: AccountIndex64 { index: 3, network: None }.into(), + }, + ]); + let hash = fake_message_hash(&message); let old_trapped_assets = TrappedAssets::get(); - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ - ClaimAsset { assets: (Here, 100u128).into(), ticket: GeneralIndex(1).into() }, - DepositAsset { - assets: Wild(AllCounted(1)), - beneficiary: AccountIndex64 { index: 3, network: None }.into(), - }, - ]), - 20, - ); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 20); assert_eq!(r, Outcome::Incomplete(10, XcmError::UnknownClaim)); assert_eq!(asset_list(Parachain(1)), vec![(Here, 900u128).into()]); assert_eq!(asset_list(AccountIndex64 { index: 3, network: None }), vec![]); assert_eq!(old_trapped_assets, TrappedAssets::get()); // Incorrect origin doesn't work. + let message = Xcm(vec![ + ClaimAsset { assets: (Here, 100u128).into(), ticket: GeneralIndex(0).into() }, + DepositAsset { + assets: Wild(AllCounted(1)), + beneficiary: AccountIndex64 { index: 3, network: None }.into(), + }, + ]); + let hash = fake_message_hash(&message); let old_trapped_assets = TrappedAssets::get(); - let r = XcmExecutor::::execute_xcm( - Parachain(2), - Xcm(vec![ - ClaimAsset { assets: (Here, 100u128).into(), ticket: GeneralIndex(0u128).into() }, - DepositAsset { - assets: Wild(AllCounted(1)), - beneficiary: AccountIndex64 { index: 3, network: None }.into(), - }, - ]), - 20, - ); + let r = XcmExecutor::::execute_xcm(Parachain(2), message, hash, 20); assert_eq!(r, Outcome::Incomplete(10, XcmError::UnknownClaim)); assert_eq!(asset_list(Parachain(1)), vec![(Here, 900u128).into()]); assert_eq!(asset_list(AccountIndex64 { index: 3, network: None }), vec![]); assert_eq!(old_trapped_assets, TrappedAssets::get()); // Incorrect assets doesn't work. + let message = Xcm(vec![ + ClaimAsset { assets: (Here, 101u128).into(), ticket: GeneralIndex(0).into() }, + DepositAsset { + assets: Wild(AllCounted(1)), + beneficiary: AccountIndex64 { index: 3, network: None }.into(), + }, + ]); + let hash = fake_message_hash(&message); let old_trapped_assets = TrappedAssets::get(); - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ - ClaimAsset { assets: (Here, 101u128).into(), ticket: GeneralIndex(0u128).into() }, - DepositAsset { - assets: Wild(AllCounted(1)), - beneficiary: AccountIndex64 { index: 3, network: None }.into(), - }, - ]), - 20, - ); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 20); assert_eq!(r, Outcome::Incomplete(10, XcmError::UnknownClaim)); assert_eq!(asset_list(Parachain(1)), vec![(Here, 900u128).into()]); assert_eq!(asset_list(AccountIndex64 { index: 3, network: None }), vec![]); assert_eq!(old_trapped_assets, TrappedAssets::get()); - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ - ClaimAsset { assets: (Here, 100u128).into(), ticket: GeneralIndex(0u128).into() }, - DepositAsset { - assets: Wild(AllCounted(1)), - beneficiary: AccountIndex64 { index: 3, network: None }.into(), - }, - ]), - 20, - ); + let message = Xcm(vec![ + ClaimAsset { assets: (Here, 100u128).into(), ticket: GeneralIndex(0).into() }, + DepositAsset { + assets: Wild(AllCounted(1)), + beneficiary: AccountIndex64 { index: 3, network: None }.into(), + }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 20); assert_eq!(r, Outcome::Complete(20)); assert_eq!(asset_list(Parachain(1)), vec![(Here, 900u128).into()]); assert_eq!( @@ -305,17 +275,15 @@ fn basic_asset_trap_should_work() { ); // Same again doesn't work :-) - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ - ClaimAsset { assets: (Here, 100u128).into(), ticket: GeneralIndex(0u128).into() }, - DepositAsset { - assets: Wild(AllCounted(1)), - beneficiary: AccountIndex64 { index: 3, network: None }.into(), - }, - ]), - 20, - ); + let message = Xcm(vec![ + ClaimAsset { assets: (Here, 100u128).into(), ticket: GeneralIndex(0).into() }, + DepositAsset { + assets: Wild(AllCounted(1)), + beneficiary: AccountIndex64 { index: 3, network: None }.into(), + }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 20); assert_eq!(r, Outcome::Incomplete(10, XcmError::UnknownClaim)); } @@ -335,99 +303,89 @@ fn max_assets_limit_should_work() { add_asset(Parachain(1), ([9u8; 32], 1000u128)); // Attempt to withdraw 8 (=2x4)different assets. This will succeed. - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ - WithdrawAsset(([1u8; 32], 100u128).into()), - WithdrawAsset(([2u8; 32], 100u128).into()), - WithdrawAsset(([3u8; 32], 100u128).into()), - WithdrawAsset(([4u8; 32], 100u128).into()), - WithdrawAsset(([5u8; 32], 100u128).into()), - WithdrawAsset(([6u8; 32], 100u128).into()), - WithdrawAsset(([7u8; 32], 100u128).into()), - WithdrawAsset(([8u8; 32], 100u128).into()), - ]), - 100, - ); + let message = Xcm(vec![ + WithdrawAsset(([1u8; 32], 100u128).into()), + WithdrawAsset(([2u8; 32], 100u128).into()), + WithdrawAsset(([3u8; 32], 100u128).into()), + WithdrawAsset(([4u8; 32], 100u128).into()), + WithdrawAsset(([5u8; 32], 100u128).into()), + WithdrawAsset(([6u8; 32], 100u128).into()), + WithdrawAsset(([7u8; 32], 100u128).into()), + WithdrawAsset(([8u8; 32], 100u128).into()), + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 100); assert_eq!(r, Outcome::Complete(85)); // Attempt to withdraw 9 different assets will fail. - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ - WithdrawAsset(([1u8; 32], 100u128).into()), - WithdrawAsset(([2u8; 32], 100u128).into()), - WithdrawAsset(([3u8; 32], 100u128).into()), - WithdrawAsset(([4u8; 32], 100u128).into()), - WithdrawAsset(([5u8; 32], 100u128).into()), - WithdrawAsset(([6u8; 32], 100u128).into()), - WithdrawAsset(([7u8; 32], 100u128).into()), - WithdrawAsset(([8u8; 32], 100u128).into()), - WithdrawAsset(([9u8; 32], 100u128).into()), - ]), - 100, - ); + let message = Xcm(vec![ + WithdrawAsset(([1u8; 32], 100u128).into()), + WithdrawAsset(([2u8; 32], 100u128).into()), + WithdrawAsset(([3u8; 32], 100u128).into()), + WithdrawAsset(([4u8; 32], 100u128).into()), + WithdrawAsset(([5u8; 32], 100u128).into()), + WithdrawAsset(([6u8; 32], 100u128).into()), + WithdrawAsset(([7u8; 32], 100u128).into()), + WithdrawAsset(([8u8; 32], 100u128).into()), + WithdrawAsset(([9u8; 32], 100u128).into()), + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 100); assert_eq!(r, Outcome::Incomplete(95, XcmError::HoldingWouldOverflow)); // Attempt to withdraw 4 different assets and then the same 4 and then a different 4 will succeed. - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ - WithdrawAsset(([1u8; 32], 100u128).into()), - WithdrawAsset(([2u8; 32], 100u128).into()), - WithdrawAsset(([3u8; 32], 100u128).into()), - WithdrawAsset(([4u8; 32], 100u128).into()), - WithdrawAsset(([1u8; 32], 100u128).into()), - WithdrawAsset(([2u8; 32], 100u128).into()), - WithdrawAsset(([3u8; 32], 100u128).into()), - WithdrawAsset(([4u8; 32], 100u128).into()), - WithdrawAsset(([5u8; 32], 100u128).into()), - WithdrawAsset(([6u8; 32], 100u128).into()), - WithdrawAsset(([7u8; 32], 100u128).into()), - WithdrawAsset(([8u8; 32], 100u128).into()), - ]), - 200, - ); + let message = Xcm(vec![ + WithdrawAsset(([1u8; 32], 100u128).into()), + WithdrawAsset(([2u8; 32], 100u128).into()), + WithdrawAsset(([3u8; 32], 100u128).into()), + WithdrawAsset(([4u8; 32], 100u128).into()), + WithdrawAsset(([1u8; 32], 100u128).into()), + WithdrawAsset(([2u8; 32], 100u128).into()), + WithdrawAsset(([3u8; 32], 100u128).into()), + WithdrawAsset(([4u8; 32], 100u128).into()), + WithdrawAsset(([5u8; 32], 100u128).into()), + WithdrawAsset(([6u8; 32], 100u128).into()), + WithdrawAsset(([7u8; 32], 100u128).into()), + WithdrawAsset(([8u8; 32], 100u128).into()), + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 200); assert_eq!(r, Outcome::Complete(125)); // Attempt to withdraw 4 different assets and then a different 4 and then the same 4 will fail. - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ - WithdrawAsset(([1u8; 32], 100u128).into()), - WithdrawAsset(([2u8; 32], 100u128).into()), - WithdrawAsset(([3u8; 32], 100u128).into()), - WithdrawAsset(([4u8; 32], 100u128).into()), - WithdrawAsset(([5u8; 32], 100u128).into()), - WithdrawAsset(([6u8; 32], 100u128).into()), - WithdrawAsset(([7u8; 32], 100u128).into()), - WithdrawAsset(([8u8; 32], 100u128).into()), - WithdrawAsset(([1u8; 32], 100u128).into()), - WithdrawAsset(([2u8; 32], 100u128).into()), - WithdrawAsset(([3u8; 32], 100u128).into()), - WithdrawAsset(([4u8; 32], 100u128).into()), - ]), - 200, - ); + let message = Xcm(vec![ + WithdrawAsset(([1u8; 32], 100u128).into()), + WithdrawAsset(([2u8; 32], 100u128).into()), + WithdrawAsset(([3u8; 32], 100u128).into()), + WithdrawAsset(([4u8; 32], 100u128).into()), + WithdrawAsset(([5u8; 32], 100u128).into()), + WithdrawAsset(([6u8; 32], 100u128).into()), + WithdrawAsset(([7u8; 32], 100u128).into()), + WithdrawAsset(([8u8; 32], 100u128).into()), + WithdrawAsset(([1u8; 32], 100u128).into()), + WithdrawAsset(([2u8; 32], 100u128).into()), + WithdrawAsset(([3u8; 32], 100u128).into()), + WithdrawAsset(([4u8; 32], 100u128).into()), + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 200); assert_eq!(r, Outcome::Incomplete(95, XcmError::HoldingWouldOverflow)); // Attempt to withdraw 4 different assets and then a different 4 and then the same 4 will fail. - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ - WithdrawAsset(MultiAssets::from(vec![ - ([1u8; 32], 100u128).into(), - ([2u8; 32], 100u128).into(), - ([3u8; 32], 100u128).into(), - ([4u8; 32], 100u128).into(), - ([5u8; 32], 100u128).into(), - ([6u8; 32], 100u128).into(), - ([7u8; 32], 100u128).into(), - ([8u8; 32], 100u128).into(), - ])), - WithdrawAsset(([1u8; 32], 100u128).into()), - ]), - 200, - ); + let message = Xcm(vec![ + WithdrawAsset(MultiAssets::from(vec![ + ([1u8; 32], 100u128).into(), + ([2u8; 32], 100u128).into(), + ([3u8; 32], 100u128).into(), + ([4u8; 32], 100u128).into(), + ([5u8; 32], 100u128).into(), + ([6u8; 32], 100u128).into(), + ([7u8; 32], 100u128).into(), + ([8u8; 32], 100u128).into(), + ])), + WithdrawAsset(([1u8; 32], 100u128).into()), + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 200); assert_eq!(r, Outcome::Incomplete(25, XcmError::HoldingWouldOverflow)); } diff --git a/xcm/xcm-builder/src/tests/basic.rs b/xcm/xcm-builder/src/tests/basic.rs index 383385a9baad..ea378e849873 100644 --- a/xcm/xcm-builder/src/tests/basic.rs +++ b/xcm/xcm-builder/src/tests/basic.rs @@ -84,13 +84,15 @@ fn code_registers_should_work() { let limit = ::Weigher::weight(&mut message).unwrap(); assert_eq!(limit, 70); - let r = XcmExecutor::::execute_xcm(Here, message.clone(), limit); + let hash = fake_message_hash(&message); + + let r = XcmExecutor::::execute_xcm(Here, message.clone(), hash, limit); assert_eq!(r, Outcome::Complete(50)); // We don't pay the 20 weight for the error handler. assert_eq!(asset_list(AccountIndex64 { index: 3, network: None }), vec![(Here, 13u128).into()]); assert_eq!(asset_list(Here), vec![(Here, 8u128).into()]); assert_eq!(sent_xcm(), vec![]); - let r = XcmExecutor::::execute_xcm(Here, message, limit); + let r = XcmExecutor::::execute_xcm(Here, message, hash, limit); assert_eq!(r, Outcome::Complete(70)); // We pay the full weight here. assert_eq!(asset_list(AccountIndex64 { index: 3, network: None }), vec![(Here, 20u128).into()]); assert_eq!(asset_list(Here), vec![(Here, 1u128).into()]); diff --git a/xcm/xcm-builder/src/tests/bridging/local_para_para.rs b/xcm/xcm-builder/src/tests/bridging/local_para_para.rs index 0e6bcfd4b6dd..396aa81d792f 100644 --- a/xcm/xcm-builder/src/tests/bridging/local_para_para.rs +++ b/xcm/xcm-builder/src/tests/bridging/local_para_para.rs @@ -42,7 +42,7 @@ type Router = LocalUnpaidExporter, Un fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); let dest = (Parent, Parent, Remote::get(), Parachain(1)).into(); - assert_eq!(send_xcm::(dest, msg), Ok((Here, 100).into())); + assert_eq!(send_xcm::(dest, msg).unwrap().1, (Here, 100).into()); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -69,8 +69,9 @@ fn sending_to_bridged_chain_works() { /// ``` #[test] fn sending_to_parachain_of_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok((Here, 100).into())); + assert_eq!(send_xcm::(dest, msg).unwrap().1, (Here, 100).into()); assert_eq!(TheBridge::service(), 1); let expected = vec![( (Parent, Parachain(1000)).into(), @@ -95,8 +96,9 @@ fn sending_to_parachain_of_bridged_chain_works() { /// ``` #[test] fn sending_to_relay_chain_of_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); let dest = (Parent, Parent, Remote::get()).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok((Here, 100).into())); + assert_eq!(send_xcm::(dest, msg).unwrap().1, (Here, 100).into()); assert_eq!(TheBridge::service(), 1); let expected = vec![( Parent.into(), diff --git a/xcm/xcm-builder/src/tests/bridging/local_relay_relay.rs b/xcm/xcm-builder/src/tests/bridging/local_relay_relay.rs index 244458f363fd..7fd5de2b2719 100644 --- a/xcm/xcm-builder/src/tests/bridging/local_relay_relay.rs +++ b/xcm/xcm-builder/src/tests/bridging/local_relay_relay.rs @@ -37,7 +37,10 @@ type Router = LocalUnpaidExporter, Un #[test] fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); - assert_eq!(send_xcm::((Parent, Remote::get()).into(), msg), Ok((Here, 100).into())); + assert_eq!( + send_xcm::((Parent, Remote::get()).into(), msg).unwrap().1, + (Here, 100).into() + ); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -57,8 +60,9 @@ fn sending_to_bridged_chain_works() { /// ``` #[test] fn sending_to_parachain_of_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); let dest = (Parent, Remote::get(), Parachain(1000)).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok((Here, 100).into())); + assert_eq!(send_xcm::(dest, msg).unwrap().1, (Here, 100).into()); assert_eq!(TheBridge::service(), 1); let expected = vec![(Parachain(1000).into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]; diff --git a/xcm/xcm-builder/src/tests/bridging/mod.rs b/xcm/xcm-builder/src/tests/bridging/mod.rs index 575cd7af7368..f145fc2d4d0c 100644 --- a/xcm/xcm-builder/src/tests/bridging/mod.rs +++ b/xcm/xcm-builder/src/tests/bridging/mod.rs @@ -69,9 +69,10 @@ impl SendXcm for TestRemoteIncomingRouter { let pair = (dest.take().unwrap(), msg.take().unwrap()); Ok((pair, MultiAssets::new())) } - fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result<(), SendError> { + fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result { + let hash = fake_message_hash(&pair.1); REMOTE_INCOMING_XCM.with(|q| q.borrow_mut().push(pair)); - Ok(()) + Ok(hash) } } @@ -99,9 +100,8 @@ fn deliver( c: u32, d: InteriorMultiLocation, m: Xcm<()>, -) -> Result<(), SendError> { - export_xcm::(n, c, d, m)?; - Ok(()) +) -> Result { + export_xcm::(n, c, d, m).map(|(hash, _)| hash) } impl, Remote: Get, RemoteExporter: ExportXcm> SendXcm @@ -121,7 +121,7 @@ impl, Remote: Get, RemoteExporter: ExportXcm> S Ok((message, MultiAssets::new())) } - fn deliver(message: Xcm<()>) -> Result<(), SendError> { + fn deliver(message: Xcm<()>) -> Result { // We now pretend that the message was delivered from `Local` to `Remote`, and execute // so we need to ensure that the `TestConfig` is set up properly for executing as // though it is `Remote`. @@ -130,10 +130,11 @@ impl, Remote: Get, RemoteExporter: ExportXcm> S AllowUnpaidFrom::set(vec![origin.clone()]); set_exporter_override(price::, deliver::); // The we execute it: + let hash = fake_message_hash(&message); let outcome = - XcmExecutor::::execute_xcm(origin, message.into(), 2_000_000_000_000); + XcmExecutor::::execute_xcm(origin, message.into(), hash, 2_000_000_000_000); match outcome { - Outcome::Complete(..) => Ok(()), + Outcome::Complete(..) => Ok(hash), Outcome::Incomplete(..) => Err(Transport("Error executing")), Outcome::Error(..) => Err(Transport("Unable to execute")), } @@ -161,7 +162,7 @@ impl, Remote: Get, RemoteExporter: ExportXcm> S Ok((message, MultiAssets::new())) } - fn deliver(message: Xcm<()>) -> Result<(), SendError> { + fn deliver(message: Xcm<()>) -> Result { // We now pretend that the message was delivered from `Local` to `Remote`, and execute // so we need to ensure that the `TestConfig` is set up properly for executing as // though it is `Remote`. @@ -170,10 +171,11 @@ impl, Remote: Get, RemoteExporter: ExportXcm> S AllowPaidFrom::set(vec![origin.clone()]); set_exporter_override(price::, deliver::); // The we execute it: + let hash = fake_message_hash(&message); let outcome = - XcmExecutor::::execute_xcm(origin, message.into(), 2_000_000_000_000); + XcmExecutor::::execute_xcm(origin, message.into(), hash, 2_000_000_000_000); match outcome { - Outcome::Complete(..) => Ok(()), + Outcome::Complete(..) => Ok(hash), Outcome::Incomplete(..) => Err(Transport("Error executing")), Outcome::Error(..) => Err(Transport("Unable to execute")), } diff --git a/xcm/xcm-builder/src/tests/bridging/paid_remote_relay_relay.rs b/xcm/xcm-builder/src/tests/bridging/paid_remote_relay_relay.rs index f3b31582116c..86cea75e7300 100644 --- a/xcm/xcm-builder/src/tests/bridging/paid_remote_relay_relay.rs +++ b/xcm/xcm-builder/src/tests/bridging/paid_remote_relay_relay.rs @@ -66,7 +66,7 @@ fn sending_to_bridged_chain_works() { add_asset(Parachain(100), (Here, 1000u128)); let msg = Xcm(vec![Trap(1)]); - assert_eq!(send_xcm::(dest, msg), Ok((Parent, 150u128).into())); + assert_eq!(send_xcm::(dest, msg).unwrap().1, (Parent, 150u128).into()); assert_eq!(TheBridge::service(), 1); assert_eq!( take_received_remote_messages(), @@ -106,7 +106,8 @@ fn sending_to_parachain_of_bridged_chain_works() { // Initialize the local relay so that our parachain has funds to pay for export. add_asset(Parachain(100), (Here, 1000u128)); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok((Parent, 150u128).into())); + let msg = Xcm(vec![Trap(1)]); + assert_eq!(send_xcm::(dest, msg).unwrap().1, (Parent, 150u128).into()); assert_eq!(TheBridge::service(), 1); let expected = vec![( Parachain(100).into(), diff --git a/xcm/xcm-builder/src/tests/bridging/remote_para_para.rs b/xcm/xcm-builder/src/tests/bridging/remote_para_para.rs index a79c1efa23ba..648807763b2a 100644 --- a/xcm/xcm-builder/src/tests/bridging/remote_para_para.rs +++ b/xcm/xcm-builder/src/tests/bridging/remote_para_para.rs @@ -50,8 +50,10 @@ type LocalRouter = (LocalInnerRouter, LocalBridgingRouter); fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); assert_eq!( - send_xcm::((Parent, Parent, Remote::get(), Parachain(1)).into(), msg), - Ok(MultiAssets::new()) + send_xcm::((Parent, Parent, Remote::get(), Parachain(1)).into(), msg) + .unwrap() + .1, + MultiAssets::new() ); assert_eq!(TheBridge::service(), 1); assert_eq!( @@ -79,8 +81,9 @@ fn sending_to_bridged_chain_works() { /// ``` #[test] fn sending_to_sibling_of_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); + assert_eq!(send_xcm::(dest, msg).unwrap().1, MultiAssets::new()); assert_eq!(TheBridge::service(), 1); let expected = vec![( (Parent, Parachain(1000)).into(), @@ -105,8 +108,9 @@ fn sending_to_sibling_of_bridged_chain_works() { /// ``` #[test] fn sending_to_relay_of_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); let dest = (Parent, Parent, Remote::get()).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); + assert_eq!(send_xcm::(dest, msg).unwrap().1, MultiAssets::new()); assert_eq!(TheBridge::service(), 1); let expected = vec![( Parent.into(), diff --git a/xcm/xcm-builder/src/tests/bridging/remote_para_para_via_relay.rs b/xcm/xcm-builder/src/tests/bridging/remote_para_para_via_relay.rs index 3746e5c7766c..0a6e0bfb73dd 100644 --- a/xcm/xcm-builder/src/tests/bridging/remote_para_para_via_relay.rs +++ b/xcm/xcm-builder/src/tests/bridging/remote_para_para_via_relay.rs @@ -50,8 +50,10 @@ type LocalRouter = (LocalInnerRouter, LocalBridgingRouter); fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); assert_eq!( - send_xcm::((Parent, Remote::get(), Parachain(1)).into(), msg), - Ok(MultiAssets::new()) + send_xcm::((Parent, Remote::get(), Parachain(1)).into(), msg) + .unwrap() + .1, + MultiAssets::new() ); assert_eq!(TheBridge::service(), 1); assert_eq!( @@ -72,8 +74,9 @@ fn sending_to_bridged_chain_works() { /// ``` #[test] fn sending_to_sibling_of_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); let dest = (Parent, Remote::get(), Parachain(1000)).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); + assert_eq!(send_xcm::(dest, msg).unwrap().1, MultiAssets::new()); assert_eq!(TheBridge::service(), 1); let expected = vec![( (Parent, Parachain(1000)).into(), @@ -94,8 +97,9 @@ fn sending_to_sibling_of_bridged_chain_works() { /// ``` #[test] fn sending_to_relay_of_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); let dest = (Parent, Remote::get()).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); + assert_eq!(send_xcm::(dest, msg).unwrap().1, MultiAssets::new()); assert_eq!(TheBridge::service(), 1); let expected = vec![(Parent.into(), Xcm(vec![UniversalOrigin(Local::get().into()), Trap(1)]))]; assert_eq!(take_received_remote_messages(), expected); diff --git a/xcm/xcm-builder/src/tests/bridging/remote_relay_relay.rs b/xcm/xcm-builder/src/tests/bridging/remote_relay_relay.rs index 0fed565a53f0..83cc0ac78096 100644 --- a/xcm/xcm-builder/src/tests/bridging/remote_relay_relay.rs +++ b/xcm/xcm-builder/src/tests/bridging/remote_relay_relay.rs @@ -50,8 +50,8 @@ type LocalRouter = (LocalInnerRouter, LocalBridgeRouter); fn sending_to_bridged_chain_works() { let msg = Xcm(vec![Trap(1)]); assert_eq!( - send_xcm::((Parent, Parent, Remote::get()).into(), msg), - Ok(MultiAssets::new()) + send_xcm::((Parent, Parent, Remote::get()).into(), msg).unwrap().1, + MultiAssets::new() ); assert_eq!(TheBridge::service(), 1); assert_eq!( @@ -79,8 +79,9 @@ fn sending_to_bridged_chain_works() { /// ``` #[test] fn sending_to_parachain_of_bridged_chain_works() { + let msg = Xcm(vec![Trap(1)]); let dest = (Parent, Parent, Remote::get(), Parachain(1000)).into(); - assert_eq!(send_xcm::(dest, Xcm(vec![Trap(1)])), Ok(MultiAssets::new())); + assert_eq!(send_xcm::(dest, msg).unwrap().1, MultiAssets::new()); assert_eq!(TheBridge::service(), 1); let expected = vec![( Parachain(1000).into(), diff --git a/xcm/xcm-builder/src/tests/expecting.rs b/xcm/xcm-builder/src/tests/expecting.rs index d54eef86c358..33830a721e05 100644 --- a/xcm/xcm-builder/src/tests/expecting.rs +++ b/xcm/xcm-builder/src/tests/expecting.rs @@ -21,137 +21,117 @@ fn expect_pallet_should_work() { AllowUnpaidFrom::set(vec![X1(Parachain(1)).into()]); // They want to transfer 100 of our native asset from sovereign account of parachain #1 into #2 // and let them know to hand it to account #3. - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ExpectPallet { - index: 1, - name: b"Balances".as_ref().into(), - module_name: b"pallet_balances".as_ref().into(), - crate_major: 1, - min_crate_minor: 42, - }]), - 50, - ); + let message = Xcm(vec![ExpectPallet { + index: 1, + name: b"Balances".as_ref().into(), + module_name: b"pallet_balances".as_ref().into(), + crate_major: 1, + min_crate_minor: 42, + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Complete(10)); - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ExpectPallet { - index: 1, - name: b"Balances".as_ref().into(), - module_name: b"pallet_balances".as_ref().into(), - crate_major: 1, - min_crate_minor: 41, - }]), - 50, - ); + let message = Xcm(vec![ExpectPallet { + index: 1, + name: b"Balances".as_ref().into(), + module_name: b"pallet_balances".as_ref().into(), + crate_major: 1, + min_crate_minor: 41, + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Complete(10)); } #[test] fn expect_pallet_should_fail_correctly() { AllowUnpaidFrom::set(vec![X1(Parachain(1)).into()]); - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ExpectPallet { - index: 1, - name: b"Balances".as_ref().into(), - module_name: b"pallet_balances".as_ref().into(), - crate_major: 1, - min_crate_minor: 60, - }]), - 50, - ); + let message = Xcm(vec![ExpectPallet { + index: 1, + name: b"Balances".as_ref().into(), + module_name: b"pallet_balances".as_ref().into(), + crate_major: 1, + min_crate_minor: 60, + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Incomplete(10, XcmError::VersionIncompatible)); - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ExpectPallet { - index: 1, - name: b"System".as_ref().into(), - module_name: b"pallet_balances".as_ref().into(), - crate_major: 1, - min_crate_minor: 42, - }]), - 50, - ); + let message = Xcm(vec![ExpectPallet { + index: 1, + name: b"System".as_ref().into(), + module_name: b"pallet_balances".as_ref().into(), + crate_major: 1, + min_crate_minor: 42, + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Incomplete(10, XcmError::NameMismatch)); - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ExpectPallet { - index: 1, - name: b"Balances".as_ref().into(), - module_name: b"pallet_system".as_ref().into(), - crate_major: 1, - min_crate_minor: 42, - }]), - 50, - ); + let message = Xcm(vec![ExpectPallet { + index: 1, + name: b"Balances".as_ref().into(), + module_name: b"pallet_system".as_ref().into(), + crate_major: 1, + min_crate_minor: 42, + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Incomplete(10, XcmError::NameMismatch)); - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ExpectPallet { - index: 0, - name: b"Balances".as_ref().into(), - module_name: b"pallet_balances".as_ref().into(), - crate_major: 1, - min_crate_minor: 42, - }]), - 50, - ); + let message = Xcm(vec![ExpectPallet { + index: 0, + name: b"Balances".as_ref().into(), + module_name: b"pallet_balances".as_ref().into(), + crate_major: 1, + min_crate_minor: 42, + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Incomplete(10, XcmError::NameMismatch)); - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ExpectPallet { - index: 2, - name: b"Balances".as_ref().into(), - module_name: b"pallet_balances".as_ref().into(), - crate_major: 1, - min_crate_minor: 42, - }]), - 50, - ); + let message = Xcm(vec![ExpectPallet { + index: 2, + name: b"Balances".as_ref().into(), + module_name: b"pallet_balances".as_ref().into(), + crate_major: 1, + min_crate_minor: 42, + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Incomplete(10, XcmError::PalletNotFound)); - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ExpectPallet { - index: 1, - name: b"Balances".as_ref().into(), - module_name: b"pallet_balances".as_ref().into(), - crate_major: 2, - min_crate_minor: 42, - }]), - 50, - ); + let message = Xcm(vec![ExpectPallet { + index: 1, + name: b"Balances".as_ref().into(), + module_name: b"pallet_balances".as_ref().into(), + crate_major: 2, + min_crate_minor: 42, + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Incomplete(10, XcmError::VersionIncompatible)); - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ExpectPallet { - index: 1, - name: b"Balances".as_ref().into(), - module_name: b"pallet_balances".as_ref().into(), - crate_major: 0, - min_crate_minor: 42, - }]), - 50, - ); + let message = Xcm(vec![ExpectPallet { + index: 1, + name: b"Balances".as_ref().into(), + module_name: b"pallet_balances".as_ref().into(), + crate_major: 0, + min_crate_minor: 42, + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Incomplete(10, XcmError::VersionIncompatible)); - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ExpectPallet { - index: 1, - name: b"Balances".as_ref().into(), - module_name: b"pallet_balances".as_ref().into(), - crate_major: 1, - min_crate_minor: 43, - }]), - 50, - ); + let message = Xcm(vec![ExpectPallet { + index: 1, + name: b"Balances".as_ref().into(), + module_name: b"pallet_balances".as_ref().into(), + crate_major: 1, + min_crate_minor: 43, + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Incomplete(10, XcmError::VersionIncompatible)); } diff --git a/xcm/xcm-builder/src/tests/locking.rs b/xcm/xcm-builder/src/tests/locking.rs index d6afe08e4970..9f58936faa39 100644 --- a/xcm/xcm-builder/src/tests/locking.rs +++ b/xcm/xcm-builder/src/tests/locking.rs @@ -27,31 +27,24 @@ fn lock_roundtrip_should_work() { set_send_price((Parent, 10u128)); // They want to lock 100 of the native parent tokens to be unlocked only by Parachain #1. - let r = XcmExecutor::::execute_xcm( - (3u64,), - Xcm(vec![ - WithdrawAsset((Parent, 100u128).into()), - SetAppendix( - vec![DepositAsset { assets: AllCounted(2).into(), beneficiary: (3u64,).into() }] - .into(), - ), - LockAsset { asset: (Parent, 100u128).into(), unlocker: (Parent, Parachain(1)).into() }, - ]), - 50, - ); + let message = Xcm(vec![ + WithdrawAsset((Parent, 100u128).into()), + SetAppendix( + vec![DepositAsset { assets: AllCounted(2).into(), beneficiary: (3u64,).into() }].into(), + ), + LockAsset { asset: (Parent, 100u128).into(), unlocker: (Parent, Parachain(1)).into() }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm((3u64,), message, hash, 50); assert_eq!(r, Outcome::Complete(40)); assert_eq!(asset_list((3u64,)), vec![(Parent, 990u128).into()]); - assert_eq!( - sent_xcm(), - vec![( - (Parent, Parachain(1)).into(), - Xcm::<()>(vec![NoteUnlockable { - owner: (Parent, Parachain(42), 3u64).into(), - asset: (Parent, 100u128).into() - },]), - )] - ); + let expected_msg = Xcm::<()>(vec![NoteUnlockable { + owner: (Parent, Parachain(42), 3u64).into(), + asset: (Parent, 100u128).into(), + }]); + let expected_hash = fake_message_hash(&expected_msg); + assert_eq!(sent_xcm(), vec![((Parent, Parachain(1)).into(), expected_msg, expected_hash)]); assert_eq!( take_lock_trace(), vec![Lock { @@ -62,11 +55,10 @@ fn lock_roundtrip_should_work() { ); // Now we'll unlock it. - let r = XcmExecutor::::execute_xcm( - (Parent, Parachain(1)), - Xcm(vec![UnlockAsset { asset: (Parent, 100u128).into(), target: (3u64,).into() }]), - 50, - ); + let message = + Xcm(vec![UnlockAsset { asset: (Parent, 100u128).into(), target: (3u64,).into() }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm((Parent, Parachain(1)), message, hash, 50); assert_eq!(r, Outcome::Complete(10)); } @@ -80,14 +72,12 @@ fn auto_fee_paying_should_work() { set_send_price((Parent, 10u128)); // They want to lock 100 of the native parent tokens to be unlocked only by Parachain #1. - let r = XcmExecutor::::execute_xcm( - (3u64,), - Xcm(vec![ - SetFeesMode { jit_withdraw: true }, - LockAsset { asset: (Parent, 100u128).into(), unlocker: (Parent, Parachain(1)).into() }, - ]), - 50, - ); + let message = Xcm(vec![ + SetFeesMode { jit_withdraw: true }, + LockAsset { asset: (Parent, 100u128).into(), unlocker: (Parent, Parachain(1)).into() }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm((3u64,), message, hash, 50); assert_eq!(r, Outcome::Complete(20)); assert_eq!(asset_list((3u64,)), vec![(Parent, 990u128).into()]); } @@ -99,14 +89,12 @@ fn lock_should_fail_correctly() { // #3 wants to lock 100 of the native parent tokens to be unlocked only by parachain ../#1, // but they don't have any. - let r = XcmExecutor::::execute_xcm( - (3u64,), - Xcm(vec![LockAsset { - asset: (Parent, 100u128).into(), - unlocker: (Parent, Parachain(1)).into(), - }]), - 50, - ); + let message = Xcm(vec![LockAsset { + asset: (Parent, 100u128).into(), + unlocker: (Parent, Parachain(1)).into(), + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm((3u64,), message, hash, 50); assert_eq!(r, Outcome::Incomplete(10, XcmError::LockError)); assert_eq!(sent_xcm(), vec![]); assert_eq!(take_lock_trace(), vec![]); @@ -118,14 +106,12 @@ fn lock_should_fail_correctly() { // #3 wants to lock 100 of the native parent tokens to be unlocked only by parachain ../#1, // but there's nothing to pay the fees for sending the notification message. - let r = XcmExecutor::::execute_xcm( - (3u64,), - Xcm(vec![LockAsset { - asset: (Parent, 100u128).into(), - unlocker: (Parent, Parachain(1)).into(), - }]), - 50, - ); + let message = Xcm(vec![LockAsset { + asset: (Parent, 100u128).into(), + unlocker: (Parent, Parachain(1)).into(), + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm((3u64,), message, hash, 50); assert_eq!(r, Outcome::Incomplete(10, XcmError::NotHoldingFees)); assert_eq!(sent_xcm(), vec![]); assert_eq!(take_lock_trace(), vec![]); @@ -141,11 +127,10 @@ fn remote_unlock_roundtrip_should_work() { set_send_price((Parent, 10u128)); // We have been told by Parachain #1 that Account #3 has locked funds which we can unlock. - let r = XcmExecutor::::execute_xcm( - (Parent, Parachain(1)), - Xcm(vec![NoteUnlockable { asset: (Parent, 100u128).into(), owner: (3u64,).into() }]), - 50, - ); + let message = + Xcm(vec![NoteUnlockable { asset: (Parent, 100u128).into(), owner: (3u64,).into() }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm((Parent, Parachain(1)), message, hash, 50); assert_eq!(r, Outcome::Complete(10)); assert_eq!( take_lock_trace(), @@ -157,34 +142,22 @@ fn remote_unlock_roundtrip_should_work() { ); // Let's request those funds be unlocked. - let r = XcmExecutor::::execute_xcm( - (3u64,), - Xcm(vec![ - WithdrawAsset((Parent, 100u128).into()), - SetAppendix( - vec![DepositAsset { assets: AllCounted(2).into(), beneficiary: (3u64,).into() }] - .into(), - ), - RequestUnlock { - asset: (Parent, 100u128).into(), - locker: (Parent, Parachain(1)).into(), - }, - ]), - 50, - ); + let message = Xcm(vec![ + WithdrawAsset((Parent, 100u128).into()), + SetAppendix( + vec![DepositAsset { assets: AllCounted(2).into(), beneficiary: (3u64,).into() }].into(), + ), + RequestUnlock { asset: (Parent, 100u128).into(), locker: (Parent, Parachain(1)).into() }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm((3u64,), message, hash, 50); assert_eq!(r, Outcome::Complete(40)); assert_eq!(asset_list((3u64,)), vec![(Parent, 990u128).into()]); - assert_eq!( - sent_xcm(), - vec![( - (Parent, Parachain(1)).into(), - Xcm::<()>(vec![UnlockAsset { - target: (3u64,).into(), - asset: (Parent, 100u128).into() - },]), - )] - ); + let expected_msg = + Xcm::<()>(vec![UnlockAsset { target: (3u64,).into(), asset: (Parent, 100u128).into() }]); + let expected_hash = fake_message_hash(&expected_msg); + assert_eq!(sent_xcm(), vec![((Parent, Parachain(1)).into(), expected_msg, expected_hash)]); assert_eq!( take_lock_trace(), vec![Reduce { @@ -205,38 +178,33 @@ fn remote_unlock_should_fail_correctly() { // We want to unlock 100 of the native parent tokens which were locked for us on parachain. // This won't work as we don't have any record of them being locked for us. // No message will be sent and no lock records changed. - let r = XcmExecutor::::execute_xcm( - (3u64,), - Xcm(vec![RequestUnlock { - asset: (Parent, 100u128).into(), - locker: (Parent, Parachain(1)).into(), - }]), - 50, - ); + let message = Xcm(vec![RequestUnlock { + asset: (Parent, 100u128).into(), + locker: (Parent, Parachain(1)).into(), + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm((3u64,), message, hash, 50); assert_eq!(r, Outcome::Incomplete(10, XcmError::LockError)); assert_eq!(sent_xcm(), vec![]); assert_eq!(take_lock_trace(), vec![]); // We have been told by Parachain #1 that Account #3 has locked funds which we can unlock. - let r = XcmExecutor::::execute_xcm( - (Parent, Parachain(1)), - Xcm(vec![NoteUnlockable { asset: (Parent, 100u128).into(), owner: (3u64,).into() }]), - 50, - ); + let message = + Xcm(vec![NoteUnlockable { asset: (Parent, 100u128).into(), owner: (3u64,).into() }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm((Parent, Parachain(1)), message, hash, 50); assert_eq!(r, Outcome::Complete(10)); let _discard = take_lock_trace(); // We want to unlock 100 of the native parent tokens which were locked for us on parachain. // This won't work now as we don't have the funds to send the onward message. // No message will be sent and no lock records changed. - let r = XcmExecutor::::execute_xcm( - (3u64,), - Xcm(vec![RequestUnlock { - asset: (Parent, 100u128).into(), - locker: (Parent, Parachain(1)).into(), - }]), - 50, - ); + let message = Xcm(vec![RequestUnlock { + asset: (Parent, 100u128).into(), + locker: (Parent, Parachain(1)).into(), + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm((3u64,), message, hash, 50); assert_eq!(r, Outcome::Incomplete(10, XcmError::NotHoldingFees)); assert_eq!(sent_xcm(), vec![]); diff --git a/xcm/xcm-builder/src/tests/mock.rs b/xcm/xcm-builder/src/tests/mock.rs index 3972170ab15c..e1f2fbcbaa87 100644 --- a/xcm/xcm-builder/src/tests/mock.rs +++ b/xcm/xcm-builder/src/tests/mock.rs @@ -30,13 +30,14 @@ pub use frame_support::{ weights::{GetDispatchInfo, PostDispatchInfo}, }; pub use parity_scale_codec::{Decode, Encode}; +pub use sp_io::hashing::blake2_256; pub use sp_std::{ cell::RefCell, collections::{btree_map::BTreeMap, btree_set::BTreeSet}, fmt::Debug, marker::PhantomData, }; -pub use xcm::latest::prelude::*; +pub use xcm::prelude::*; pub use xcm_executor::{ traits::{ AssetExchange, AssetLock, ConvertOrigin, Enact, ExportXcm, FeeManager, FeeReason, @@ -105,26 +106,26 @@ impl GetDispatchInfo for TestCall { } thread_local! { - pub static SENT_XCM: RefCell)>> = RefCell::new(Vec::new()); - pub static EXPORTED_XCM: RefCell)>> = RefCell::new(Vec::new()); + pub static SENT_XCM: RefCell, XcmHash)>> = RefCell::new(Vec::new()); + pub static EXPORTED_XCM: RefCell, XcmHash)>> = RefCell::new(Vec::new()); pub static EXPORTER_OVERRIDE: RefCell) -> Result, - fn(NetworkId, u32, InteriorMultiLocation, Xcm<()>) -> Result<(), SendError>, + fn(NetworkId, u32, InteriorMultiLocation, Xcm<()>) -> Result, )>> = RefCell::new(None); pub static SEND_PRICE: RefCell = RefCell::new(MultiAssets::new()); } -pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> { +pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm, XcmHash)> { SENT_XCM.with(|q| (*q.borrow()).clone()) } pub fn set_send_price(p: impl Into) { SEND_PRICE.with(|l| l.replace(p.into().into())); } -pub fn exported_xcm() -> Vec<(NetworkId, u32, InteriorMultiLocation, opaque::Xcm)> { +pub fn exported_xcm() -> Vec<(NetworkId, u32, InteriorMultiLocation, opaque::Xcm, XcmHash)> { EXPORTED_XCM.with(|q| (*q.borrow()).clone()) } pub fn set_exporter_override( price: fn(NetworkId, u32, &InteriorMultiLocation, &Xcm<()>) -> Result, - deliver: fn(NetworkId, u32, InteriorMultiLocation, Xcm<()>) -> Result<(), SendError>, + deliver: fn(NetworkId, u32, InteriorMultiLocation, Xcm<()>) -> Result, ) { EXPORTER_OVERRIDE.with(|x| x.replace(Some((price, deliver)))); } @@ -134,28 +135,31 @@ pub fn clear_exporter_override() { } pub struct TestMessageSender; impl SendXcm for TestMessageSender { - type Ticket = (MultiLocation, Xcm<()>); + type Ticket = (MultiLocation, Xcm<()>, XcmHash); fn validate( dest: &mut Option, msg: &mut Option>, - ) -> SendResult<(MultiLocation, Xcm<()>)> { - let pair = (dest.take().unwrap(), msg.take().unwrap()); - Ok((pair, SEND_PRICE.with(|l| l.borrow().clone()))) + ) -> SendResult<(MultiLocation, Xcm<()>, XcmHash)> { + let msg = msg.take().unwrap(); + let hash = fake_message_hash(&msg); + let triplet = (dest.take().unwrap(), msg, hash); + Ok((triplet, SEND_PRICE.with(|l| l.borrow().clone()))) } - fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result<(), SendError> { - SENT_XCM.with(|q| q.borrow_mut().push(pair)); - Ok(()) + fn deliver(triplet: (MultiLocation, Xcm<()>, XcmHash)) -> Result { + let hash = triplet.2; + SENT_XCM.with(|q| q.borrow_mut().push(triplet)); + Ok(hash) } } pub struct TestMessageExporter; impl ExportXcm for TestMessageExporter { - type Ticket = (NetworkId, u32, InteriorMultiLocation, Xcm<()>); + type Ticket = (NetworkId, u32, InteriorMultiLocation, Xcm<()>, XcmHash); fn validate( network: NetworkId, channel: u32, dest: &mut Option, msg: &mut Option>, - ) -> SendResult<(NetworkId, u32, InteriorMultiLocation, Xcm<()>)> { + ) -> SendResult<(NetworkId, u32, InteriorMultiLocation, Xcm<()>, XcmHash)> { let (d, m) = (dest.take().unwrap(), msg.take().unwrap()); let r: Result = EXPORTER_OVERRIDE.with(|e| { if let Some((ref f, _)) = &*e.borrow() { @@ -164,8 +168,9 @@ impl ExportXcm for TestMessageExporter { Ok(MultiAssets::new()) } }); + let h = fake_message_hash(&m); match r { - Ok(price) => Ok(((network, channel, d, m), price)), + Ok(price) => Ok(((network, channel, d, m, h), price)), Err(e) => { *dest = Some(d); *msg = Some(m); @@ -173,14 +178,17 @@ impl ExportXcm for TestMessageExporter { }, } } - fn deliver(tuple: (NetworkId, u32, InteriorMultiLocation, Xcm<()>)) -> Result<(), SendError> { + fn deliver( + tuple: (NetworkId, u32, InteriorMultiLocation, Xcm<()>, XcmHash), + ) -> Result { EXPORTER_OVERRIDE.with(|e| { if let Some((_, ref f)) = &*e.borrow() { - let (network, channel, dest, msg) = tuple; + let (network, channel, dest, msg, _hash) = tuple; f(network, channel, dest, msg) } else { + let hash = tuple.4; EXPORTED_XCM.with(|q| q.borrow_mut().push(tuple)); - Ok(()) + Ok(hash) } }) } @@ -201,12 +209,20 @@ pub fn add_asset(who: impl Into, what: impl Into) { pub struct TestAssetTransactor; impl TransactAsset for TestAssetTransactor { - fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> Result<(), XcmError> { + fn deposit_asset( + what: &MultiAsset, + who: &MultiLocation, + _context: &XcmContext, + ) -> Result<(), XcmError> { add_asset(who.clone(), what.clone()); Ok(()) } - fn withdraw_asset(what: &MultiAsset, who: &MultiLocation) -> Result { + fn withdraw_asset( + what: &MultiAsset, + who: &MultiLocation, + _maybe_context: Option<&XcmContext>, + ) -> Result { ASSETS.with(|a| { a.borrow_mut() .get_mut(who) @@ -330,6 +346,7 @@ impl OnResponse for TestResponseHandler { _querier: Option<&MultiLocation>, response: xcm::latest::Response, _max_weight: Weight, + _context: &XcmContext, ) -> Weight { QUERIES.with(|q| { q.borrow_mut().entry(query_id).and_modify(|v| { @@ -596,3 +613,7 @@ impl Config for TestConfig { pub fn fungible_multi_asset(location: MultiLocation, amount: u128) -> MultiAsset { (AssetId::from(location), Fungibility::Fungible(amount)).into() } + +pub fn fake_message_hash(message: &Xcm) -> XcmHash { + message.using_encoded(sp_io::hashing::blake2_256) +} diff --git a/xcm/xcm-builder/src/tests/origins.rs b/xcm/xcm-builder/src/tests/origins.rs index 93569d9d0fb1..9b75c4f98650 100644 --- a/xcm/xcm-builder/src/tests/origins.rs +++ b/xcm/xcm-builder/src/tests/origins.rs @@ -25,35 +25,29 @@ fn universal_origin_should_work() { // Parachain 2 may represent Polkadot to us add_universal_alias(Parachain(2), Polkadot); - let r = XcmExecutor::::execute_xcm( - Parachain(2), - Xcm(vec![ - UniversalOrigin(GlobalConsensus(Kusama)), - TransferAsset { assets: (Parent, 100u128).into(), beneficiary: Here.into() }, - ]), - 50, - ); + let message = Xcm(vec![ + UniversalOrigin(GlobalConsensus(Kusama)), + TransferAsset { assets: (Parent, 100u128).into(), beneficiary: Here.into() }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(2), message, hash, 50); assert_eq!(r, Outcome::Incomplete(10, XcmError::InvalidLocation)); - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ - UniversalOrigin(GlobalConsensus(Kusama)), - TransferAsset { assets: (Parent, 100u128).into(), beneficiary: Here.into() }, - ]), - 50, - ); + let message = Xcm(vec![ + UniversalOrigin(GlobalConsensus(Kusama)), + TransferAsset { assets: (Parent, 100u128).into(), beneficiary: Here.into() }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Incomplete(20, XcmError::NotWithdrawable)); - add_asset((Ancestor(2), GlobalConsensus(Kusama)), (Parent, 100u128)); - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ - UniversalOrigin(GlobalConsensus(Kusama)), - TransferAsset { assets: (Parent, 100u128).into(), beneficiary: Here.into() }, - ]), - 50, - ); + add_asset((Ancestor(2), GlobalConsensus(Kusama)), (Parent, 100)); + let message = Xcm(vec![ + UniversalOrigin(GlobalConsensus(Kusama)), + TransferAsset { assets: (Parent, 100u128).into(), beneficiary: Here.into() }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Complete(20)); assert_eq!(asset_list((Ancestor(2), GlobalConsensus(Kusama))), vec![]); } @@ -64,15 +58,18 @@ fn export_message_should_work() { AllowUnpaidFrom::set(vec![X1(Parachain(1)).into()]); // Local parachain #1 issues a transfer asset on Polkadot Relay-chain, transfering 100 Planck to // Polkadot parachain #2. - let message = Xcm(vec![TransferAsset { + let expected_message = Xcm(vec![TransferAsset { assets: (Here, 100u128).into(), beneficiary: Parachain(2).into(), }]); - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![ExportMessage { network: Polkadot, destination: Here, xcm: message.clone() }]), - 50, - ); + let expected_hash = fake_message_hash(&expected_message); + let message = Xcm(vec![ExportMessage { + network: Polkadot, + destination: Here, + xcm: expected_message.clone(), + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Complete(10)); - assert_eq!(exported_xcm(), vec![(Polkadot, 403611790, Here, message)]); + assert_eq!(exported_xcm(), vec![(Polkadot, 403611790, Here, expected_message, expected_hash)]); } diff --git a/xcm/xcm-builder/src/tests/querying.rs b/xcm/xcm-builder/src/tests/querying.rs index feb3259208ed..43b3235cbaca 100644 --- a/xcm/xcm-builder/src/tests/querying.rs +++ b/xcm/xcm-builder/src/tests/querying.rs @@ -21,32 +21,26 @@ fn pallet_query_should_work() { AllowUnpaidFrom::set(vec![X1(Parachain(1)).into()]); // They want to transfer 100 of our native asset from sovereign account of parachain #1 into #2 // and let them know to hand it to account #3. - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![QueryPallet { - module_name: "Error".into(), - response_info: QueryResponseInfo { - destination: Parachain(1).into(), - query_id: 1, - max_weight: 50, - }, - }]), - 50, - ); + let message = Xcm(vec![QueryPallet { + module_name: "Error".into(), + response_info: QueryResponseInfo { + destination: Parachain(1).into(), + query_id: 1, + max_weight: 50, + }, + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Complete(10)); - assert_eq!( - sent_xcm(), - vec![( - Parachain(1).into(), - Xcm::<()>(vec![QueryResponse { - query_id: 1, - max_weight: 50, - response: Response::PalletsInfo(vec![].try_into().unwrap()), - querier: Some(Here.into()), - }]), - )] - ); + let expected_msg = Xcm::<()>(vec![QueryResponse { + query_id: 1, + max_weight: 50, + response: Response::PalletsInfo(vec![].try_into().unwrap()), + querier: Some(Here.into()), + }]); + let expected_hash = fake_message_hash(&expected_msg); + assert_eq!(sent_xcm(), vec![(Parachain(1).into(), expected_msg, expected_hash)]); } #[test] @@ -54,44 +48,38 @@ fn pallet_query_with_results_should_work() { AllowUnpaidFrom::set(vec![X1(Parachain(1)).into()]); // They want to transfer 100 of our native asset from sovereign account of parachain #1 into #2 // and let them know to hand it to account #3. - let r = XcmExecutor::::execute_xcm( - Parachain(1), - Xcm(vec![QueryPallet { - module_name: "pallet_balances".into(), - response_info: QueryResponseInfo { - destination: Parachain(1).into(), - query_id: 1, - max_weight: 50, - }, - }]), - 50, - ); + let message = Xcm(vec![QueryPallet { + module_name: "pallet_balances".into(), + response_info: QueryResponseInfo { + destination: Parachain(1).into(), + query_id: 1, + max_weight: 50, + }, + }]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(1), message, hash, 50); assert_eq!(r, Outcome::Complete(10)); - assert_eq!( - sent_xcm(), - vec![( - Parachain(1).into(), - Xcm::<()>(vec![QueryResponse { - query_id: 1, - max_weight: 50, - response: Response::PalletsInfo( - vec![PalletInfo::new( - 1, - b"Balances".as_ref().into(), - b"pallet_balances".as_ref().into(), - 1, - 42, - 69, - ) - .unwrap(),] - .try_into() - .unwrap() - ), - querier: Some(Here.into()), - }]), - )] - ); + let expected_msg = Xcm::<()>(vec![QueryResponse { + query_id: 1, + max_weight: 50, + response: Response::PalletsInfo( + vec![PalletInfo::new( + 1, + b"Balances".as_ref().into(), + b"pallet_balances".as_ref().into(), + 1, + 42, + 69, + ) + .unwrap()] + .try_into() + .unwrap(), + ), + querier: Some(Here.into()), + }]); + let expected_hash = fake_message_hash(&expected_msg); + assert_eq!(sent_xcm(), vec![(Parachain(1).into(), expected_msg, expected_hash)]); } #[test] @@ -107,14 +95,15 @@ fn prepaid_result_of_query_should_get_free_execution() { max_weight: 10, querier: Some(Here.into()), }]); + let hash = fake_message_hash(&message); let weight_limit = 10; // First time the response gets through since we're expecting it... - let r = XcmExecutor::::execute_xcm(Parent, message.clone(), weight_limit); + let r = XcmExecutor::::execute_xcm(Parent, message.clone(), hash, weight_limit); assert_eq!(r, Outcome::Complete(10)); assert_eq!(response(query_id).unwrap(), the_response); // Second time it doesn't, since we're not. - let r = XcmExecutor::::execute_xcm(Parent, message.clone(), weight_limit); + let r = XcmExecutor::::execute_xcm(Parent, message.clone(), hash, weight_limit); assert_eq!(r, Outcome::Error(XcmError::Barrier)); } diff --git a/xcm/xcm-builder/src/tests/transacting.rs b/xcm/xcm-builder/src/tests/transacting.rs index 009ba5ad96d1..4528cef2202b 100644 --- a/xcm/xcm-builder/src/tests/transacting.rs +++ b/xcm/xcm-builder/src/tests/transacting.rs @@ -25,8 +25,9 @@ fn transacting_should_work() { require_weight_at_most: 50, call: TestCall::Any(50, None).encode().into(), }]); + let hash = fake_message_hash(&message); let weight_limit = 60; - let r = XcmExecutor::::execute_xcm(Parent, message, weight_limit); + let r = XcmExecutor::::execute_xcm(Parent, message, hash, weight_limit); assert_eq!(r, Outcome::Complete(60)); } @@ -39,8 +40,9 @@ fn transacting_should_respect_max_weight_requirement() { require_weight_at_most: 40, call: TestCall::Any(50, None).encode().into(), }]); + let hash = fake_message_hash(&message); let weight_limit = 60; - let r = XcmExecutor::::execute_xcm(Parent, message, weight_limit); + let r = XcmExecutor::::execute_xcm(Parent, message, hash, weight_limit); assert_eq!(r, Outcome::Incomplete(50, XcmError::MaxWeightInvalid)); } @@ -53,8 +55,9 @@ fn transacting_should_refund_weight() { require_weight_at_most: 50, call: TestCall::Any(50, Some(30)).encode().into(), }]); + let hash = fake_message_hash(&message); let weight_limit = 60; - let r = XcmExecutor::::execute_xcm(Parent, message, weight_limit); + let r = XcmExecutor::::execute_xcm(Parent, message, hash, weight_limit); assert_eq!(r, Outcome::Complete(40)); } @@ -79,8 +82,9 @@ fn paid_transacting_should_refund_payment_for_unused_weight() { RefundSurplus, DepositAsset { assets: AllCounted(1).into(), beneficiary: one.clone() }, ]); + let hash = fake_message_hash(&message); let weight_limit = 100; - let r = XcmExecutor::::execute_xcm(origin, message, weight_limit); + let r = XcmExecutor::::execute_xcm(origin, message, hash, weight_limit); assert_eq!(r, Outcome::Complete(60)); assert_eq!( asset_list(AccountIndex64 { index: 1, network: None }), @@ -104,21 +108,18 @@ fn report_successful_transact_status_should_work() { max_weight: 5000, }), ]); + let hash = fake_message_hash(&message); let weight_limit = 70; - let r = XcmExecutor::::execute_xcm(Parent, message, weight_limit); + let r = XcmExecutor::::execute_xcm(Parent, message, hash, weight_limit); assert_eq!(r, Outcome::Complete(70)); - assert_eq!( - sent_xcm(), - vec![( - Parent.into(), - Xcm(vec![QueryResponse { - response: Response::DispatchResult(MaybeErrorCode::Success), - query_id: 42, - max_weight: 5000, - querier: Some(Here.into()), - }]) - )] - ); + let expected_msg = Xcm(vec![QueryResponse { + response: Response::DispatchResult(MaybeErrorCode::Success), + query_id: 42, + max_weight: 5000, + querier: Some(Here.into()), + }]); + let expected_hash = fake_message_hash(&expected_msg); + assert_eq!(sent_xcm(), vec![(Parent.into(), expected_msg, expected_hash)]); } #[test] @@ -137,21 +138,18 @@ fn report_failed_transact_status_should_work() { max_weight: 5000, }), ]); + let hash = fake_message_hash(&message); let weight_limit = 70; - let r = XcmExecutor::::execute_xcm(Parent, message, weight_limit); + let r = XcmExecutor::::execute_xcm(Parent, message, hash, weight_limit); assert_eq!(r, Outcome::Complete(70)); - assert_eq!( - sent_xcm(), - vec![( - Parent.into(), - Xcm(vec![QueryResponse { - response: Response::DispatchResult(MaybeErrorCode::Error(vec![2])), - query_id: 42, - max_weight: 5000, - querier: Some(Here.into()), - }]) - )] - ); + let expected_msg = Xcm(vec![QueryResponse { + response: Response::DispatchResult(MaybeErrorCode::Error(vec![2])), + query_id: 42, + max_weight: 5000, + querier: Some(Here.into()), + }]); + let expected_hash = fake_message_hash(&expected_msg); + assert_eq!(sent_xcm(), vec![(Parent.into(), expected_msg, expected_hash)]); } #[test] @@ -171,19 +169,16 @@ fn clear_transact_status_should_work() { max_weight: 5000, }), ]); + let hash = fake_message_hash(&message); let weight_limit = 80; - let r = XcmExecutor::::execute_xcm(Parent, message, weight_limit); + let r = XcmExecutor::::execute_xcm(Parent, message, hash, weight_limit); assert_eq!(r, Outcome::Complete(80)); - assert_eq!( - sent_xcm(), - vec![( - Parent.into(), - Xcm(vec![QueryResponse { - response: Response::DispatchResult(MaybeErrorCode::Success), - query_id: 42, - max_weight: 5000, - querier: Some(Here.into()), - }]) - )] - ); + let expected_msg = Xcm(vec![QueryResponse { + response: Response::DispatchResult(MaybeErrorCode::Success), + query_id: 42, + max_weight: 5000, + querier: Some(Here.into()), + }]); + let expected_hash = fake_message_hash(&expected_msg); + assert_eq!(sent_xcm(), vec![(Parent.into(), expected_msg, expected_hash)]); } diff --git a/xcm/xcm-builder/src/tests/version_subscriptions.rs b/xcm/xcm-builder/src/tests/version_subscriptions.rs index 2ecd12a05b4f..e7125e6e75a5 100644 --- a/xcm/xcm-builder/src/tests/version_subscriptions.rs +++ b/xcm/xcm-builder/src/tests/version_subscriptions.rs @@ -25,18 +25,20 @@ fn simple_version_subscriptions_should_work() { SetAppendix(Xcm(vec![])), SubscribeVersion { query_id: 42, max_response_weight: 5000 }, ]); + let hash = fake_message_hash(&message); let weight_limit = 20; - let r = XcmExecutor::::execute_xcm(origin, message, weight_limit); + let r = XcmExecutor::::execute_xcm(origin, message, hash, weight_limit); assert_eq!(r, Outcome::Error(XcmError::Barrier)); let origin = Parachain(1000); let message = Xcm::(vec![SubscribeVersion { query_id: 42, max_response_weight: 5000 }]); + let hash = fake_message_hash(&message); let weight_limit = 10; - let r = XcmExecutor::::execute_xcm(origin, message.clone(), weight_limit); + let r = XcmExecutor::::execute_xcm(origin, message.clone(), hash, weight_limit); assert_eq!(r, Outcome::Error(XcmError::Barrier)); - let r = XcmExecutor::::execute_xcm(Parent, message, weight_limit); + let r = XcmExecutor::::execute_xcm(Parent, message, hash, weight_limit); assert_eq!(r, Outcome::Complete(10)); assert_eq!(SubscriptionRequests::get(), vec![(Parent.into(), Some((42, 5000)))]); @@ -49,10 +51,12 @@ fn version_subscription_instruction_should_work() { DescendOrigin(X1(AccountIndex64 { index: 1, network: None })), SubscribeVersion { query_id: 42, max_response_weight: 5000 }, ]); + let hash = fake_message_hash(&message); let weight_limit = 20; let r = XcmExecutor::::execute_xcm_in_credit( origin.clone(), - message.clone(), + message, + hash, weight_limit, weight_limit, ); @@ -62,9 +66,11 @@ fn version_subscription_instruction_should_work() { SetAppendix(Xcm(vec![])), SubscribeVersion { query_id: 42, max_response_weight: 5000 }, ]); + let hash = fake_message_hash(&message); let r = XcmExecutor::::execute_xcm_in_credit( origin, - message.clone(), + message, + hash, weight_limit, weight_limit, ); @@ -79,17 +85,19 @@ fn simple_version_unsubscriptions_should_work() { let origin = Parachain(1000); let message = Xcm::(vec![SetAppendix(Xcm(vec![])), UnsubscribeVersion]); + let hash = fake_message_hash(&message); let weight_limit = 20; - let r = XcmExecutor::::execute_xcm(origin, message, weight_limit); + let r = XcmExecutor::::execute_xcm(origin, message, hash, weight_limit); assert_eq!(r, Outcome::Error(XcmError::Barrier)); let origin = Parachain(1000); let message = Xcm::(vec![UnsubscribeVersion]); + let hash = fake_message_hash(&message); let weight_limit = 10; - let r = XcmExecutor::::execute_xcm(origin, message.clone(), weight_limit); + let r = XcmExecutor::::execute_xcm(origin, message.clone(), hash, weight_limit); assert_eq!(r, Outcome::Error(XcmError::Barrier)); - let r = XcmExecutor::::execute_xcm(Parent, message, weight_limit); + let r = XcmExecutor::::execute_xcm(Parent, message, hash, weight_limit); assert_eq!(r, Outcome::Complete(10)); assert_eq!(SubscriptionRequests::get(), vec![(Parent.into(), None)]); @@ -105,10 +113,12 @@ fn version_unsubscription_instruction_should_work() { DescendOrigin(X1(AccountIndex64 { index: 1, network: None })), UnsubscribeVersion, ]); + let hash = fake_message_hash(&message); let weight_limit = 20; let r = XcmExecutor::::execute_xcm_in_credit( origin.clone(), - message.clone(), + message, + hash, weight_limit, weight_limit, ); @@ -116,9 +126,11 @@ fn version_unsubscription_instruction_should_work() { // Fine to do it when origin is untouched. let message = Xcm::(vec![SetAppendix(Xcm(vec![])), UnsubscribeVersion]); + let hash = fake_message_hash(&message); let r = XcmExecutor::::execute_xcm_in_credit( origin, - message.clone(), + message, + hash, weight_limit, weight_limit, ); diff --git a/xcm/xcm-builder/src/tests/weight.rs b/xcm/xcm-builder/src/tests/weight.rs index f0a3aaf57a1b..c09c2420cda0 100644 --- a/xcm/xcm-builder/src/tests/weight.rs +++ b/xcm/xcm-builder/src/tests/weight.rs @@ -43,25 +43,27 @@ fn errors_should_return_unused_weight() { let limit = ::Weigher::weight(&mut message).unwrap(); assert_eq!(limit, 30); - let r = XcmExecutor::::execute_xcm(Here, message.clone(), limit); + let hash = fake_message_hash(&message); + + let r = XcmExecutor::::execute_xcm(Here, message.clone(), hash, limit); assert_eq!(r, Outcome::Complete(30)); assert_eq!(asset_list(AccountIndex64 { index: 3, network: None }), vec![(Here, 7u128).into()]); assert_eq!(asset_list(Here), vec![(Here, 4u128).into()]); assert_eq!(sent_xcm(), vec![]); - let r = XcmExecutor::::execute_xcm(Here, message.clone(), limit); + let r = XcmExecutor::::execute_xcm(Here, message.clone(), hash, limit); assert_eq!(r, Outcome::Incomplete(30, XcmError::NotWithdrawable)); assert_eq!(asset_list(AccountIndex64 { index: 3, network: None }), vec![(Here, 10u128).into()]); assert_eq!(asset_list(Here), vec![(Here, 1u128).into()]); assert_eq!(sent_xcm(), vec![]); - let r = XcmExecutor::::execute_xcm(Here, message.clone(), limit); + let r = XcmExecutor::::execute_xcm(Here, message.clone(), hash, limit); assert_eq!(r, Outcome::Incomplete(20, XcmError::NotWithdrawable)); assert_eq!(asset_list(AccountIndex64 { index: 3, network: None }), vec![(Here, 11u128).into()]); assert_eq!(asset_list(Here), vec![]); assert_eq!(sent_xcm(), vec![]); - let r = XcmExecutor::::execute_xcm(Here, message, limit); + let r = XcmExecutor::::execute_xcm(Here, message, hash, limit); assert_eq!(r, Outcome::Incomplete(10, XcmError::NotWithdrawable)); assert_eq!(asset_list(AccountIndex64 { index: 3, network: None }), vec![(Here, 11u128).into()]); assert_eq!(asset_list(Here), vec![]); diff --git a/xcm/xcm-builder/src/universal_exports.rs b/xcm/xcm-builder/src/universal_exports.rs index d88329a4128c..1e0869801812 100644 --- a/xcm/xcm-builder/src/universal_exports.rs +++ b/xcm/xcm-builder/src/universal_exports.rs @@ -84,7 +84,7 @@ impl> SendXcm validate_export::(network, 0, destination, message) } - fn deliver(ticket: Exporter::Ticket) -> Result<(), SendError> { + fn deliver(ticket: Exporter::Ticket) -> Result { Exporter::deliver(ticket) } } @@ -190,7 +190,7 @@ impl Ok((v, cost)) } - fn deliver(validation: Router::Ticket) -> Result<(), SendError> { + fn deliver(validation: Router::Ticket) -> Result { Router::deliver(validation) } } @@ -264,7 +264,7 @@ impl Ok((v, cost)) } - fn deliver(ticket: Router::Ticket) -> Result<(), SendError> { + fn deliver(ticket: Router::Ticket) -> Result { Router::deliver(ticket) } } @@ -331,14 +331,14 @@ pub struct HaulBlobExporter( impl, Price: Get> ExportXcm for HaulBlobExporter { - type Ticket = Vec; + type Ticket = (Vec, XcmHash); fn validate( network: NetworkId, _channel: u32, destination: &mut Option, message: &mut Option>, - ) -> Result<(Vec, MultiAssets), SendError> { + ) -> Result<((Vec, XcmHash), MultiAssets), SendError> { let bridged_network = BridgedNetwork::get(); ensure!(&network == &bridged_network, SendError::NotApplicable); // We don't/can't use the `channel` for this adapter. @@ -351,13 +351,14 @@ impl, Price: Get> }, }; let message = VersionedXcm::from(message.take().ok_or(SendError::MissingArgument)?); + let hash = message.using_encoded(sp_io::hashing::blake2_256); let blob = BridgeMessage { universal_dest, message }.encode(); - Ok((blob, Price::get())) + Ok(((blob, hash), Price::get())) } - fn deliver(blob: Vec) -> Result<(), SendError> { + fn deliver((blob, hash): (Vec, XcmHash)) -> Result { Bridge::haul_blob(blob); - Ok(()) + Ok(hash) } } diff --git a/xcm/xcm-builder/tests/mock/mod.rs b/xcm/xcm-builder/tests/mock/mod.rs index bc56e7dad0d3..3b782d99de66 100644 --- a/xcm/xcm-builder/tests/mock/mod.rs +++ b/xcm/xcm-builder/tests/mock/mod.rs @@ -19,6 +19,7 @@ use frame_support::{ traits::{Everything, Nothing}, weights::Weight, }; +use parity_scale_codec::Encode; use sp_core::H256; use sp_runtime::{testing::Header, traits::IdentityLookup, AccountId32}; use sp_std::cell::RefCell; @@ -40,24 +41,27 @@ pub type AccountId = AccountId32; pub type Balance = u128; thread_local! { - pub static SENT_XCM: RefCell> = RefCell::new(Vec::new()); + pub static SENT_XCM: RefCell> = RefCell::new(Vec::new()); } -pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm)> { +pub fn sent_xcm() -> Vec<(MultiLocation, opaque::Xcm, XcmHash)> { SENT_XCM.with(|q| (*q.borrow()).clone()) } pub struct TestSendXcm; impl SendXcm for TestSendXcm { - type Ticket = (MultiLocation, Xcm<()>); + type Ticket = (MultiLocation, Xcm<()>, XcmHash); fn validate( dest: &mut Option, msg: &mut Option>, - ) -> SendResult<(MultiLocation, Xcm<()>)> { - let pair = (dest.take().unwrap(), msg.take().unwrap()); - Ok((pair, MultiAssets::new())) + ) -> SendResult<(MultiLocation, Xcm<()>, XcmHash)> { + let msg = msg.take().unwrap(); + let hash = fake_message_hash(&msg); + let triplet = (dest.take().unwrap(), msg, hash); + Ok((triplet, MultiAssets::new())) } - fn deliver(pair: (MultiLocation, Xcm<()>)) -> Result<(), SendError> { - SENT_XCM.with(|q| q.borrow_mut().push(pair)); - Ok(()) + fn deliver(triplet: (MultiLocation, Xcm<()>, XcmHash)) -> Result { + let hash = triplet.2; + SENT_XCM.with(|q| q.borrow_mut().push(triplet)); + Ok(hash) } } @@ -245,3 +249,7 @@ pub fn kusama_like_with_balances(balances: Vec<(AccountId, Balance)>) -> sp_io:: ext.execute_with(|| System::set_block_number(1)); ext } + +pub fn fake_message_hash(message: &Xcm) -> XcmHash { + message.using_encoded(sp_io::hashing::blake2_256) +} diff --git a/xcm/xcm-builder/tests/scenarios.rs b/xcm/xcm-builder/tests/scenarios.rs index 6932c006546a..f2c708ab849b 100644 --- a/xcm/xcm-builder/tests/scenarios.rs +++ b/xcm/xcm-builder/tests/scenarios.rs @@ -17,7 +17,8 @@ mod mock; use mock::{ - kusama_like_with_balances, AccountId, Balance, Balances, BaseXcmWeight, XcmConfig, CENTS, + fake_message_hash, kusama_like_with_balances, AccountId, Balance, Balances, BaseXcmWeight, + XcmConfig, CENTS, }; use polkadot_parachain::primitives::Id as ParaId; use sp_runtime::traits::AccountIdConversion; @@ -46,18 +47,16 @@ fn withdraw_and_deposit_works() { let other_para_id = 3000; let amount = REGISTER_AMOUNT; let weight = 3 * BaseXcmWeight::get(); - let r = XcmExecutor::::execute_xcm( - Parachain(PARA_ID), - Xcm(vec![ - WithdrawAsset((Here, amount).into()), - buy_execution(), - DepositAsset { - assets: AllCounted(1).into(), - beneficiary: Parachain(other_para_id).into(), - }, - ]), - weight, - ); + let message = Xcm(vec![ + WithdrawAsset((Here, amount).into()), + buy_execution(), + DepositAsset { + assets: AllCounted(1).into(), + beneficiary: Parachain(other_para_id).into(), + }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(PARA_ID), message, hash, weight); assert_eq!(r, Outcome::Complete(weight)); let other_para_acc: AccountId = ParaId::from(other_para_id).into_account(); assert_eq!(Balances::free_balance(para_acc), INITIAL_BALANCE - amount); @@ -87,20 +86,18 @@ fn report_holding_works() { query_id: 1234, max_weight: 1_000_000_000, }; - let r = XcmExecutor::::execute_xcm( - Parachain(PARA_ID), - Xcm(vec![ - WithdrawAsset((Here, amount).into()), - buy_execution(), - DepositAsset { - assets: AllCounted(1).into(), - beneficiary: OnlyChild.into(), // invalid destination - }, - // is not triggered becasue the deposit fails - ReportHolding { response_info: response_info.clone(), assets: All.into() }, - ]), - weight, - ); + let message = Xcm(vec![ + WithdrawAsset((Here, amount).into()), + buy_execution(), + DepositAsset { + assets: AllCounted(1).into(), + beneficiary: OnlyChild.into(), // invalid destination + }, + // is not triggered becasue the deposit fails + ReportHolding { response_info: response_info.clone(), assets: All.into() }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(PARA_ID), message, hash, weight); assert_eq!( r, Outcome::Incomplete( @@ -113,38 +110,32 @@ fn report_holding_works() { assert_eq!(Balances::free_balance(para_acc.clone()), INITIAL_BALANCE - amount); // now do a successful transfer - let r = XcmExecutor::::execute_xcm( - Parachain(PARA_ID), - Xcm(vec![ - WithdrawAsset((Here, amount).into()), - buy_execution(), - DepositAsset { - assets: AllCounted(1).into(), - beneficiary: Parachain(other_para_id).into(), - }, - // used to get a notification in case of success - ReportHolding { - response_info: response_info.clone(), - assets: AllCounted(1).into(), - }, - ]), - weight, - ); + let message = Xcm(vec![ + WithdrawAsset((Here, amount).into()), + buy_execution(), + DepositAsset { + assets: AllCounted(1).into(), + beneficiary: Parachain(other_para_id).into(), + }, + // used to get a notification in case of success + ReportHolding { response_info: response_info.clone(), assets: AllCounted(1).into() }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(PARA_ID), message, hash, weight); assert_eq!(r, Outcome::Complete(weight)); let other_para_acc: AccountId = ParaId::from(other_para_id).into_account(); assert_eq!(Balances::free_balance(other_para_acc), amount); assert_eq!(Balances::free_balance(para_acc), INITIAL_BALANCE - 2 * amount); + let expected_msg = Xcm(vec![QueryResponse { + query_id: response_info.query_id, + response: Response::Assets(vec![].into()), + max_weight: response_info.max_weight, + querier: Some(Here.into()), + }]); + let expected_hash = fake_message_hash(&expected_msg); assert_eq!( mock::sent_xcm(), - vec![( - Parachain(PARA_ID).into(), - Xcm(vec![QueryResponse { - query_id: response_info.query_id, - response: Response::Assets(vec![].into()), - max_weight: response_info.max_weight, - querier: Some(Here.into()), - }]), - )] + vec![(Parachain(PARA_ID).into(), expected_msg, expected_hash,)] ); }); } @@ -176,65 +167,53 @@ fn teleport_to_statemine_works() { let weight = 3 * BaseXcmWeight::get(); // teleports are allowed to community chains, even in the absence of trust from their side. - let r = XcmExecutor::::execute_xcm( - Parachain(PARA_ID), - Xcm(vec![ - WithdrawAsset((Here, amount).into()), - buy_execution(), - InitiateTeleport { - assets: All.into(), - dest: Parachain(other_para_id).into(), - xcm: Xcm(teleport_effects.clone()), - }, - ]), - weight, - ); + let message = Xcm(vec![ + WithdrawAsset((Here, amount).into()), + buy_execution(), + InitiateTeleport { + assets: All.into(), + dest: Parachain(other_para_id).into(), + xcm: Xcm(teleport_effects.clone()), + }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(PARA_ID), message, hash, weight); assert_eq!(r, Outcome::Complete(weight)); + let expected_msg = Xcm(vec![ReceiveTeleportedAsset((Parent, amount).into()), ClearOrigin] + .into_iter() + .chain(teleport_effects.clone().into_iter()) + .collect()); + let expected_hash = fake_message_hash(&expected_msg); assert_eq!( mock::sent_xcm(), - vec![( - Parachain(other_para_id).into(), - Xcm(vec![ReceiveTeleportedAsset((Parent, amount).into()), ClearOrigin,] - .into_iter() - .chain(teleport_effects.clone().into_iter()) - .collect()) - )] + vec![(Parachain(other_para_id).into(), expected_msg, expected_hash,)] ); // teleports are allowed from statemine to kusama. - let r = XcmExecutor::::execute_xcm( - Parachain(PARA_ID), - Xcm(vec![ - WithdrawAsset((Here, amount).into()), - buy_execution(), - InitiateTeleport { - assets: All.into(), - dest: Parachain(statemine_id).into(), - xcm: Xcm(teleport_effects.clone()), - }, - ]), - weight, - ); + let message = Xcm(vec![ + WithdrawAsset((Here, amount).into()), + buy_execution(), + InitiateTeleport { + assets: All.into(), + dest: Parachain(statemine_id).into(), + xcm: Xcm(teleport_effects.clone()), + }, + ]); + let hash = fake_message_hash(&message); + let r = XcmExecutor::::execute_xcm(Parachain(PARA_ID), message, hash, weight); assert_eq!(r, Outcome::Complete(weight)); // 2 * amount because of the other teleport above assert_eq!(Balances::free_balance(para_acc), INITIAL_BALANCE - 2 * amount); + let expected_msg = Xcm(vec![ReceiveTeleportedAsset((Parent, amount).into()), ClearOrigin] + .into_iter() + .chain(teleport_effects.clone().into_iter()) + .collect()); + let expected_hash = fake_message_hash(&expected_msg); assert_eq!( mock::sent_xcm(), vec![ - ( - Parachain(other_para_id).into(), - Xcm(vec![ReceiveTeleportedAsset((Parent, amount).into()), ClearOrigin,] - .into_iter() - .chain(teleport_effects.clone().into_iter()) - .collect()), - ), - ( - Parachain(statemine_id).into(), - Xcm(vec![ReceiveTeleportedAsset((Parent, amount).into()), ClearOrigin,] - .into_iter() - .chain(teleport_effects.clone().into_iter()) - .collect()), - ) + (Parachain(other_para_id).into(), expected_msg.clone(), expected_hash,), + (Parachain(statemine_id).into(), expected_msg, expected_hash,) ] ); }); @@ -261,31 +240,28 @@ fn reserve_based_transfer_works() { beneficiary: (Parent, Parachain(PARA_ID)).into(), }, ]; + let message = Xcm(vec![ + WithdrawAsset((Here, amount).into()), + buy_execution(), + DepositReserveAsset { + assets: AllCounted(1).into(), + dest: Parachain(other_para_id).into(), + xcm: Xcm(transfer_effects.clone()), + }, + ]); + let hash = fake_message_hash(&message); let weight = 3 * BaseXcmWeight::get(); - let r = XcmExecutor::::execute_xcm( - Parachain(PARA_ID), - Xcm(vec![ - WithdrawAsset((Here, amount).into()), - buy_execution(), - DepositReserveAsset { - assets: AllCounted(1).into(), - dest: Parachain(other_para_id).into(), - xcm: Xcm(transfer_effects.clone()), - }, - ]), - weight, - ); + let r = XcmExecutor::::execute_xcm(Parachain(PARA_ID), message, hash, weight); assert_eq!(r, Outcome::Complete(weight)); assert_eq!(Balances::free_balance(para_acc), INITIAL_BALANCE - amount); + let expected_msg = Xcm(vec![ReserveAssetDeposited((Parent, amount).into()), ClearOrigin] + .into_iter() + .chain(transfer_effects.into_iter()) + .collect()); + let expected_hash = fake_message_hash(&expected_msg); assert_eq!( mock::sent_xcm(), - vec![( - Parachain(other_para_id).into(), - Xcm(vec![ReserveAssetDeposited((Parent, amount).into()), ClearOrigin,] - .into_iter() - .chain(transfer_effects.into_iter()) - .collect()) - )] + vec![(Parachain(other_para_id).into(), expected_msg, expected_hash,)] ); }); } diff --git a/xcm/xcm-executor/src/lib.rs b/xcm/xcm-executor/src/lib.rs index ec7f583128e9..9b765041e251 100644 --- a/xcm/xcm-executor/src/lib.rs +++ b/xcm/xcm-executor/src/lib.rs @@ -49,7 +49,7 @@ pub struct FeesMode { pub struct XcmExecutor { holding: Assets, holding_limit: usize, - origin: Option, + context: XcmContext, original_origin: MultiLocation, trader: Config::Trader, /// The most recent error result and instruction index into the fragment in which it occurred, @@ -67,6 +67,7 @@ pub struct XcmExecutor { appendix_weight: u64, transact_status: MaybeErrorCode, fees_mode: FeesMode, + topic: Option<[u8; 32]>, _config: PhantomData, } @@ -85,10 +86,10 @@ impl XcmExecutor { self.holding_limit = v } pub fn origin(&self) -> &Option { - &self.origin + &self.context.origin } pub fn set_origin(&mut self, v: Option) { - self.origin = v + self.context.origin = v } pub fn original_origin(&self) -> &MultiLocation { &self.original_origin @@ -156,6 +157,12 @@ impl XcmExecutor { pub fn set_fees_mode(&mut self, v: FeesMode) { self.fees_mode = v } + pub fn topic(&self) -> &Option<[u8; 32]> { + &self.topic + } + pub fn set_topic(&mut self, v: Option<[u8; 32]>) { + self.topic = v; + } } pub struct WeighedMessage(Weight, Xcm); @@ -176,6 +183,7 @@ impl ExecuteXcm for XcmExecutor { fn execute( origin: impl Into, WeighedMessage(xcm_weight, mut message): WeighedMessage, + message_hash: XcmHash, mut weight_credit: Weight, ) -> Outcome { let origin = origin.into(); @@ -203,10 +211,10 @@ impl ExecuteXcm for XcmExecutor { return Outcome::Error(XcmError::Barrier) } - let mut vm = Self::new(origin); + let mut vm = Self::new(origin, message_hash); while !message.0.is_empty() { - let result = vm.execute(message); + let result = vm.process(message); log::trace!(target: "xcm::execute_xcm_in_credit", "result: {:?}", result); message = if let Err(error) = result { vm.total_surplus.saturating_accrue(error.weight); @@ -218,14 +226,14 @@ impl ExecuteXcm for XcmExecutor { } } - vm.post_execute(xcm_weight) + vm.post_process(xcm_weight) } fn charge_fees(origin: impl Into, fees: MultiAssets) -> XcmResult { let origin = origin.into(); if !Config::FeeManager::is_waived(Some(&origin), FeeReason::ChargeFees) { for asset in fees.inner() { - Config::AssetTransactor::withdraw_asset(&asset, &origin)?; + Config::AssetTransactor::withdraw_asset(&asset, &origin, None)?; } Config::FeeManager::handle_fee(fees); } @@ -254,12 +262,12 @@ impl From for frame_benchmarking::BenchmarkError { } impl XcmExecutor { - pub fn new(origin: impl Into) -> Self { + pub fn new(origin: impl Into, message_hash: XcmHash) -> Self { let origin = origin.into(); Self { holding: Assets::new(), holding_limit: Config::MaxAssetsIntoHolding::get() as usize, - origin: Some(origin.clone()), + context: XcmContext { origin: Some(origin.clone()), message_hash, topic: None }, original_origin: origin, trader: Config::Trader::new(), error: None, @@ -271,17 +279,21 @@ impl XcmExecutor { appendix_weight: 0, transact_status: Default::default(), fees_mode: FeesMode { jit_withdraw: false }, + topic: None, _config: PhantomData, } } - /// Execute the XCM program fragment and report back the error and which instruction caused it, - /// or `Ok` if there was no error. - pub fn execute(&mut self, xcm: Xcm) -> Result<(), ExecutorError> { + #[cfg(feature = "runtime-benchmarks")] + pub fn bench_process(&mut self, xcm: Xcm) -> Result<(), ExecutorError> { + self.process(xcm) + } + + fn process(&mut self, xcm: Xcm) -> Result<(), ExecutorError> { log::trace!( - target: "xcm::execute", + target: "xcm::process", "origin: {:?}, total_surplus/refunded: {:?}/{:?}, error_handler_weight: {:?}", - self.origin, + self.origin_ref(), self.total_surplus, self.total_refunded, self.error_handler_weight, @@ -305,7 +317,7 @@ impl XcmExecutor { /// Execute any final operations after having executed the XCM message. /// This includes refunding surplus weight, trapping extra holding funds, and returning any errors during execution. - pub fn post_execute(mut self, xcm_weight: Weight) -> Outcome { + pub fn post_process(mut self, xcm_weight: Weight) -> Outcome { // We silently drop any error from our attempt to refund the surplus as it's a charitable // thing so best-effort is all we will do. let _ = self.refund_surplus(); @@ -314,8 +326,13 @@ impl XcmExecutor { let mut weight_used = xcm_weight.saturating_sub(self.total_surplus); if !self.holding.is_empty() { - log::trace!(target: "xcm::execute_xcm_in_credit", "Trapping assets in holding register: {:?} (original_origin: {:?})", self.holding, self.original_origin); - let trap_weight = Config::AssetTrap::drop_assets(&self.original_origin, self.holding); + log::trace!( + target: "xcm::execute_xcm_in_credit", + "Trapping assets in holding register: {:?}, context: {:?} (original_origin: {:?})", + self.holding, self.context, self.original_origin, + ); + let trap_weight = + Config::AssetTrap::drop_assets(&self.original_origin, self.holding, &self.context); weight_used.saturating_accrue(trap_weight); }; @@ -330,20 +347,27 @@ impl XcmExecutor { } } + fn origin_ref(&self) -> Option<&MultiLocation> { + self.context.origin.as_ref() + } + + fn cloned_origin(&self) -> Option { + self.context.origin.clone() + } + /// Send an XCM, charging fees from Holding as needed. fn send( &mut self, dest: MultiLocation, msg: Xcm<()>, reason: FeeReason, - ) -> Result<(), XcmError> { + ) -> Result { let (ticket, fee) = validate_send::(dest, msg)?; - if !Config::FeeManager::is_waived(self.origin.as_ref(), reason) { + if !Config::FeeManager::is_waived(self.origin_ref(), reason) { let paid = self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?; Config::FeeManager::handle_fee(paid.into()); } - Config::XcmSender::deliver(ticket)?; - Ok(()) + Config::XcmSender::deliver(ticket).map_err(Into::into) } /// Remove the registered error handler and return it. Do not refund its weight. @@ -409,16 +433,16 @@ impl XcmExecutor { match instr { WithdrawAsset(assets) => { // Take `assets` from the origin account (on-chain) and place in holding. - let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone(); + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?.clone(); for asset in assets.into_inner().into_iter() { - Config::AssetTransactor::withdraw_asset(&asset, &origin)?; + Config::AssetTransactor::withdraw_asset(&asset, &origin, Some(&self.context))?; self.subsume_asset(asset)?; } Ok(()) }, ReserveAssetDeposited(assets) => { // check whether we trust origin to be our reserve location for this asset. - let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone(); + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?.clone(); for asset in assets.into_inner().into_iter() { // Must ensure that we recognise the asset as being managed by the origin. ensure!( @@ -431,27 +455,34 @@ impl XcmExecutor { }, TransferAsset { assets, beneficiary } => { // Take `assets` from the origin account (on-chain) and place into dest account. - let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?; + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; for asset in assets.inner() { - Config::AssetTransactor::beam_asset(&asset, origin, &beneficiary)?; + Config::AssetTransactor::beam_asset( + &asset, + origin, + &beneficiary, + &self.context, + )?; } Ok(()) }, TransferReserveAsset { mut assets, dest, xcm } => { - let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?; + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; // Take `assets` from the origin account (on-chain) and place into dest account. for asset in assets.inner() { - Config::AssetTransactor::beam_asset(asset, origin, &dest)?; + Config::AssetTransactor::beam_asset(asset, origin, &dest, &self.context)?; } - let context = Config::LocationInverter::universal_location().into(); - assets.reanchor(&dest, &context).map_err(|()| XcmError::MultiLocationFull)?; + let reanchor_context = Config::LocationInverter::universal_location().into(); + assets + .reanchor(&dest, &reanchor_context) + .map_err(|()| XcmError::MultiLocationFull)?; let mut message = vec![ReserveAssetDeposited(assets), ClearOrigin]; message.extend(xcm.0.into_iter()); self.send(dest, Xcm(message), FeeReason::TransferReserveAsset)?; Ok(()) }, ReceiveTeleportedAsset(assets) => { - let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone(); + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?.clone(); // check whether we trust origin to teleport this asset to us via config trait. for asset in assets.inner() { // We only trust the origin to send us assets that they identify as their @@ -463,17 +494,17 @@ impl XcmExecutor { // We should check that the asset can actually be teleported in (for this to be in error, there // would need to be an accounting violation by one of the trusted chains, so it's unlikely, but we // don't want to punish a possibly innocent chain/user). - Config::AssetTransactor::can_check_in(&origin, asset)?; + Config::AssetTransactor::can_check_in(&origin, asset, &self.context)?; } for asset in assets.into_inner().into_iter() { - Config::AssetTransactor::check_in(&origin, &asset); + Config::AssetTransactor::check_in(&origin, &asset, &self.context); self.subsume_asset(asset)?; } Ok(()) }, Transact { origin_kind, require_weight_at_most, mut call } => { // We assume that the Relay-chain is allowed to use transact on this parachain. - let origin = self.origin.clone().ok_or(XcmError::BadOrigin)?; + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?.clone(); // TODO: #2841 #TRANSACTFILTER allow the trait to issue filters for the relay-chain let message_call = call.take_decoded().map_err(|_| XcmError::FailedToDecode)?; @@ -505,47 +536,50 @@ impl XcmExecutor { Ok(()) }, QueryResponse { query_id, response, max_weight, querier } => { - let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?; + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; Config::ResponseHandler::on_response( origin, query_id, querier.as_ref(), response, max_weight, + &self.context, ); Ok(()) }, DescendOrigin(who) => self + .context .origin .as_mut() .ok_or(XcmError::BadOrigin)? .append_with(who) .map_err(|_| XcmError::MultiLocationFull), ClearOrigin => { - self.origin = None; + self.context.origin = None; Ok(()) }, ReportError(response_info) => { // Report the given result by sending a QueryResponse XCM to a previously given outcome // destination if one was registered. self.respond( - self.origin.clone(), + self.cloned_origin(), Response::ExecutionResult(self.error), response_info, FeeReason::Report, - ) + )?; + Ok(()) }, DepositAsset { assets, beneficiary } => { let deposited = self.holding.saturating_take(assets); for asset in deposited.into_assets_iter() { - Config::AssetTransactor::deposit_asset(&asset, &beneficiary)?; + Config::AssetTransactor::deposit_asset(&asset, &beneficiary, &self.context)?; } Ok(()) }, DepositReserveAsset { assets, dest, xcm } => { let deposited = self.holding.saturating_take(assets); for asset in deposited.assets_iter() { - Config::AssetTransactor::deposit_asset(&asset, &dest)?; + Config::AssetTransactor::deposit_asset(&asset, &dest, &self.context)?; } // Note that we pass `None` as `maybe_failed_bin` and drop any assets which cannot // be reanchored because we have already called `deposit_asset` on all assets. @@ -572,7 +606,7 @@ impl XcmExecutor { // We must do this first in order to resolve wildcards. let assets = self.holding.saturating_take(assets); for asset in assets.assets_iter() { - Config::AssetTransactor::check_out(&dest, &asset); + Config::AssetTransactor::check_out(&dest, &asset, &self.context); } // Note that we pass `None` as `maybe_failed_bin` and drop any assets which cannot // be reanchored because we have already checked all assets out. @@ -588,11 +622,12 @@ impl XcmExecutor { let assets = Self::reanchored(self.holding.min(&assets), &response_info.destination, None); self.respond( - self.origin.clone(), + self.cloned_origin(), Response::Assets(assets), response_info, FeeReason::Report, - ) + )?; + Ok(()) }, BuyExecution { fees, weight_limit } => { // There is no need to buy any weight is `weight_limit` is `Unlimited` since it @@ -630,8 +665,8 @@ impl XcmExecutor { Ok(()) }, ClaimAsset { assets, ticket } => { - let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?; - let ok = Config::AssetClaims::claim_assets(origin, &ticket, &assets); + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; + let ok = Config::AssetClaims::claim_assets(origin, &ticket, &assets, &self.context); ensure!(ok, XcmError::UnknownClaim); for asset in assets.into_inner().into_iter() { self.subsume_asset(asset)?; @@ -640,16 +675,21 @@ impl XcmExecutor { }, Trap(code) => Err(XcmError::Trap(code)), SubscribeVersion { query_id, max_response_weight } => { - let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone(); + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; // We don't allow derivative origins to subscribe since it would otherwise pose a // DoS risk. - ensure!(self.original_origin == origin, XcmError::BadOrigin); - Config::SubscriptionService::start(&origin, query_id, max_response_weight) + ensure!(&self.original_origin == origin, XcmError::BadOrigin); + Config::SubscriptionService::start( + origin, + query_id, + max_response_weight, + &self.context, + ) }, UnsubscribeVersion => { - let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?; + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; ensure!(&self.original_origin == origin, XcmError::BadOrigin); - Config::SubscriptionService::stop(origin) + Config::SubscriptionService::stop(origin, &self.context) }, BurnAsset(assets) => { self.holding.saturating_take(assets.into()); @@ -658,7 +698,7 @@ impl XcmExecutor { ExpectAsset(assets) => self.holding.ensure_contains(&assets).map_err(|_| XcmError::ExpectationFalse), ExpectOrigin(origin) => { - ensure!(self.origin == origin, XcmError::ExpectationFalse); + ensure!(self.context.origin == origin, XcmError::ExpectationFalse); Ok(()) }, ExpectError(error) => { @@ -682,7 +722,7 @@ impl XcmExecutor { .collect::, XcmError>>()?; let QueryResponseInfo { destination, query_id, max_weight } = response_info; let response = Response::PalletsInfo(pallets.try_into()?); - let querier = Self::to_querier(self.origin.clone(), &destination)?; + let querier = Self::to_querier(self.cloned_origin(), &destination)?; let instruction = QueryResponse { query_id, response, max_weight, querier }; let message = Xcm(vec![instruction]); self.send(destination, message, FeeReason::QueryPallet)?; @@ -701,12 +741,15 @@ impl XcmExecutor { ensure!(minor >= min_crate_minor, XcmError::VersionIncompatible); Ok(()) }, - ReportTransactStatus(response_info) => self.respond( - self.origin.clone(), - Response::DispatchResult(self.transact_status.clone()), - response_info, - FeeReason::Report, - ), + ReportTransactStatus(response_info) => { + self.respond( + self.cloned_origin(), + Response::DispatchResult(self.transact_status.clone()), + response_info, + FeeReason::Report, + )?; + Ok(()) + }, ClearTransactStatus => { self.transact_status = Default::default(); Ok(()) @@ -714,17 +757,17 @@ impl XcmExecutor { UniversalOrigin(new_global) => { let universal_location = Config::LocationInverter::universal_location(); ensure!(universal_location.first() != Some(&new_global), XcmError::InvalidLocation); - let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone(); + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?.clone(); let origin_xform = (origin, new_global); let ok = Config::UniversalAliases::contains(&origin_xform); ensure!(ok, XcmError::InvalidLocation); let (_, new_global) = origin_xform; let new_origin = X1(new_global).relative_to(&universal_location); - self.origin = Some(new_origin); + self.context.origin = Some(new_origin); Ok(()) }, ExportMessage { network, destination, xcm } => { - let hash = (&self.origin, &destination).using_encoded(blake2_128); + let hash = (self.origin_ref(), &destination).using_encoded(blake2_128); let channel = u32::decode(&mut hash.as_ref()).unwrap_or(0); // Hash identifies the lane on the exporter which we use. We use the pairwise // combination of the origin and destination to ensure origin/destination pairs will @@ -736,7 +779,7 @@ impl XcmExecutor { Ok(()) }, LockAsset { asset, unlocker } => { - let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone(); + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?.clone(); let (remote_asset, context) = Self::try_reanchor(asset.clone(), &unlocker)?; let lock_ticket = Config::AssetLocker::prepare_lock(unlocker.clone(), asset, origin.clone())?; @@ -750,17 +793,17 @@ impl XcmExecutor { Ok(()) }, UnlockAsset { asset, target } => { - let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone(); + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?.clone(); Config::AssetLocker::prepare_unlock(origin.clone(), asset, target)?.enact()?; Ok(()) }, NoteUnlockable { asset, owner } => { - let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone(); + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?.clone(); Config::AssetLocker::note_unlockable(origin, asset, owner)?; Ok(()) }, RequestUnlock { asset, locker } => { - let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?.clone(); + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?.clone(); let remote_asset = Self::try_reanchor(asset.clone(), &locker)?.0; let reduce_ticket = Config::AssetLocker::prepare_reduce_unlockable( locker.clone(), @@ -776,9 +819,9 @@ impl XcmExecutor { Ok(()) }, ExchangeAsset { give, want, maximal } => { - let origin = self.origin.as_ref(); let give = self.holding.saturating_take(give); - let r = Config::AssetExchanger::exchange_asset(origin, give, &want, maximal); + let r = + Config::AssetExchanger::exchange_asset(self.origin_ref(), give, &want, maximal); let completed = r.is_ok(); let received = r.unwrap_or_else(|a| a); for asset in received.into_assets_iter() { @@ -794,6 +837,14 @@ impl XcmExecutor { self.fees_mode = FeesMode { jit_withdraw }; Ok(()) }, + SetTopic(topic) => { + self.topic = Some(topic); + Ok(()) + }, + ClearTopic => { + self.topic = None; + Ok(()) + }, HrmpNewChannelOpenRequest { .. } => Err(XcmError::Unimplemented), HrmpChannelAccepted { .. } => Err(XcmError::Unimplemented), HrmpChannelClosing { .. } => Err(XcmError::Unimplemented), @@ -801,13 +852,13 @@ impl XcmExecutor { } fn take_fee(&mut self, fee: MultiAssets, reason: FeeReason) -> XcmResult { - if Config::FeeManager::is_waived(self.origin.as_ref(), reason) { + if Config::FeeManager::is_waived(self.origin_ref(), reason) { return Ok(()) } let paid = if self.fees_mode.jit_withdraw { - let origin = self.origin.as_ref().ok_or(XcmError::BadOrigin)?; + let origin = self.origin_ref().ok_or(XcmError::BadOrigin)?; for asset in fee.inner() { - Config::AssetTransactor::withdraw_asset(&asset, origin)?; + Config::AssetTransactor::withdraw_asset(&asset, origin, Some(&self.context))?; } fee } else { @@ -840,29 +891,28 @@ impl XcmExecutor { response: Response, info: QueryResponseInfo, fee_reason: FeeReason, - ) -> Result<(), XcmError> { + ) -> Result { let querier = Self::to_querier(local_querier, &info.destination)?; let QueryResponseInfo { destination, query_id, max_weight } = info; let instruction = QueryResponse { query_id, response, max_weight, querier }; let message = Xcm(vec![instruction]); let (ticket, fee) = validate_send::(destination, message)?; - if !Config::FeeManager::is_waived(self.origin.as_ref(), fee_reason) { + if !Config::FeeManager::is_waived(self.origin_ref(), fee_reason) { let paid = self.holding.try_take(fee.into()).map_err(|_| XcmError::NotHoldingFees)?; Config::FeeManager::handle_fee(paid.into()); } - Config::XcmSender::deliver(ticket)?; - Ok(()) + Config::XcmSender::deliver(ticket).map_err(Into::into) } fn try_reanchor( asset: MultiAsset, destination: &MultiLocation, ) -> Result<(MultiAsset, MultiLocation), XcmError> { - let context = Config::LocationInverter::universal_location().into(); + let reanchor_context = Config::LocationInverter::universal_location().into(); let asset = asset - .reanchored(&destination, &context) + .reanchored(&destination, &reanchor_context) .map_err(|()| XcmError::ReanchorFailed)?; - Ok((asset, context)) + Ok((asset, reanchor_context)) } /// NOTE: Any assets which were unable to be reanchored are introduced into `failed_bin`. @@ -871,8 +921,8 @@ impl XcmExecutor { dest: &MultiLocation, maybe_failed_bin: Option<&mut Assets>, ) -> MultiAssets { - let context = Config::LocationInverter::universal_location().into(); - assets.reanchor(dest, &context, maybe_failed_bin); + let reanchor_context = Config::LocationInverter::universal_location().into(); + assets.reanchor(dest, &reanchor_context, maybe_failed_bin); assets.into_assets_iter().collect::>().into() } } diff --git a/xcm/xcm-executor/src/traits/drop_assets.rs b/xcm/xcm-executor/src/traits/drop_assets.rs index 5f82c5feb74b..45eefc9fba4e 100644 --- a/xcm/xcm-executor/src/traits/drop_assets.rs +++ b/xcm/xcm-executor/src/traits/drop_assets.rs @@ -17,15 +17,15 @@ use crate::Assets; use core::marker::PhantomData; use frame_support::{traits::Contains, weights::Weight}; -use xcm::latest::{MultiAssets, MultiLocation}; +use xcm::latest::{MultiAssets, MultiLocation, XcmContext}; /// Define a handler for when some non-empty `Assets` value should be dropped. pub trait DropAssets { /// Handler for receiving dropped assets. Returns the weight consumed by this operation. - fn drop_assets(origin: &MultiLocation, assets: Assets) -> Weight; + fn drop_assets(origin: &MultiLocation, assets: Assets, context: &XcmContext) -> Weight; } impl DropAssets for () { - fn drop_assets(_origin: &MultiLocation, _assets: Assets) -> Weight { + fn drop_assets(_origin: &MultiLocation, _assets: Assets, _context: &XcmContext) -> Weight { 0 } } @@ -35,9 +35,9 @@ impl DropAssets for () { pub struct FilterAssets(PhantomData<(D, A)>); impl> DropAssets for FilterAssets { - fn drop_assets(origin: &MultiLocation, assets: Assets) -> Weight { + fn drop_assets(origin: &MultiLocation, assets: Assets, context: &XcmContext) -> Weight { if A::contains(&assets) { - D::drop_assets(origin, assets) + D::drop_assets(origin, assets, context) } else { 0 } @@ -50,9 +50,9 @@ impl> DropAssets for FilterAssets { pub struct FilterOrigin(PhantomData<(D, O)>); impl> DropAssets for FilterOrigin { - fn drop_assets(origin: &MultiLocation, assets: Assets) -> Weight { + fn drop_assets(origin: &MultiLocation, assets: Assets, context: &XcmContext) -> Weight { if O::contains(origin) { - D::drop_assets(origin, assets) + D::drop_assets(origin, assets, context) } else { 0 } @@ -63,14 +63,24 @@ impl> DropAssets for FilterOrigin bool; + fn claim_assets( + origin: &MultiLocation, + ticket: &MultiLocation, + what: &MultiAssets, + context: &XcmContext, + ) -> bool; } #[impl_trait_for_tuples::impl_for_tuples(30)] impl ClaimAssets for Tuple { - fn claim_assets(origin: &MultiLocation, ticket: &MultiLocation, what: &MultiAssets) -> bool { + fn claim_assets( + origin: &MultiLocation, + ticket: &MultiLocation, + what: &MultiAssets, + context: &XcmContext, + ) -> bool { for_tuples!( #( - if Tuple::claim_assets(origin, ticket, what) { + if Tuple::claim_assets(origin, ticket, what, context) { return true; } )* ); diff --git a/xcm/xcm-executor/src/traits/export.rs b/xcm/xcm-executor/src/traits/export.rs index 65823b8b3c14..4b4169ddc16d 100644 --- a/xcm/xcm-executor/src/traits/export.rs +++ b/xcm/xcm-executor/src/traits/export.rs @@ -41,7 +41,7 @@ pub trait ExportXcm { /// The implementation should do everything possible to ensure that this function is infallible /// if called immediately after `validate`. Returning an error here would result in a price /// paid without the service being delivered. - fn deliver(ticket: Self::Ticket) -> Result<(), SendError>; + fn deliver(ticket: Self::Ticket) -> Result; } #[impl_trait_for_tuples::impl_for_tuples(30)] @@ -76,7 +76,7 @@ impl ExportXcm for Tuple { } } - fn deliver(one_ticket: Self::Ticket) -> Result<(), SendError> { + fn deliver(one_ticket: Self::Ticket) -> Result { for_tuples!( #( if let Some(validated) = one_ticket.Tuple.take() { return Tuple::deliver(validated); @@ -110,8 +110,8 @@ pub fn export_xcm( channel: u32, dest: InteriorMultiLocation, msg: Xcm<()>, -) -> Result { - let (ticket, price) = T::validate(network, channel, &mut Some(dest), &mut Some(msg))?; - T::deliver(ticket)?; - Ok(price) +) -> Result<(XcmHash, MultiAssets), SendError> { + let (ticket, price) = T::validate(network, channel, &mut Some(dest), &mut Some(msg.clone()))?; + let hash = T::deliver(ticket)?; + Ok((hash, price)) } diff --git a/xcm/xcm-executor/src/traits/on_response.rs b/xcm/xcm-executor/src/traits/on_response.rs index 016976453511..2720dfd6ce5a 100644 --- a/xcm/xcm-executor/src/traits/on_response.rs +++ b/xcm/xcm-executor/src/traits/on_response.rs @@ -15,7 +15,9 @@ // along with Polkadot. If not, see . use frame_support::weights::Weight; -use xcm::latest::{Error as XcmError, MultiLocation, QueryId, Response, Result as XcmResult}; +use xcm::latest::{ + Error as XcmError, MultiLocation, QueryId, Response, Result as XcmResult, XcmContext, +}; /// Define what needs to be done upon receiving a query response. pub trait OnResponse { @@ -34,6 +36,7 @@ pub trait OnResponse { querier: Option<&MultiLocation>, response: Response, max_weight: Weight, + context: &XcmContext, ) -> Weight; } impl OnResponse for () { @@ -50,6 +53,7 @@ impl OnResponse for () { _querier: Option<&MultiLocation>, _response: Response, _max_weight: Weight, + _context: &XcmContext, ) -> Weight { 0 } @@ -65,21 +69,26 @@ pub trait VersionChangeNotifier { /// /// If the `location` has an ongoing notification and when this function is called, then an /// error should be returned. - fn start(location: &MultiLocation, query_id: QueryId, max_weight: u64) -> XcmResult; + fn start( + location: &MultiLocation, + query_id: QueryId, + max_weight: u64, + context: &XcmContext, + ) -> XcmResult; /// Stop notifying `location` should the XCM change. Returns an error if there is no existing /// notification set up. - fn stop(location: &MultiLocation) -> XcmResult; + fn stop(location: &MultiLocation, context: &XcmContext) -> XcmResult; /// Return true if a location is subscribed to XCM version changes. fn is_subscribed(location: &MultiLocation) -> bool; } impl VersionChangeNotifier for () { - fn start(_: &MultiLocation, _: QueryId, _: u64) -> XcmResult { + fn start(_: &MultiLocation, _: QueryId, _: u64, _: &XcmContext) -> XcmResult { Err(XcmError::Unimplemented) } - fn stop(_: &MultiLocation) -> XcmResult { + fn stop(_: &MultiLocation, _: &XcmContext) -> XcmResult { Err(XcmError::Unimplemented) } fn is_subscribed(_: &MultiLocation) -> bool { diff --git a/xcm/xcm-executor/src/traits/transact_asset.rs b/xcm/xcm-executor/src/traits/transact_asset.rs index 7a933275a45a..49cf2f43fff8 100644 --- a/xcm/xcm-executor/src/traits/transact_asset.rs +++ b/xcm/xcm-executor/src/traits/transact_asset.rs @@ -16,7 +16,7 @@ use crate::Assets; use sp_std::result::Result; -use xcm::latest::{Error as XcmError, MultiAsset, MultiLocation, Result as XcmResult}; +use xcm::latest::{Error as XcmError, MultiAsset, MultiLocation, Result as XcmResult, XcmContext}; /// Facility for asset transacting. /// @@ -29,7 +29,11 @@ pub trait TransactAsset { /// Ensure that `check_in` will result in `Ok`. /// /// When composed as a tuple, all type-items are called and at least one must result in `Ok`. - fn can_check_in(_origin: &MultiLocation, _what: &MultiAsset) -> XcmResult { + fn can_check_in( + _origin: &MultiLocation, + _what: &MultiAsset, + _context: &XcmContext, + ) -> XcmResult { Err(XcmError::Unimplemented) } @@ -46,7 +50,7 @@ pub trait TransactAsset { /// /// When composed as a tuple, all type-items are called. It is up to the implementer that there exists no /// value for `_what` which can cause side-effects for more than one of the type-items. - fn check_in(_origin: &MultiLocation, _what: &MultiAsset) {} + fn check_in(_origin: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {} /// An asset has been teleported out to the given destination. This should do whatever housekeeping is needed. /// @@ -58,20 +62,28 @@ pub trait TransactAsset { /// /// When composed as a tuple, all type-items are called. It is up to the implementer that there exists no /// value for `_what` which can cause side-effects for more than one of the type-items. - fn check_out(_dest: &MultiLocation, _what: &MultiAsset) {} + fn check_out(_dest: &MultiLocation, _what: &MultiAsset, _context: &XcmContext) {} /// Deposit the `what` asset into the account of `who`. /// /// Implementations should return `XcmError::FailedToTransactAsset` if deposit failed. - fn deposit_asset(_what: &MultiAsset, _who: &MultiLocation) -> XcmResult { + fn deposit_asset(_what: &MultiAsset, _who: &MultiLocation, _context: &XcmContext) -> XcmResult { Err(XcmError::Unimplemented) } /// Withdraw the given asset from the consensus system. Return the actual asset(s) withdrawn, /// which should always be equal to `_what`. /// + /// The XCM `_maybe_context` parameter may be `None` when the caller of `withdraw_asset` is + /// outside of the context of a currently-executing XCM. An example will be the `charge_fees` + /// method in the XCM executor. + /// /// Implementations should return `XcmError::FailedToTransactAsset` if withdraw failed. - fn withdraw_asset(_what: &MultiAsset, _who: &MultiLocation) -> Result { + fn withdraw_asset( + _what: &MultiAsset, + _who: &MultiLocation, + _maybe_context: Option<&XcmContext>, + ) -> Result { Err(XcmError::Unimplemented) } @@ -82,6 +94,7 @@ pub trait TransactAsset { _asset: &MultiAsset, _from: &MultiLocation, _to: &MultiLocation, + _context: &XcmContext, ) -> Result { Err(XcmError::Unimplemented) } @@ -93,12 +106,13 @@ pub trait TransactAsset { asset: &MultiAsset, from: &MultiLocation, to: &MultiLocation, + context: &XcmContext, ) -> Result { - match Self::transfer_asset(asset, from, to) { + match Self::transfer_asset(asset, from, to, context) { Err(XcmError::AssetNotFound | XcmError::Unimplemented) => { - let assets = Self::withdraw_asset(asset, from)?; + let assets = Self::withdraw_asset(asset, from, Some(context))?; // Not a very forgiving attitude; once we implement roll-backs then it'll be nicer. - Self::deposit_asset(asset, to)?; + Self::deposit_asset(asset, to, context)?; Ok(assets) }, result => result, @@ -108,62 +122,69 @@ pub trait TransactAsset { #[impl_trait_for_tuples::impl_for_tuples(30)] impl TransactAsset for Tuple { - fn can_check_in(origin: &MultiLocation, what: &MultiAsset) -> XcmResult { + fn can_check_in(origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) -> XcmResult { for_tuples!( #( - match Tuple::can_check_in(origin, what) { + match Tuple::can_check_in(origin, what, context) { Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (), r => return r, } )* ); log::trace!( target: "xcm::TransactAsset::can_check_in", - "asset not found: what: {:?}, origin: {:?}", + "asset not found: what: {:?}, origin: {:?}, context: {:?}", what, origin, + context, ); Err(XcmError::AssetNotFound) } - fn check_in(origin: &MultiLocation, what: &MultiAsset) { + fn check_in(origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) { for_tuples!( #( - Tuple::check_in(origin, what); + Tuple::check_in(origin, what, context); )* ); } - fn check_out(dest: &MultiLocation, what: &MultiAsset) { + fn check_out(dest: &MultiLocation, what: &MultiAsset, context: &XcmContext) { for_tuples!( #( - Tuple::check_out(dest, what); + Tuple::check_out(dest, what, context); )* ); } - fn deposit_asset(what: &MultiAsset, who: &MultiLocation) -> XcmResult { + fn deposit_asset(what: &MultiAsset, who: &MultiLocation, context: &XcmContext) -> XcmResult { for_tuples!( #( - match Tuple::deposit_asset(what, who) { + match Tuple::deposit_asset(what, who, context) { Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (), r => return r, } )* ); log::trace!( target: "xcm::TransactAsset::deposit_asset", - "did not deposit asset: what: {:?}, who: {:?}", + "did not deposit asset: what: {:?}, who: {:?}, context: {:?}", what, who, + context, ); Err(XcmError::AssetNotFound) } - fn withdraw_asset(what: &MultiAsset, who: &MultiLocation) -> Result { + fn withdraw_asset( + what: &MultiAsset, + who: &MultiLocation, + maybe_context: Option<&XcmContext>, + ) -> Result { for_tuples!( #( - match Tuple::withdraw_asset(what, who) { + match Tuple::withdraw_asset(what, who, maybe_context) { Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (), r => return r, } )* ); log::trace!( target: "xcm::TransactAsset::withdraw_asset", - "did not withdraw asset: what: {:?}, who: {:?}", + "did not withdraw asset: what: {:?}, who: {:?}, maybe_context: {:?}", what, who, + maybe_context, ); Err(XcmError::AssetNotFound) } @@ -172,19 +193,21 @@ impl TransactAsset for Tuple { what: &MultiAsset, from: &MultiLocation, to: &MultiLocation, + context: &XcmContext, ) -> Result { for_tuples!( #( - match Tuple::transfer_asset(what, from, to) { + match Tuple::transfer_asset(what, from, to, context) { Err(XcmError::AssetNotFound) | Err(XcmError::Unimplemented) => (), r => return r, } )* ); log::trace!( target: "xcm::TransactAsset::transfer_asset", - "did not transfer asset: what: {:?}, from: {:?}, to: {:?}", + "did not transfer asset: what: {:?}, from: {:?}, to: {:?}, context: {:?}", what, from, to, + context, ); Err(XcmError::AssetNotFound) } @@ -200,15 +223,27 @@ mod tests { pub struct NotFoundTransactor; impl TransactAsset for NotFoundTransactor { - fn can_check_in(_origin: &MultiLocation, _what: &MultiAsset) -> XcmResult { + fn can_check_in( + _origin: &MultiLocation, + _what: &MultiAsset, + _context: &XcmContext, + ) -> XcmResult { Err(XcmError::AssetNotFound) } - fn deposit_asset(_what: &MultiAsset, _who: &MultiLocation) -> XcmResult { + fn deposit_asset( + _what: &MultiAsset, + _who: &MultiLocation, + _context: &XcmContext, + ) -> XcmResult { Err(XcmError::AssetNotFound) } - fn withdraw_asset(_what: &MultiAsset, _who: &MultiLocation) -> Result { + fn withdraw_asset( + _what: &MultiAsset, + _who: &MultiLocation, + _context: Option<&XcmContext>, + ) -> Result { Err(XcmError::AssetNotFound) } @@ -216,6 +251,7 @@ mod tests { _what: &MultiAsset, _from: &MultiLocation, _to: &MultiLocation, + _context: &XcmContext, ) -> Result { Err(XcmError::AssetNotFound) } @@ -223,15 +259,27 @@ mod tests { pub struct OverflowTransactor; impl TransactAsset for OverflowTransactor { - fn can_check_in(_origin: &MultiLocation, _what: &MultiAsset) -> XcmResult { + fn can_check_in( + _origin: &MultiLocation, + _what: &MultiAsset, + _context: &XcmContext, + ) -> XcmResult { Err(XcmError::Overflow) } - fn deposit_asset(_what: &MultiAsset, _who: &MultiLocation) -> XcmResult { + fn deposit_asset( + _what: &MultiAsset, + _who: &MultiLocation, + _context: &XcmContext, + ) -> XcmResult { Err(XcmError::Overflow) } - fn withdraw_asset(_what: &MultiAsset, _who: &MultiLocation) -> Result { + fn withdraw_asset( + _what: &MultiAsset, + _who: &MultiLocation, + _context: Option<&XcmContext>, + ) -> Result { Err(XcmError::Overflow) } @@ -239,6 +287,7 @@ mod tests { _what: &MultiAsset, _from: &MultiLocation, _to: &MultiLocation, + _context: &XcmContext, ) -> Result { Err(XcmError::Overflow) } @@ -246,15 +295,27 @@ mod tests { pub struct SuccessfulTransactor; impl TransactAsset for SuccessfulTransactor { - fn can_check_in(_origin: &MultiLocation, _what: &MultiAsset) -> XcmResult { + fn can_check_in( + _origin: &MultiLocation, + _what: &MultiAsset, + _context: &XcmContext, + ) -> XcmResult { Ok(()) } - fn deposit_asset(_what: &MultiAsset, _who: &MultiLocation) -> XcmResult { + fn deposit_asset( + _what: &MultiAsset, + _who: &MultiLocation, + _context: &XcmContext, + ) -> XcmResult { Ok(()) } - fn withdraw_asset(_what: &MultiAsset, _who: &MultiLocation) -> Result { + fn withdraw_asset( + _what: &MultiAsset, + _who: &MultiLocation, + _context: Option<&XcmContext>, + ) -> Result { Ok(Assets::default()) } @@ -262,6 +323,7 @@ mod tests { _what: &MultiAsset, _from: &MultiLocation, _to: &MultiLocation, + _context: &XcmContext, ) -> Result { Ok(Assets::default()) } @@ -273,7 +335,11 @@ mod tests { (UnimplementedTransactor, NotFoundTransactor, UnimplementedTransactor); assert_eq!( - MultiTransactor::deposit_asset(&(Here, 1u128).into(), &Here.into()), + MultiTransactor::deposit_asset( + &(Here, 1u128).into(), + &Here.into(), + &XcmContext::with_message_hash([0; 32]), + ), Err(XcmError::AssetNotFound) ); } @@ -282,7 +348,14 @@ mod tests { fn unimplemented_and_not_found_continue_iteration() { type MultiTransactor = (UnimplementedTransactor, NotFoundTransactor, SuccessfulTransactor); - assert_eq!(MultiTransactor::deposit_asset(&(Here, 1u128).into(), &Here.into()), Ok(()),); + assert_eq!( + MultiTransactor::deposit_asset( + &(Here, 1u128).into(), + &Here.into(), + &XcmContext::with_message_hash([0; 32]), + ), + Ok(()) + ); } #[test] @@ -290,7 +363,11 @@ mod tests { type MultiTransactor = (OverflowTransactor, SuccessfulTransactor); assert_eq!( - MultiTransactor::deposit_asset(&(Here, 1u128).into(), &Here.into()), + MultiTransactor::deposit_asset( + &(Here, 1u128).into(), + &Here.into(), + &XcmContext::with_message_hash([0; 32]), + ), Err(XcmError::Overflow) ); } @@ -299,6 +376,13 @@ mod tests { fn success_stops_iteration() { type MultiTransactor = (SuccessfulTransactor, OverflowTransactor); - assert_eq!(MultiTransactor::deposit_asset(&(Here, 1u128).into(), &Here.into()), Ok(()),); + assert_eq!( + MultiTransactor::deposit_asset( + &(Here, 1u128).into(), + &Here.into(), + &XcmContext::with_message_hash([0; 32]), + ), + Ok(()), + ); } } diff --git a/xcm/xcm-simulator/example/src/parachain.rs b/xcm/xcm-simulator/example/src/parachain.rs index ac322dddb499..8c5474a31e84 100644 --- a/xcm/xcm-simulator/example/src/parachain.rs +++ b/xcm/xcm-simulator/example/src/parachain.rs @@ -309,10 +309,11 @@ pub mod mock_msg_queue { max_weight: Weight, ) -> Result { let hash = Encode::using_encoded(&xcm, T::Hashing::hash); + let message_hash = Encode::using_encoded(&xcm, sp_io::hashing::blake2_256); let (result, event) = match Xcm::::try_from(xcm) { Ok(xcm) => { let location = (Parent, Parachain(sender.into())); - match T::XcmExecutor::execute_xcm(location, xcm, max_weight) { + match T::XcmExecutor::execute_xcm(location, xcm, message_hash, max_weight) { Outcome::Error(e) => (Err(e.clone()), Event::Fail(Some(hash), e)), Outcome::Complete(w) => (Ok(w), Event::Success(Some(hash))), // As far as the caller is concerned, this was dispatched without error, so @@ -357,19 +358,18 @@ pub mod mock_msg_queue { ) -> Weight { for (_i, (_sent_at, data)) in iter.enumerate() { let id = sp_io::hashing::blake2_256(&data[..]); - let maybe_msg = - VersionedXcm::::decode(&mut &data[..]).map(Xcm::::try_from); - match maybe_msg { + let maybe_versioned = VersionedXcm::::decode(&mut &data[..]); + match maybe_versioned { Err(_) => { Self::deposit_event(Event::InvalidFormat(id)); }, - Ok(Err(())) => { - Self::deposit_event(Event::UnsupportedVersion(id)); - }, - Ok(Ok(x)) => { - let outcome = T::XcmExecutor::execute_xcm(Parent, x.clone(), limit); - >::append(x); - Self::deposit_event(Event::ExecutedDownward(id, outcome)); + Ok(versioned) => match Xcm::try_from(versioned) { + Err(()) => Self::deposit_event(Event::UnsupportedVersion(id)), + Ok(x) => { + let outcome = T::XcmExecutor::execute_xcm(Parent, x.clone(), id, limit); + >::append(x); + Self::deposit_event(Event::ExecutedDownward(id, outcome)); + }, }, } } diff --git a/xcm/xcm-simulator/fuzzer/src/parachain.rs b/xcm/xcm-simulator/fuzzer/src/parachain.rs index ef9958ecd522..ebecc61d40c7 100644 --- a/xcm/xcm-simulator/fuzzer/src/parachain.rs +++ b/xcm/xcm-simulator/fuzzer/src/parachain.rs @@ -227,10 +227,11 @@ pub mod mock_msg_queue { max_weight: Weight, ) -> Result { let hash = Encode::using_encoded(&xcm, T::Hashing::hash); + let message_hash = xcm.using_encoded(sp_io::hashing::blake2_256); let (result, event) = match Xcm::::try_from(xcm) { Ok(xcm) => { let location = MultiLocation::new(1, X1(Parachain(sender.into()))); - match T::XcmExecutor::execute_xcm(location, xcm, max_weight) { + match T::XcmExecutor::execute_xcm(location, xcm, message_hash, max_weight) { Outcome::Error(e) => (Err(e.clone()), Event::Fail(Some(hash), e)), Outcome::Complete(w) => (Ok(w), Event::Success(Some(hash))), // As far as the caller is concerned, this was dispatched without error, so @@ -285,7 +286,7 @@ pub mod mock_msg_queue { Self::deposit_event(Event::UnsupportedVersion(id)); }, Ok(Ok(x)) => { - let outcome = T::XcmExecutor::execute_xcm(Parent, x.clone(), limit); + let outcome = T::XcmExecutor::execute_xcm(Parent, x.clone(), id, limit); >::append(x); Self::deposit_event(Event::ExecutedDownward(id, outcome)); }, diff --git a/xcm/xcm-simulator/src/lib.rs b/xcm/xcm-simulator/src/lib.rs index 05effd284ec3..6a60eecb71d4 100644 --- a/xcm/xcm-simulator/src/lib.rs +++ b/xcm/xcm-simulator/src/lib.rs @@ -20,7 +20,7 @@ pub use codec::Encode; pub use paste; pub use frame_support::{traits::Get, weights::Weight}; -pub use sp_io::TestExternalities; +pub use sp_io::{hashing::blake2_256, TestExternalities}; pub use sp_std::{cell::RefCell, collections::vec_deque::VecDeque, marker::PhantomData}; pub use polkadot_core_primitives::BlockNumber as RelayBlockNumber; @@ -76,6 +76,10 @@ pub fn encode_xcm(message: Xcm<()>, message_kind: MessageKind) -> Vec { } } +pub fn fake_message_hash(message: &Xcm) -> XcmHash { + message.using_encoded(blake2_256) +} + #[macro_export] #[rustfmt::skip] macro_rules! decl_test_relay_chain { @@ -210,6 +214,7 @@ macro_rules! decl_test_network { parachains = vec![ $( ($para_id:expr, $parachain:ty), )* ], } ) => { + use $crate::Encode; pub struct $name; impl $name { @@ -319,9 +324,10 @@ macro_rules! decl_test_network { } fn deliver( triple: ($crate::ParaId, $crate::MultiLocation, $crate::Xcm<()>), - ) -> Result<(), $crate::SendError> { + ) -> Result<$crate::XcmHash, $crate::SendError> { + let hash = $crate::fake_message_hash(&triple.2); $crate::PARA_MESSAGE_BUS.with(|b| b.borrow_mut().push_back(triple)); - Ok(()) + Ok(hash) } } @@ -350,9 +356,10 @@ macro_rules! decl_test_network { } fn deliver( pair: ($crate::MultiLocation, $crate::Xcm<()>), - ) -> Result<(), $crate::SendError> { + ) -> Result<$crate::XcmHash, $crate::SendError> { + let hash = $crate::fake_message_hash(&pair.1); $crate::RELAY_MESSAGE_BUS.with(|b| b.borrow_mut().push_back(pair)); - Ok(()) + Ok(hash) } } };