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>
This commit is contained in:
Adrian Catangiu
2024-01-31 12:47:47 +02:00
committed by GitHub
parent e757f81110
commit 5354097aa1
8 changed files with 294 additions and 34 deletions
@@ -56,7 +56,8 @@ pub use westend_system_emulated_network::{
AssetHubWestendPara as AssetHubWestend, AssetHubWestendParaReceiver as AssetHubWestendReceiver,
AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubWestendPara as BridgeHubWestend,
BridgeHubWestendParaReceiver as BridgeHubWestendReceiver,
CollectivesWestendPara as CollectivesWestend, PenpalBPara as PenpalB,
CollectivesWestendPara as CollectivesWestend, PenpalAPara as PenpalA,
PenpalAParaReceiver as PenpalAReceiver, PenpalBPara as PenpalB,
PenpalBParaReceiver as PenpalBReceiver, PenpalBParaSender as PenpalBSender,
WestendRelay as Westend, WestendRelayReceiver as WestendReceiver,
WestendRelaySender as WestendSender,
@@ -72,6 +73,7 @@ pub type RelayToParaTest = Test<Westend, PenpalB>;
pub type SystemParaToRelayTest = Test<AssetHubWestend, Westend>;
pub type SystemParaToParaTest = Test<AssetHubWestend, PenpalB>;
pub type ParaToSystemParaTest = Test<PenpalB, AssetHubWestend>;
pub type ParaToParaTest = Test<PenpalB, PenpalA, Westend>;
#[cfg(test)]
mod tests;
@@ -162,6 +162,69 @@ fn system_para_to_para_assets_receiver_assertions<Test>(_: Test) {
);
}
fn para_to_para_sender_assertions(t: ParaToParaTest) {
type RuntimeEvent = <PenpalB as Chain>::RuntimeEvent;
PenpalB::assert_xcm_pallet_attempted_complete(None);
assert_expected_events!(
PenpalB,
vec![
// Amount to reserve transfer is transferred to Parachain's Sovereign account
RuntimeEvent::Balances(
pallet_balances::Event::Withdraw { who, amount }
) => {
who: *who == t.sender.account_id,
amount: *amount == t.args.amount,
},
// XCM sent to relay reserve
RuntimeEvent::ParachainSystem(
cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. }
) => {},
]
);
}
fn para_to_para_relay_hop_assertions(t: ParaToParaTest) {
type RuntimeEvent = <Westend as Chain>::RuntimeEvent;
let sov_penpal_b_on_westend =
Westend::sovereign_account_id_of(Westend::child_location_of(PenpalB::para_id()));
let sov_penpal_a_on_westend =
Westend::sovereign_account_id_of(Westend::child_location_of(PenpalA::para_id()));
assert_expected_events!(
Westend,
vec![
// Withdrawn from sender parachain SA
RuntimeEvent::Balances(
pallet_balances::Event::Withdraw { who, amount }
) => {
who: *who == sov_penpal_b_on_westend,
amount: *amount == t.args.amount,
},
// Deposited to receiver parachain SA
RuntimeEvent::Balances(
pallet_balances::Event::Deposit { who, .. }
) => {
who: *who == sov_penpal_a_on_westend,
},
RuntimeEvent::MessageQueue(
pallet_message_queue::Event::Processed { success: true, .. }
) => {},
]
);
}
fn para_to_para_receiver_assertions(_: ParaToParaTest) {
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
assert_expected_events!(
PenpalA,
vec![
RuntimeEvent::Balances(pallet_balances::Event::Deposit { .. }) => {},
RuntimeEvent::MessageQueue(
pallet_message_queue::Event::Processed { success: true, .. }
) => {},
]
);
}
fn relay_to_para_reserve_transfer_assets(t: RelayToParaTest) -> DispatchResult {
<Westend as WestendPallet>::XcmPallet::limited_reserve_transfer_assets(
t.signed_origin,
@@ -195,6 +258,17 @@ fn para_to_system_para_reserve_transfer_assets(t: ParaToSystemParaTest) -> Dispa
)
}
fn para_to_para_limited_reserve_transfer_assets(t: ParaToParaTest) -> DispatchResult {
<PenpalB as PenpalBPallet>::PolkadotXcm::limited_reserve_transfer_assets(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
t.args.fee_asset_item,
t.args.weight_limit,
)
}
/// Reserve Transfers of native asset from Relay Chain to the System Parachain shouldn't work
#[test]
fn reserve_transfer_native_asset_from_relay_to_system_para_fails() {
@@ -503,3 +577,51 @@ fn reserve_transfer_assets_from_system_para_to_para() {
// Receiver's balance is increased by exact amount
assert_eq!(receiver_assets_after, receiver_assets_before + asset_amount_to_send);
}
/// Reserve Transfers of native asset from Parachain to Parachain (through Relay reserve) should
/// work
#[test]
fn reserve_transfer_native_asset_from_para_to_para() {
// Init values for Penpal Parachain
let destination = PenpalB::sibling_location_of(PenpalA::para_id());
let beneficiary_id = PenpalAReceiver::get();
let amount_to_send: Balance = ASSET_HUB_WESTEND_ED * 1000;
let assets = (Parent, amount_to_send).into();
let test_args = TestContext {
sender: PenpalBSender::get(),
receiver: PenpalAReceiver::get(),
args: TestArgs::new_para(destination, beneficiary_id, amount_to_send, assets, None, 0),
};
let mut test = ParaToParaTest::new(test_args);
let sender_balance_before = test.sender.balance;
let receiver_balance_before = test.receiver.balance;
let sender_as_seen_by_relay = Westend::child_location_of(PenpalB::para_id());
let sov_of_sender_on_relay = Westend::sovereign_account_id_of(sender_as_seen_by_relay);
// fund the PenpalB's SA on Westend with the native tokens held in reserve
Westend::fund_accounts(vec![(sov_of_sender_on_relay.into(), amount_to_send * 2)]);
test.set_assertion::<PenpalB>(para_to_para_sender_assertions);
test.set_assertion::<Westend>(para_to_para_relay_hop_assertions);
test.set_assertion::<PenpalA>(para_to_para_receiver_assertions);
test.set_dispatchable::<PenpalB>(para_to_para_limited_reserve_transfer_assets);
test.assert();
let sender_balance_after = test.sender.balance;
let receiver_balance_after = test.receiver.balance;
let delivery_fees = PenpalB::execute_with(|| {
xcm_helpers::transfer_assets_delivery_fees::<
<PenpalWestendXcmConfig as xcm_executor::Config>::XcmSender,
>(test.args.assets.clone(), 0, test.args.weight_limit, test.args.beneficiary, test.args.dest)
});
// Sender's balance is reduced
assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after);
// Receiver's balance is increased
assert!(receiver_balance_after > receiver_balance_before);
}