diff --git a/Cargo.lock b/Cargo.lock index 70a8e72fe36f..61ba9d99e1f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -893,9 +893,11 @@ dependencies = [ "sp-core", "sp-genesis-builder", "sp-inherents", + "sp-io", "sp-offchain", "sp-runtime", "sp-session", + "sp-std 14.0.0", "sp-storage 19.0.0", "sp-transaction-pool", "sp-version", diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs index 25e583286a32..dceee4c83afd 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs @@ -379,7 +379,6 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { use ahr_xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; let assethub_location = BridgeHubRococo::sibling_location_of(AssetHubRococo::para_id()); let assethub_sovereign = BridgeHubRococo::sovereign_account_id_of(assethub_location); - let bridgehub_location = AssetHubRococo::sibling_location_of(BridgeHubRococo::para_id()); AssetHubRococo::force_xcm_version( Location::new(2, [GlobalConsensus(Ethereum { chain_id: CHAIN_ID })]), @@ -388,9 +387,8 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { const WETH_AMOUNT: u128 = 1_000_000_000; const FEE_AMOUNT: u128 = 2_750_872_500_000; - const TELEPORT_FEE_AMOUNT: u128 = 12_000_000; // To cover the delivery cost on BH and - const LOCAL_FEE_AMOUNT: u128 = DefaultBridgeHubEthereumBaseFee::get() + TELEPORT_FEE_AMOUNT; + const LOCAL_FEE_AMOUNT: u128 = DefaultBridgeHubEthereumBaseFee::get(); // To cover the delivery cost on Ethereum const REMOTE_FEE_AMOUNT: u128 = FEE_AMOUNT - LOCAL_FEE_AMOUNT; @@ -450,23 +448,10 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, ]); - let teleport_xcm_on_bh = Xcm(vec![ - BuyExecution { fees: local_fee_asset.clone(), weight_limit: Unlimited }, - DepositAsset { - assets: Wild(AllCounted(1)), - beneficiary: - (AccountId32 { id: assethub_sovereign.clone().into(), network: None },).into(), - }, - ]); - let xcms = VersionedXcm::from(Xcm(vec![ WithdrawAsset(assets.clone().into()), + BurnAsset(local_fee_asset.clone().into()), SetFeesMode { jit_withdraw: true }, - InitiateTeleport { - assets: Definite(vec![local_fee_asset.clone()].into()), - xcm: teleport_xcm_on_bh, - dest: bridgehub_location, - }, InitiateReserveWithdraw { assets: Definite(vec![remote_fee_asset.clone(), weth_asset.clone()].into()), // with reserve set to Ethereum destination, the ExportMessage will @@ -511,7 +496,7 @@ fn send_weth_asset_from_asset_hub_to_ethereum() { // Assert there is still some fee left in sov account after the transfer let free_balance_of_sovereign_on_bh_after = ::Balances::free_balance(assethub_sovereign); - assert_eq!(free_balance_of_sovereign_on_bh_after, 3613334); + assert_eq!(free_balance_of_sovereign_on_bh_after, 15590000); }); } diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml index 98df41090a40..6e48248b7c72 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/Cargo.toml @@ -49,10 +49,12 @@ sp-block-builder = { workspace = true } sp-consensus-aura = { workspace = true } sp-core = { workspace = true } sp-inherents = { workspace = true } +sp-io = { workspace = true } sp-genesis-builder = { workspace = true } sp-offchain = { workspace = true } sp-runtime = { workspace = true } sp-session = { workspace = true } +sp-std = { workspace = true } sp-storage = { workspace = true } sp-transaction-pool = { workspace = true } sp-version = { workspace = true } @@ -237,9 +239,11 @@ std = [ "sp-core/std", "sp-genesis-builder/std", "sp-inherents/std", + "sp-io/std", "sp-offchain/std", "sp-runtime/std", "sp-session/std", + "sp-std/std", "sp-storage/std", "sp-transaction-pool/std", "sp-version/std", diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index bc66f936c00c..65c470870303 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -20,15 +20,17 @@ use super::{ ToWestendXcmRouter, TransactionByteFee, TrustBackedAssetsInstance, Uniques, WeightToFee, XcmpQueue, }; +use crate::{vec, Vec}; use assets_common::{ matching::{FromNetwork, FromSiblingParachain, IsForeignConcreteAsset}, TrustBackedAssetsAsLocation, }; +use codec::Encode; use frame_support::{ parameter_types, traits::{ tokens::imbalance::{ResolveAssetTo, ResolveTo}, - ConstU32, Contains, Equals, Everything, Nothing, PalletInfoAccess, + ConstU32, Contains, Equals, Everything, Get, Nothing, PalletInfoAccess, }, }; use frame_system::EnsureRoot; @@ -44,22 +46,27 @@ use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::xcm_sender::ExponentialPrice; use snowbridge_router_primitives::inbound::GlobalConsensusEthereumConvertsFor; use sp_runtime::traits::{AccountIdConversion, ConvertInto}; +use sp_std::marker::PhantomData; use testnet_parachains_constants::rococo::snowbridge::{ EthereumNetwork, INBOUND_QUEUE_PALLET_INDEX, }; -use xcm::latest::prelude::*; +use xcm::{ + latest::prelude::*, + prelude::SendError::{MissingArgument, NotApplicable, Unroutable}, + VersionedLocation, VersionedXcm, +}; use xcm_builder::{ - AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, - AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, - EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, - GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, - NetworkExportTableItem, NoChecking, NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignPaidRemoteExporter, - SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, + ensure_is_remote, AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, + AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, + DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, ExporterFor, FrameTransactionalProcessor, + FungibleAdapter, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, + InspectMessageQueues, IsConcrete, LocalMint, NetworkExportTableItem, NoChecking, + NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, + SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, StartsWith, + StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; @@ -443,6 +450,92 @@ type LocalXcmRouter = ( XcmpQueue, ); +pub struct SovereignReceiveTeleportRemoteExporter( + PhantomData<(Bridges, Router, UniversalLocation)>, +); +impl> SendXcm + for SovereignReceiveTeleportRemoteExporter +{ + type Ticket = Router::Ticket; + + fn validate( + dest: &mut Option, + msg: &mut Option>, + ) -> SendResult { + let d = dest.as_ref().ok_or(MissingArgument)?; + let devolved = + ensure_is_remote(UniversalLocation::get(), d.clone()).map_err(|_| NotApplicable)?; + let (remote_network, remote_location) = devolved; + let xcm = msg.take().ok_or(MissingArgument)?; + + // find exporter + let Some((bridge, maybe_payment)) = + Bridges::exporter_for(&remote_network, &remote_location, &xcm) + else { + // We need to make sure that msg is not consumed in case of `NotApplicable`. + *msg = Some(xcm); + return Err(NotApplicable) + }; + + // `xcm` should already end with `SetTopic` - if it does, then extract and derive into + // an onward topic ID. + let maybe_forward_id = match xcm.last() { + Some(SetTopic(t)) => + Some((b"forward_id_for", t).using_encoded(sp_io::hashing::blake2_256)), + _ => None, + }; + + let local_from_bridge = + UniversalLocation::get().invert_target(&bridge).map_err(|_| Unroutable)?; + let export_instruction = + ExportMessage { network: remote_network, destination: remote_location, xcm }; + + let mut message = Xcm(if let Some(ref payment) = maybe_payment { + let fees = payment + .clone() + .reanchored(&bridge, &UniversalLocation::get()) + .map_err(|_| Unroutable)?; + vec![ + ReceiveTeleportedAsset(fees.clone().into()), + BuyExecution { fees, weight_limit: Unlimited }, + // `SetAppendix` ensures that `fees` are not trapped in any case, for example, when + // `ExportXcm::validate` encounters an error during the processing of + // `ExportMessage`. + SetAppendix(Xcm(vec![DepositAsset { + assets: AllCounted(1).into(), + beneficiary: local_from_bridge, + }])), + export_instruction, + ] + } else { + vec![export_instruction] + }); + if let Some(forward_id) = maybe_forward_id { + message.0.push(SetTopic(forward_id)); + } + + // We then send a normal message to the bridge asking it to export the prepended + // message to the remote chain. + let (v, mut cost) = validate_send::(bridge, message)?; + if let Some(bridge_payment) = maybe_payment { + cost.push(bridge_payment); + } + Ok((v, cost)) + } + + fn deliver(ticket: Router::Ticket) -> Result { + Router::deliver(ticket) + } +} + +impl InspectMessageQueues + for SovereignReceiveTeleportRemoteExporter +{ + fn get_messages() -> Vec<(VersionedLocation, Vec>)> { + Router::get_messages() + } +} + /// The means for routing XCM messages which are not for local execution into the right message /// queues. pub type XcmRouter = WithUniqueTopic<( @@ -452,7 +545,11 @@ pub type XcmRouter = WithUniqueTopic<( ToWestendXcmRouter, // Router which wraps and sends xcm to BridgeHub to be delivered to the Ethereum // GlobalConsensus - SovereignPaidRemoteExporter, + SovereignReceiveTeleportRemoteExporter< + bridging::EthereumNetworkExportTable, + XcmpQueue, + UniversalLocation, + >, )>; impl pallet_xcm::Config for Runtime {