diff --git a/bridges/snowbridge/primitives/router/src/outbound/v2.rs b/bridges/snowbridge/primitives/router/src/outbound/v2.rs index 5ec82c16d68b..f88f4327a363 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/v2.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/v2.rs @@ -16,7 +16,7 @@ use snowbridge_core::{ AgentId, TokenId, TokenIdOf, }; use sp_core::{H160, H256}; -use sp_runtime::traits::MaybeEquivalence; +use sp_runtime::traits::{BlakeTwo256, Hash, MaybeEquivalence}; use sp_std::{iter::Peekable, marker::PhantomData, prelude::*}; use xcm::prelude::*; use xcm_builder::{CreateMatcher, ExporterFor, MatchXcm}; @@ -112,13 +112,13 @@ where SendError::MissingArgument })?; - // Inspect ExpectAsset as V2 message + // Inspect AliasOrigin as V2 message let mut instructions = message.clone().0; let result = instructions.matcher().match_next_inst_while( |_| true, |inst| { return match inst { - ExpectAsset(..) => Err(ProcessMessageError::Yield), + AliasOrigin(..) => Err(ProcessMessageError::Yield), _ => Ok(ControlFlow::Continue(())), } }, @@ -180,6 +180,7 @@ enum XcmConverterError { InvalidAsset, UnexpectedInstruction, TooManyCommands, + AliasOriginExpected, } macro_rules! match_expression { @@ -229,6 +230,14 @@ where Ok(result) } + /// Convert the xcm for Ethereum-native token from AH into the Command + /// To match transfers of Ethereum-native tokens, we expect an input of the form: + /// # WithdrawAsset(WETH_FEE) + /// # PayFees(WETH_FEE) + /// # WithdrawAsset(WETH) + /// # AliasOrigin(origin) + /// # DepositAsset(WETH) + /// # SetTopic fn send_tokens_message(&mut self) -> Result { use XcmConverterError::*; @@ -239,15 +248,9 @@ where match_expression!(self.next()?, WithdrawAsset(reserve_assets), reserve_assets) .ok_or(WithdrawAssetExpected)?; - // Check if clear origin exists and skip over it. - if match_expression!(self.peek(), Ok(ClearOrigin), ()).is_some() { - let _ = self.next(); - } - - // Check if ExpectAsset exists and skip over it. - if match_expression!(self.peek(), Ok(ExpectAsset { .. }), ()).is_some() { - let _ = self.next(); - } + // Check AliasOrigin. + let origin = match_expression!(self.next()?, AliasOrigin(origin), origin) + .ok_or(AliasOriginExpected)?; let (deposit_assets, beneficiary) = match_expression!( self.next()?, @@ -298,8 +301,7 @@ where let message = Message { id: (*topic_id).into(), - // Todo: from XCMV5 AliasOrigin - origin: H256::zero(), + origin: BlakeTwo256::hash_of(origin), fee: fee_amount, commands: BoundedVec::try_from(vec![Command::UnlockNativeToken { agent_id: self.agent_id, @@ -317,10 +319,6 @@ where self.iter.next().ok_or(XcmConverterError::UnexpectedEndOfXcm) } - fn peek(&mut self) -> Result<&&'a Instruction, XcmConverterError> { - self.iter.peek().ok_or(XcmConverterError::UnexpectedEndOfXcm) - } - fn network_matches(&self, network: &Option) -> bool { if let Some(network) = network { *network == self.ethereum_network @@ -331,10 +329,11 @@ where /// Convert the xcm for Polkadot-native token from AH into the Command /// To match transfers of Polkadot-native tokens, we expect an input of the form: - /// # ReserveAssetDeposited - /// # ClearOrigin - /// # BuyExecution - /// # DepositAsset + /// # WithdrawAsset(WETH) + /// # PayFees(WETH) + /// # ReserveAssetDeposited(DOT) + /// # AliasOrigin(origin) + /// # DepositAsset(DOT) /// # SetTopic fn send_native_tokens_message(&mut self) -> Result { use XcmConverterError::*; @@ -346,15 +345,9 @@ where match_expression!(self.next()?, ReserveAssetDeposited(reserve_assets), reserve_assets) .ok_or(ReserveAssetDepositedExpected)?; - // Check if clear origin exists and skip over it. - if match_expression!(self.peek(), Ok(ClearOrigin), ()).is_some() { - let _ = self.next(); - } - - // Check if ExpectAsset exists and skip over it. - if match_expression!(self.peek(), Ok(ExpectAsset { .. }), ()).is_some() { - let _ = self.next(); - } + // Check AliasOrigin. + let origin = match_expression!(self.next()?, AliasOrigin(origin), origin) + .ok_or(AliasOriginExpected)?; let (deposit_assets, beneficiary) = match_expression!( self.next()?, @@ -406,7 +399,7 @@ where let topic_id = match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; let message = Message { - origin: H256::zero(), + origin: BlakeTwo256::hash_of(origin), fee: fee_amount, id: (*topic_id).into(), commands: BoundedVec::try_from(vec![Command::MintForeignToken { @@ -472,7 +465,7 @@ impl Contains> for XcmForSnowbridgeV2 { |_| true, |inst| { return match inst { - ExpectAsset(..) => Err(ProcessMessageError::Yield), + AliasOrigin(..) => Err(ProcessMessageError::Yield), _ => Ok(ControlFlow::Continue(())), } }, diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs index ea48daf2610c..f7238142119a 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend/src/tests/snowbridge_v2.rs @@ -13,19 +13,13 @@ // See the License for the specific language governing permissions and // limitations under the License. use crate::imports::*; -use asset_hub_westend_runtime::xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee; use bridge_hub_westend_runtime::EthereumInboundQueue; -use codec::{Decode, Encode}; -use emulated_integration_tests_common::RESERVABLE_ASSET_ID; -use frame_support::pallet_prelude::TypeInfo; use hex_literal::hex; -use rococo_westend_system_emulated_network::asset_hub_westend_emulated_chain::genesis::AssetHubWestendAssetOwner; -use snowbridge_core::{outbound::OperatingMode, AssetMetadata, TokenIdOf}; +use snowbridge_core::{AssetMetadata, TokenIdOf}; use snowbridge_router_primitives::inbound::{ v1::{Command, Destination, MessageV1, VersionedMessage}, GlobalConsensusEthereumConvertsFor, }; -use sp_core::H256; use sp_runtime::MultiAddress; use testnet_parachains_constants::westend::snowbridge::EthereumNetwork; use xcm::v5::AssetTransferFilter; @@ -121,12 +115,7 @@ fn send_weth_from_asset_hub_to_ethereum() { [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], ); - // Internal xcm of InitiateReserveWithdraw, WithdrawAssets + ClearOrigin instructions will - // be appended to the front of the list by the xcm executor - let xcm_on_bh = Xcm(vec![ - ExpectAsset(vec![remote_fee_asset.clone()].into()), - DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, - ]); + let xcm_on_bh = Xcm(vec![DepositAsset { assets: Wild(AllCounted(2)), beneficiary }]); let xcms = VersionedXcm::from(Xcm(vec![ WithdrawAsset(assets.clone().into()), @@ -136,6 +125,7 @@ fn send_weth_from_asset_hub_to_ethereum() { remote_fees: Some(AssetTransferFilter::ReserveWithdraw(Definite( remote_fee_asset.clone().into(), ))), + preserve_origin: true, assets: vec![AssetTransferFilter::ReserveWithdraw(Definite( reserve_asset.clone().into(), ))], @@ -153,7 +143,6 @@ fn send_weth_from_asset_hub_to_ethereum() { }); BridgeHubWestend::execute_with(|| { - use bridge_hub_westend_runtime::xcm_config::TreasuryAccount; type RuntimeEvent = ::RuntimeEvent; // Check that the transfer token back to Ethereum message was queue in the Ethereum // Outbound Queue @@ -185,8 +174,6 @@ fn transfer_relay_token() { let expected_asset_id: Location = Location { parents: 1, interior: [GlobalConsensus(Westend)].into() }; - let expected_token_id = TokenIdOf::convert_location(&expected_asset_id).unwrap(); - let ethereum_sovereign: AccountId = GlobalConsensusEthereumConvertsFor::<[u8; 32]>::convert_location(&Location::new( 2, @@ -274,10 +261,7 @@ fn transfer_relay_token() { [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }], ); - let xcm_on_bh = Xcm(vec![ - ExpectAsset(vec![remote_fee_asset.clone()].into()), - DepositAsset { assets: Wild(AllCounted(2)), beneficiary }, - ]); + let xcm_on_bh = Xcm(vec![DepositAsset { assets: Wild(AllCounted(2)), beneficiary }]); let xcms = VersionedXcm::from(Xcm(vec![ WithdrawAsset(assets.clone().into()), @@ -287,6 +271,7 @@ fn transfer_relay_token() { remote_fees: Some(AssetTransferFilter::ReserveWithdraw(Definite( remote_fee_asset.clone().into(), ))), + preserve_origin: true, assets: vec![AssetTransferFilter::ReserveDeposit(Definite( Asset { id: AssetId(Location::parent()), fun: Fungible(TOKEN_AMOUNT) }.into(), ))], @@ -302,16 +287,16 @@ fn transfer_relay_token() { ) .unwrap(); - let events = AssetHubWestend::events(); // Check that the native asset transferred to some reserved account(sovereign of Ethereum) - // assert!( - // events.iter().any(|event| matches!( - // event, - // RuntimeEvent::Balances(pallet_balances::Event::Transfer { to, ..}) - // if *to == ethereum_sovereign.clone(), - // )), - // "native token reserved to Ethereum sovereign account." - // ); + let events = AssetHubWestend::events(); + assert!( + events.iter().any(|event| matches!( + event, + RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount}) + if *who == ethereum_sovereign.clone() && *amount == TOKEN_AMOUNT, + )), + "native token reserved to Ethereum sovereign account." + ); }); BridgeHubWestend::execute_with(|| {