Skip to content

Commit 4c5e93b

Browse files
xcm-executor: DepositReserveAsset charges delivery fees from inner assets (#3142)
This fix aims to solve an issue in Kusama that resulted in failed reserve asset transfers. During multi-hop XCMs, like reserve asset transfers where the reserve is not the sender nor the destination, but a third remote chain, the origin is not available to pay for delivery fees out of their account directly, so delivery fees should be paid out of transferred assets. This commit also adds an xcm-emulator regression test that validates this scenario is now working. Signed-off-by: Adrian Catangiu <adrian@parity.io> Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
1 parent 451ef3d commit 4c5e93b

8 files changed

Lines changed: 312 additions & 34 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@ xcm = { package = "staging-xcm", path = "../../../../../../../polkadot/xcm", def
2424
pallet-xcm = { path = "../../../../../../../polkadot/xcm/pallet-xcm", default-features = false, version = "6.0.0" }
2525
xcm-executor = { package = "staging-xcm-executor", path = "../../../../../../../polkadot/xcm/xcm-executor", default-features = false, version = "6.0.0" }
2626
rococo-runtime = { version = "6.0.0", path = "../../../../../../../polkadot/runtime/rococo" }
27+
28+
# Cumulus
2729
asset-test-utils = { version = "6.0.0", path = "../../../../../runtimes/assets/test-utils" }
28-
parachains-common = { version = "6.0.0", path = "../../../../../common" }
30+
parachains-common = { version = "6.0.0", path = "../../../../../../parachains/common" }
31+
cumulus-pallet-parachain-system = { path = "../../../../../../pallets/parachain-system", default-features = false }
32+
testnet-parachains-constants = { path = "../../../../../runtimes/constants", features = ["rococo"] }
2933
asset-hub-rococo-runtime = { version = "0.11.0", path = "../../../../../runtimes/assets/asset-hub-rococo" }
3034
emulated-integration-tests-common = { path = "../../../common", default-features = false, version = "2.0.0" }
3135
rococo-system-emulated-network = { version = "0.1.0", path = "../../../networks/rococo-system" }

cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ pub use rococo_system_emulated_network::{
5151
AssetHubRococoParaSender as AssetHubRococoSender, BridgeHubRococoPara as BridgeHubRococo,
5252
BridgeHubRococoParaReceiver as BridgeHubRococoReceiver, PenpalAPara as PenpalA,
5353
PenpalAParaReceiver as PenpalAReceiver, PenpalAParaSender as PenpalASender,
54-
RococoRelay as Rococo, RococoRelayReceiver as RococoReceiver,
55-
RococoRelaySender as RococoSender,
54+
PenpalBPara as PenpalB, PenpalBParaReceiver as PenpalBReceiver, RococoRelay as Rococo,
55+
RococoRelayReceiver as RococoReceiver, RococoRelaySender as RococoSender,
5656
};
5757

5858
pub const ASSET_ID: u32 = 1;
@@ -65,6 +65,7 @@ pub type RelayToParaTest = Test<Rococo, PenpalA>;
6565
pub type SystemParaToRelayTest = Test<AssetHubRococo, Rococo>;
6666
pub type SystemParaToParaTest = Test<AssetHubRococo, PenpalA>;
6767
pub type ParaToSystemParaTest = Test<PenpalA, AssetHubRococo>;
68+
pub type ParaToParaTest = Test<PenpalA, PenpalB, Rococo>;
6869

6970
/// Returns a `TestArgs` instance to be used for the Relay Chain across integration tests
7071
pub fn relay_test_args(

cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/tests/reserve_transfer.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,69 @@ fn system_para_to_para_assets_receiver_assertions<Test>(_: Test) {
152152
);
153153
}
154154

155+
fn para_to_para_sender_assertions(t: ParaToParaTest) {
156+
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
157+
PenpalA::assert_xcm_pallet_attempted_complete(None);
158+
assert_expected_events!(
159+
PenpalA,
160+
vec![
161+
// Amount to reserve transfer is transferred to Parachain's Sovereign account
162+
RuntimeEvent::Balances(
163+
pallet_balances::Event::Withdraw { who, amount }
164+
) => {
165+
who: *who == t.sender.account_id,
166+
amount: *amount == t.args.amount,
167+
},
168+
// XCM sent to relay reserve
169+
RuntimeEvent::ParachainSystem(
170+
cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. }
171+
) => {},
172+
]
173+
);
174+
}
175+
176+
fn para_to_para_relay_hop_assertions(t: ParaToParaTest) {
177+
type RuntimeEvent = <Rococo as Chain>::RuntimeEvent;
178+
let sov_penpal_a_on_rococo =
179+
Rococo::sovereign_account_id_of(Rococo::child_location_of(PenpalA::para_id()));
180+
let sov_penpal_b_on_rococo =
181+
Rococo::sovereign_account_id_of(Rococo::child_location_of(PenpalB::para_id()));
182+
assert_expected_events!(
183+
Rococo,
184+
vec![
185+
// Withdrawn from sender parachain SA
186+
RuntimeEvent::Balances(
187+
pallet_balances::Event::Withdraw { who, amount }
188+
) => {
189+
who: *who == sov_penpal_a_on_rococo,
190+
amount: *amount == t.args.amount,
191+
},
192+
// Deposited to receiver parachain SA
193+
RuntimeEvent::Balances(
194+
pallet_balances::Event::Deposit { who, .. }
195+
) => {
196+
who: *who == sov_penpal_b_on_rococo,
197+
},
198+
RuntimeEvent::MessageQueue(
199+
pallet_message_queue::Event::Processed { success: true, .. }
200+
) => {},
201+
]
202+
);
203+
}
204+
205+
fn para_to_para_receiver_assertions(_: ParaToParaTest) {
206+
type RuntimeEvent = <PenpalB as Chain>::RuntimeEvent;
207+
assert_expected_events!(
208+
PenpalB,
209+
vec![
210+
RuntimeEvent::Balances(pallet_balances::Event::Deposit { .. }) => {},
211+
RuntimeEvent::MessageQueue(
212+
pallet_message_queue::Event::Processed { success: true, .. }
213+
) => {},
214+
]
215+
);
216+
}
217+
155218
fn relay_to_para_reserve_transfer_assets(t: RelayToParaTest) -> DispatchResult {
156219
<Rococo as RococoPallet>::XcmPallet::limited_reserve_transfer_assets(
157220
t.signed_origin,
@@ -185,6 +248,17 @@ fn para_to_system_para_reserve_transfer_assets(t: ParaToSystemParaTest) -> Dispa
185248
)
186249
}
187250

251+
fn para_to_para_limited_reserve_transfer_assets(t: ParaToParaTest) -> DispatchResult {
252+
<PenpalA as PenpalAPallet>::PolkadotXcm::limited_reserve_transfer_assets(
253+
t.signed_origin,
254+
bx!(t.args.dest.into()),
255+
bx!(t.args.beneficiary.into()),
256+
bx!(t.args.assets.into()),
257+
t.args.fee_asset_item,
258+
t.args.weight_limit,
259+
)
260+
}
261+
188262
/// Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't work
189263
#[test]
190264
fn reserve_transfer_native_asset_from_relay_to_system_para_fails() {
@@ -493,3 +567,51 @@ fn reserve_transfer_assets_from_system_para_to_para() {
493567
// Receiver's balance is increased by exact amount
494568
assert_eq!(receiver_assets_after, receiver_assets_before + asset_amount_to_send);
495569
}
570+
571+
/// Reserve Transfers of native asset from Parachain to Parachain (through Relay reserve) should
572+
/// work
573+
#[test]
574+
fn reserve_transfer_native_asset_from_para_to_para() {
575+
// Init values for Penpal Parachain
576+
let destination = PenpalA::sibling_location_of(PenpalB::para_id());
577+
let beneficiary_id = PenpalBReceiver::get();
578+
let amount_to_send: Balance = ASSET_HUB_ROCOCO_ED * 10000;
579+
let assets = (Parent, amount_to_send).into();
580+
581+
let test_args = TestContext {
582+
sender: PenpalASender::get(),
583+
receiver: PenpalBReceiver::get(),
584+
args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0),
585+
};
586+
587+
let mut test = ParaToParaTest::new(test_args);
588+
589+
let sender_balance_before = test.sender.balance;
590+
let receiver_balance_before = test.receiver.balance;
591+
592+
let sender_as_seen_by_relay = Rococo::child_location_of(PenpalA::para_id());
593+
let sov_of_sender_on_relay = Rococo::sovereign_account_id_of(sender_as_seen_by_relay);
594+
595+
// fund the PenpalA's SA on Rococo with the native tokens held in reserve
596+
Rococo::fund_accounts(vec![(sov_of_sender_on_relay.into(), amount_to_send * 2)]);
597+
598+
test.set_assertion::<PenpalA>(para_to_para_sender_assertions);
599+
test.set_assertion::<Rococo>(para_to_para_relay_hop_assertions);
600+
test.set_assertion::<PenpalB>(para_to_para_receiver_assertions);
601+
test.set_dispatchable::<PenpalA>(para_to_para_limited_reserve_transfer_assets);
602+
test.assert();
603+
604+
let sender_balance_after = test.sender.balance;
605+
let receiver_balance_after = test.receiver.balance;
606+
607+
let delivery_fees = PenpalA::execute_with(|| {
608+
xcm_helpers::transfer_assets_delivery_fees::<
609+
<PenpalRococoXcmConfig as xcm_executor::Config>::XcmSender,
610+
>(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest)
611+
});
612+
613+
// Sender's balance is reduced
614+
assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after);
615+
// Receiver's balance is increased
616+
assert!(receiver_balance_after > receiver_balance_before);
617+
}

cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ pub use westend_system_emulated_network::{
5151
westend_emulated_chain::{genesis::ED as WESTEND_ED, WestendRelayPallet as WestendPallet},
5252
AssetHubWestendPara as AssetHubWestend, AssetHubWestendParaReceiver as AssetHubWestendReceiver,
5353
AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubWestendPara as BridgeHubWestend,
54-
BridgeHubWestendParaReceiver as BridgeHubWestendReceiver, PenpalBPara as PenpalB,
54+
BridgeHubWestendParaReceiver as BridgeHubWestendReceiver,
55+
CollectivesWestendPara as CollectivesWestend, PenpalAPara as PenpalA,
56+
PenpalAParaReceiver as PenpalAReceiver, PenpalBPara as PenpalB,
5557
PenpalBParaReceiver as PenpalBReceiver, PenpalBParaSender as PenpalBSender,
5658
WestendRelay as Westend, WestendRelayReceiver as WestendReceiver,
5759
WestendRelaySender as WestendSender,
@@ -67,6 +69,7 @@ pub type RelayToParaTest = Test<Westend, PenpalB>;
6769
pub type SystemParaToRelayTest = Test<AssetHubWestend, Westend>;
6870
pub type SystemParaToParaTest = Test<AssetHubWestend, PenpalB>;
6971
pub type ParaToSystemParaTest = Test<PenpalB, AssetHubWestend>;
72+
pub type ParaToParaTest = Test<PenpalB, PenpalA, Westend>;
7073

7174
/// Returns a `TestArgs` instance to be used for the Relay Chain across integration tests
7275
pub fn relay_test_args(

cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/reserve_transfer.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,69 @@ fn system_para_to_para_assets_receiver_assertions<Test>(_: Test) {
162162
);
163163
}
164164

165+
fn para_to_para_sender_assertions(t: ParaToParaTest) {
166+
type RuntimeEvent = <PenpalB as Chain>::RuntimeEvent;
167+
PenpalB::assert_xcm_pallet_attempted_complete(None);
168+
assert_expected_events!(
169+
PenpalB,
170+
vec![
171+
// Amount to reserve transfer is transferred to Parachain's Sovereign account
172+
RuntimeEvent::Balances(
173+
pallet_balances::Event::Withdraw { who, amount }
174+
) => {
175+
who: *who == t.sender.account_id,
176+
amount: *amount == t.args.amount,
177+
},
178+
// XCM sent to relay reserve
179+
RuntimeEvent::ParachainSystem(
180+
cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. }
181+
) => {},
182+
]
183+
);
184+
}
185+
186+
fn para_to_para_relay_hop_assertions(t: ParaToParaTest) {
187+
type RuntimeEvent = <Westend as Chain>::RuntimeEvent;
188+
let sov_penpal_b_on_westend =
189+
Westend::sovereign_account_id_of(Westend::child_location_of(PenpalB::para_id()));
190+
let sov_penpal_a_on_westend =
191+
Westend::sovereign_account_id_of(Westend::child_location_of(PenpalA::para_id()));
192+
assert_expected_events!(
193+
Westend,
194+
vec![
195+
// Withdrawn from sender parachain SA
196+
RuntimeEvent::Balances(
197+
pallet_balances::Event::Withdraw { who, amount }
198+
) => {
199+
who: *who == sov_penpal_b_on_westend,
200+
amount: *amount == t.args.amount,
201+
},
202+
// Deposited to receiver parachain SA
203+
RuntimeEvent::Balances(
204+
pallet_balances::Event::Deposit { who, .. }
205+
) => {
206+
who: *who == sov_penpal_a_on_westend,
207+
},
208+
RuntimeEvent::MessageQueue(
209+
pallet_message_queue::Event::Processed { success: true, .. }
210+
) => {},
211+
]
212+
);
213+
}
214+
215+
fn para_to_para_receiver_assertions(_: ParaToParaTest) {
216+
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
217+
assert_expected_events!(
218+
PenpalA,
219+
vec![
220+
RuntimeEvent::Balances(pallet_balances::Event::Deposit { .. }) => {},
221+
RuntimeEvent::MessageQueue(
222+
pallet_message_queue::Event::Processed { success: true, .. }
223+
) => {},
224+
]
225+
);
226+
}
227+
165228
fn relay_to_para_reserve_transfer_assets(t: RelayToParaTest) -> DispatchResult {
166229
<Westend as WestendPallet>::XcmPallet::limited_reserve_transfer_assets(
167230
t.signed_origin,
@@ -195,6 +258,17 @@ fn para_to_system_para_reserve_transfer_assets(t: ParaToSystemParaTest) -> Dispa
195258
)
196259
}
197260

