// Copyright 2020 Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Polkadot is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Polkadot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . use super::*; use super::mock::*; use {MultiAsset::*, Option::None}; use xcm::v0::{Order, NetworkId::Any, Outcome, Response, ExecuteXcm}; use xcm_executor::{XcmExecutor, Config, traits::*}; #[test] fn basic_setup_works() { add_reserve(X1(Parent), AllConcreteFungible { id: X1(Parent) }); assert!(::IsReserve::filter_asset_location( &ConcreteFungible { id: X1(Parent), amount: 100 }, &X1(Parent), )); assert_eq!(to_account(X1(Parachain(1))), Ok(1001)); assert_eq!(to_account(X1(Parachain(50))), Ok(1050)); assert_eq!(to_account(X2(Parent, Parachain(1))), Ok(2001)); assert_eq!(to_account(X2(Parent, Parachain(50))), Ok(2050)); assert_eq!(to_account(X1(AccountIndex64{index:1, network:Any})), Ok(1)); assert_eq!(to_account(X1(AccountIndex64{index:42, network:Any})), Ok(42)); assert_eq!(to_account(Null), Ok(3000)); } #[test] fn weigher_should_work() { let mut message = opaque::Xcm::ReserveAssetDeposit { assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }], effects: vec![ Order::BuyExecution { fees: All, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] }, Order::DepositAsset { assets: vec![All], dest: Null }, ], }.into(); assert_eq!(::Weigher::shallow(&mut message), Ok(30)); } #[test] fn take_weight_credit_barrier_should_work() { let mut message = opaque::Xcm::TransferAsset { assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }], dest: Null, }; let mut weight_credit = 10; let r = TakeWeightCredit::should_execute( &X1(Parent), true, &mut message, 10, &mut weight_credit, ); assert_eq!(r, Ok(())); assert_eq!(weight_credit, 0); let r = TakeWeightCredit::should_execute( &X1(Parent), true, &mut message, 10, &mut weight_credit, ); assert_eq!(r, Err(())); assert_eq!(weight_credit, 0); } #[test] fn allow_unpaid_should_work() { let mut message = opaque::Xcm::TransferAsset { assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }], dest: Null, }; AllowUnpaidFrom::set(vec![ X1(Parent) ]); let r = AllowUnpaidExecutionFrom::>::should_execute( &X1(Parachain(1)), true, &mut message, 10, &mut 0, ); assert_eq!(r, Err(())); let r = AllowUnpaidExecutionFrom::>::should_execute( &X1(Parent), true, &mut message, 10, &mut 0, ); assert_eq!(r, Ok(())); } #[test] fn allow_paid_should_work() { AllowPaidFrom::set(vec![ X1(Parent) ]); let mut message = opaque::Xcm::TransferAsset { assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }], dest: Null, }; let r = AllowTopLevelPaidExecutionFrom::>::should_execute( &X1(Parachain(1)), true, &mut message, 10, &mut 0, ); assert_eq!(r, Err(())); let mut underpaying_message = opaque::Xcm::ReserveAssetDeposit { assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }], effects: vec![ Order::BuyExecution { fees: All, weight: 0, debt: 20, halt_on_error: true, xcm: vec![] }, Order::DepositAsset { assets: vec![All], dest: Null }, ], }; let r = AllowTopLevelPaidExecutionFrom::>::should_execute( &X1(Parent), true, &mut underpaying_message, 30, &mut 0, ); assert_eq!(r, Err(())); let mut paying_message = opaque::Xcm::ReserveAssetDeposit { assets: vec![ConcreteFungible { id: X1(Parent), amount: 100 }], effects: vec![ Order::BuyExecution { fees: All, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] }, Order::DepositAsset { assets: vec![All], dest: Null }, ], }; let r = AllowTopLevelPaidExecutionFrom::>::should_execute( &X1(Parachain(1)), true, &mut paying_message, 30, &mut 0, ); assert_eq!(r, Err(())); let r = AllowTopLevelPaidExecutionFrom::>::should_execute( &X1(Parent), true, &mut paying_message, 30, &mut 0, ); assert_eq!(r, Ok(())); } #[test] fn paying_reserve_deposit_should_work() { AllowPaidFrom::set(vec![ X1(Parent) ]); add_reserve(X1(Parent), AllConcreteFungible { id: X1(Parent) }); WeightPrice::set((X1(Parent), 1_000_000_000_000)); let origin = X1(Parent); let message = Xcm::::ReserveAssetDeposit { assets: vec![ ConcreteFungible { id: X1(Parent), amount: 100 } ], effects: vec![ Order::::BuyExecution { fees: All, weight: 0, debt: 30, halt_on_error: true, xcm: vec![] }, Order::::DepositAsset { assets: vec![ All ], dest: Null }, ], }; let weight_limit = 50; let r = XcmExecutor::::execute_xcm(origin, message, weight_limit); assert_eq!(r, Outcome::Complete(30)); assert_eq!(assets(3000), vec![ ConcreteFungible { id: X1(Parent), amount: 70 } ]); } #[test] fn transfer_should_work() { // we'll let them have message execution for free. AllowUnpaidFrom::set(vec![ X1(Parachain(1)) ]); // Child parachain #1 owns 1000 tokens held by us in reserve. add_asset(1001, ConcreteFungible { id: Null, amount: 1000 }); // They want to transfer 100 of them to their sibling parachain #2 let r = XcmExecutor::::execute_xcm( X1(Parachain(1)), Xcm::TransferAsset { assets: vec![ ConcreteFungible { id: Null, amount: 100 } ], dest: X1(AccountIndex64{index:3, network:Any}), }, 50, ); assert_eq!(r, Outcome::Complete(10)); assert_eq!(assets(3), vec![ ConcreteFungible { id: Null, amount: 100 } ]); assert_eq!(assets(1001), vec![ ConcreteFungible { id: Null, amount: 900 } ]); assert_eq!(sent_xcm(), vec![]); } #[test] fn reserve_transfer_should_work() { AllowUnpaidFrom::set(vec![ X1(Parachain(1)) ]); // Child parachain #1 owns 1000 tokens held by us in reserve. add_asset(1001, ConcreteFungible { id: Null, amount: 1000 }); // The remote account owned by gav. let three = X1(AccountIndex64{index:3, network:Any}); // 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( X1(Parachain(1)), Xcm::TransferReserveAsset { assets: vec![ ConcreteFungible { id: Null, amount: 100 } ], dest: X1(Parachain(2)), effects: vec![ Order::DepositAsset { assets: vec![ All ], dest: three.clone() } ], }, 50, ); assert_eq!(r, Outcome::Complete(10)); assert_eq!(assets(1002), vec![ ConcreteFungible { id: Null, amount: 100 } ]); assert_eq!(sent_xcm(), vec![( X1(Parachain(2)), Xcm::ReserveAssetDeposit { assets: vec![ ConcreteFungible { id: X1(Parent), amount: 100 } ], effects: vec![ Order::DepositAsset { assets: vec![ All ], dest: three } ], }) ]); } #[test] fn transacting_should_work() { AllowUnpaidFrom::set(vec![ X1(Parent) ]); let origin = X1(Parent); let message = Xcm::::Transact { origin_type: OriginKind::Native, require_weight_at_most: 50, call: TestCall::Any(50, None).encode().into(), }; let weight_limit = 60; let r = XcmExecutor::::execute_xcm(origin, message, weight_limit); assert_eq!(r, Outcome::Complete(60)); } #[test] fn transacting_should_respect_max_weight_requirement() { AllowUnpaidFrom::set(vec![ X1(Parent) ]); let origin = X1(Parent); let message = Xcm::::Transact { origin_type: OriginKind::Native, require_weight_at_most: 40, call: TestCall::Any(50, None).encode().into(), }; let weight_limit = 60; let r = XcmExecutor::::execute_xcm(origin, message, weight_limit); assert_eq!(r, Outcome::Incomplete(60, XcmError::TooMuchWeightRequired)); } #[test] fn transacting_should_refund_weight() { AllowUnpaidFrom::set(vec![ X1(Parent) ]); let origin = X1(Parent); let message = Xcm::::Transact { origin_type: OriginKind::Native, require_weight_at_most: 50, call: TestCall::Any(50, Some(30)).encode().into(), }; let weight_limit = 60; let r = XcmExecutor::::execute_xcm(origin, message, weight_limit); assert_eq!(r, Outcome::Complete(40)); } #[test] fn paid_transacting_should_refund_payment_for_unused_weight() { let one = X1(AccountIndex64{index:1, network:Any}); AllowPaidFrom::set(vec![ one.clone() ]); add_asset(1, ConcreteFungible { id: X1(Parent), amount: 100 }); WeightPrice::set((X1(Parent), 1_000_000_000_000)); let origin = one.clone(); let message = Xcm::::WithdrawAsset { assets: vec![ ConcreteFungible { id: X1(Parent), amount: 100 } ], // enough for 100 units of weight. effects: vec![ Order::::BuyExecution { fees: All, weight: 70, debt: 30, halt_on_error: true, xcm: vec![ Xcm::::Transact { origin_type: OriginKind::Native, require_weight_at_most: 60, // call estimated at 70 but only takes 10. call: TestCall::Any(60, Some(10)).encode().into(), } ] }, Order::::DepositAsset { assets: vec![ All ], dest: one.clone() }, ], }; let weight_limit = 100; let r = XcmExecutor::::execute_xcm(origin, message, weight_limit); assert_eq!(r, Outcome::Complete(50)); assert_eq!(assets(1), vec![ ConcreteFungible { id: X1(Parent), amount: 50 } ]); } #[test] fn prepaid_result_of_query_should_get_free_execution() { let query_id = 33; let origin = X1(Parent); // We put this in manually here, but normally this would be done at the point of crafting the message. expect_response(query_id, origin.clone()); let the_response = Response::Assets(vec![ ConcreteFungible { id: X1(Parent), amount: 100 } ]); let message = Xcm::::QueryResponse { query_id, response: the_response.clone(), }; let weight_limit = 10; // First time the response gets through since we're expecting it... let r = XcmExecutor::::execute_xcm(origin.clone(), message.clone(), 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(origin.clone(), message.clone(), weight_limit); assert_eq!(r, Outcome::Incomplete(10, XcmError::Barrier)); }