261+
fn para_to_para_limited_reserve_transfer_assets(t: ParaToParaTest) -> DispatchResult {
262+
<PenpalB as PenpalBPallet>::PolkadotXcm::limited_reserve_transfer_assets(
263+
t.signed_origin,
264+
bx!(t.args.dest.into()),
265+
bx!(t.args.beneficiary.into()),
266+
bx!(t.args.assets.into()),
267+
t.args.fee_asset_item,
268+
t.args.weight_limit,
269+
)
270+
}
271+
198272
/// Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't work
199273
#[test]
200274
fn reserve_transfer_native_asset_from_relay_to_system_para_fails() {
@@ -503,3 +577,51 @@ fn reserve_transfer_assets_from_system_para_to_para() {
503577
// Receiver's balance is increased by exact amount
504578
assert_eq!(receiver_assets_after, receiver_assets_before + asset_amount_to_send);
505579
}
580+
581+
/// Reserve Transfers of native asset from Parachain to Parachain (through Relay reserve) should
582+
/// work
583+
#[test]
584+
fn reserve_transfer_native_asset_from_para_to_para() {
585+
// Init values for Penpal Parachain
586+
let destination = PenpalB::sibling_location_of(PenpalA::para_id());
587+
let beneficiary_id = PenpalAReceiver::get();
588+
let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000;
589+
let assets = (Parent, amount_to_send).into();
590+
591+
let test_args = TestContext {
592+
sender: PenpalBSender::get(),
593+
receiver: PenpalAReceiver::get(),
594+
args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0),
595+
};
596+
597+
let mut test = ParaToParaTest::new(test_args);
598+
599+
let sender_balance_before = test.sender.balance;
600+
let receiver_balance_before = test.receiver.balance;
601+
602+
let sender_as_seen_by_relay = Westend::child_location_of(PenpalB::para_id());
603+
let sov_of_sender_on_relay = Westend::sovereign_account_id_of(sender_as_seen_by_relay);
604+
605+
// fund the PenpalB's SA on Westend with the native tokens held in reserve
606+
Westend::fund_accounts(vec![(sov_of_sender_on_relay.into(), amount_to_send * 2)]);
607+
608+
test.set_assertion::<PenpalB>(para_to_para_sender_assertions);
609+
test.set_assertion::<Westend>(para_to_para_relay_hop_assertions);
610+
test.set_assertion::<PenpalA>(para_to_para_receiver_assertions);
611+
test.set_dispatchable::<PenpalB>(para_to_para_limited_reserve_transfer_assets);
612+
test.assert();
613+
614+
let sender_balance_after = test.sender.balance;
615+
let receiver_balance_after = test.receiver.balance;
616+
617+
let delivery_fees = PenpalB::execute_with(|| {
618+
xcm_helpers::transfer_assets_delivery_fees::<
619+
<PenpalWestendXcmConfig as xcm_executor::Config>::XcmSender,
620+
>(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest)
621+
});
622+
623+
// Sender's balance is reduced
624+
assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after);
625+
// Receiver's balance is increased
626+
assert!(receiver_balance_after > receiver_balance_before);
627+
}

0 commit comments

Comments
 (0)