From 21d52c14759c6ea1a8955c7f2c4f2e480ceaf96a Mon Sep 17 00:00:00 2001 From: ron Date: Wed, 21 Feb 2024 00:28:38 +0800 Subject: [PATCH 01/16] Transact from sub to eth --- Cargo.lock | 1 + .../primitives/core/src/outbound.rs | 25 ++++ .../primitives/router/src/outbound/mod.rs | 57 ++++++++- .../parachains/testing/penpal/src/lib.rs | 1 + .../bridges/bridge-hub-rococo/Cargo.toml | 1 + .../bridge-hub-rococo/src/tests/snowbridge.rs | 108 ++++++++++++++++ .../bridge-hub-rococo/src/xcm_config.rs | 4 +- .../runtimes/testing/penpal/src/lib.rs | 3 + .../testing/penpal/src/pallets/mod.rs | 18 +++ .../penpal/src/pallets/transact_helper.rs | 118 ++++++++++++++++++ .../runtimes/testing/penpal/src/xcm_config.rs | 65 +++++++++- 11 files changed, 390 insertions(+), 11 deletions(-) create mode 100644 cumulus/parachains/runtimes/testing/penpal/src/pallets/mod.rs create mode 100644 cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs diff --git a/Cargo.lock b/Cargo.lock index b41e0e7f1763..89220611b289 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1953,6 +1953,7 @@ dependencies = [ "pallet-xcm", "parachains-common", "parity-scale-codec", + "penpal-runtime", "rococo-system-emulated-network", "rococo-westend-system-emulated-network", "scale-info", diff --git a/bridges/snowbridge/primitives/core/src/outbound.rs b/bridges/snowbridge/primitives/core/src/outbound.rs index bce123878d3a..d1c6b5bb3f82 100644 --- a/bridges/snowbridge/primitives/core/src/outbound.rs +++ b/bridges/snowbridge/primitives/core/src/outbound.rs @@ -235,12 +235,22 @@ mod v1 { /// The amount of tokens to transfer amount: u128, }, + /// Execute a contract on the target chain + Transact { + /// Target contract address + target: H160, + /// The call data to the contract + payload: Vec, + /// The dynamic gas component that needs to be specified when executing the contract + gas_limit: u64, + }, } impl AgentExecuteCommand { fn index(&self) -> u8 { match self { AgentExecuteCommand::TransferToken { .. } => 0, + AgentExecuteCommand::Transact { .. } => 1, } } @@ -256,6 +266,14 @@ mod v1 { Token::Uint(U256::from(*amount)), ])), ]), + AgentExecuteCommand::Transact { target, payload, gas_limit } => ethabi::encode(&[ + Token::Uint(self.index().into()), + Token::Bytes(ethabi::encode(&[ + Token::Address(*target), + Token::Bytes(payload.clone()), + Token::Uint(U256::from(*gas_limit)), + ])), + ]), } } } @@ -386,6 +404,7 @@ impl GasMeter for ConstantGasMeter { // * Assume dest account in ERC20 contract does not yet have a storage slot // * ERC20.transferFrom possibly does other business logic besides updating balances AgentExecuteCommand::TransferToken { .. } => 100_000, + AgentExecuteCommand::Transact { gas_limit, .. } => *gas_limit, }, Command::Upgrade { initializer, .. } => { let initializer_max_gas = match *initializer { @@ -411,3 +430,9 @@ impl GasMeter for () { } pub const ETHER_DECIMALS: u8 = 18; + +#[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] +pub struct TransactInfo { + pub call: Vec, + pub gas_limit: u64, +} diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index ddc36ce8cb61..997aba6678c0 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -7,14 +7,15 @@ mod tests; use core::slice::Iter; -use codec::{Decode, Encode}; +use codec::{Decode, DecodeAll, Encode}; +use crate::outbound::XcmConverterError::SetTopicExpected; use frame_support::{ensure, traits::Get}; use snowbridge_core::{ - outbound::{AgentExecuteCommand, Command, Message, SendMessage}, + outbound::{AgentExecuteCommand, Command, Message, SendMessage, TransactInfo}, ChannelId, ParaId, }; -use sp_core::{H160, H256}; +use sp_core::{hexdisplay::AsBytesRef, H160, H256}; use sp_std::{iter::Peekable, marker::PhantomData, prelude::*}; use xcm::prelude::*; use xcm_executor::traits::{ConvertLocation, ExportXcm}; @@ -154,6 +155,11 @@ enum XcmConverterError { AssetResolutionFailed, InvalidFeeAsset, SetTopicExpected, + TransactDescendOriginExpected, + TransactInvalidContract, + TransactSovereignAccountExpected, + TransactExpected, + UnexpectedInstruction, } macro_rules! match_expression { @@ -175,8 +181,12 @@ impl<'a, Call> XcmConverter<'a, Call> { } fn convert(&mut self) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { - // Get withdraw/deposit and make native tokens create message. - let result = self.native_tokens_unlock_message()?; + let result = match self.peek() { + Ok(DescendOrigin { .. }) => self.transact_message()?, + // Get withdraw/deposit and make native tokens create message. + Ok(WithdrawAsset { .. }) => self.native_tokens_unlock_message()?, + _ => return Err(XcmConverterError::UnexpectedInstruction), + }; // All xcm instructions must be consumed before exit. if self.next().is_ok() { @@ -186,6 +196,43 @@ impl<'a, Call> XcmConverter<'a, Call> { Ok(result) } + fn transact_message(&mut self) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { + let contract_address = if let DescendOrigin(location) = self.next()? { + if let Some(AccountKey20 { key: contract_address, .. }) = location.first() { + contract_address + } else { + return Err(XcmConverterError::TransactInvalidContract) + } + } else { + return Err(XcmConverterError::TransactDescendOriginExpected) + }; + + let call_data = if let Transact { origin_kind, call, .. } = self.next()? { + ensure!( + *origin_kind == OriginKind::SovereignAccount, + XcmConverterError::TransactSovereignAccountExpected + ); + call + } else { + return Err(XcmConverterError::TransactExpected) + }; + + let message = + TransactInfo::decode_all(&mut call_data.clone().into_encoded().as_bytes_ref()) + .map_err(|_| XcmConverterError::TransactExpected)?; + + // Check if there is a SetTopic and skip over it if found. + let topic_id = match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; + Ok(( + AgentExecuteCommand::Transact { + target: contract_address.into(), + payload: message.call, + gas_limit: message.gas_limit, + }, + *topic_id, + )) + } + fn native_tokens_unlock_message( &mut self, ) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs index 8f586a46a75c..f3a127dadfb7 100644 --- a/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs +++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs @@ -49,6 +49,7 @@ decl_test_parachains! { Assets: penpal_runtime::Assets, ForeignAssets: penpal_runtime::ForeignAssets, Balances: penpal_runtime::Balances, + TransactHelper: penpal_runtime::TransactHelper, } }, pub struct PenpalB { diff --git a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml index 89f0d2a9ca6d..ff185ecac221 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/Cargo.toml @@ -42,6 +42,7 @@ emulated-integration-tests-common = { path = "../../../common", default-features rococo-westend-system-emulated-network = { path = "../../../networks/rococo-westend-system" } rococo-system-emulated-network = { path = "../../../networks/rococo-system" } asset-hub-rococo-runtime = { path = "../../../../../runtimes/assets/asset-hub-rococo", default-features = false } +penpal-runtime = { path = "../../../../../../parachains/runtimes/testing/penpal", default-features = false } # Snowbridge snowbridge-core = { path = "../../../../../../../bridges/snowbridge/primitives/core", default-features = false } 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 5e1a2af660b0..6e22ec0d5b72 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 @@ -535,3 +535,111 @@ fn send_token_from_ethereum_to_asset_hub_fail_for_insufficient_fund() { assert_err!(send_inbound_message(make_register_token_message()), Arithmetic(Underflow)); }); } + +#[test] +fn transact_from_penpal_to_ethereum() { + BridgeHubRococo::fund_para_sovereign(PenpalA::para_id().into(), INITIAL_FUND); + + let sudo_origin = ::RuntimeOrigin::root(); + let destination: VersionedLocation = + Rococo::child_location_of(BridgeHubRococo::para_id()).into(); + + // Construct XCM to create an agent for para 2000 + let create_agent_xcm = VersionedXcm::from(Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + DescendOrigin(Parachain(PenpalA::para_id().into()).into()), + Transact { + require_weight_at_most: 3000000000.into(), + origin_kind: OriginKind::Xcm, + call: SnowbridgeControl::Control(ControlCall::CreateAgent {}).encode().into(), + }, + ])); + + // Construct XCM to create a channel for para 2000 + let create_channel_xcm = VersionedXcm::from(Xcm(vec![ + UnpaidExecution { weight_limit: Unlimited, check_origin: None }, + DescendOrigin(Parachain(PenpalA::para_id().into()).into()), + Transact { + require_weight_at_most: 3000000000.into(), + origin_kind: OriginKind::Xcm, + call: SnowbridgeControl::Control(ControlCall::CreateChannel { + mode: OperatingMode::Normal, + }) + .encode() + .into(), + }, + ])); + + // Send XCM message from Relay Chain to create agent/channel for penpal on BH + Rococo::execute_with(|| { + assert_ok!(::XcmPallet::send( + sudo_origin.clone(), + bx!(destination.clone()), + bx!(create_agent_xcm), + )); + + assert_ok!(::XcmPallet::send( + sudo_origin, + bx!(destination), + bx!(create_channel_xcm), + )); + + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + Rococo, + vec![ + RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {}, + ] + ); + }); + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + // Check that the Channel was created + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::EthereumSystem(snowbridge_pallet_system::Event::CreateChannel { + .. + }) => {}, + ] + ); + }); + + PenpalA::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type RuntimeOrigin = ::RuntimeOrigin; + let sender = PenpalASender::get(); + assert_ok!(::TransactHelper::transact_to_ethereum( + RuntimeOrigin::signed(sender), + //contract + hex!("ee9170abfbf9421ad6dd07f6bdec9d89f2b581e0").into(), + //call + hex!("00071468656c6c6f").to_vec(), + //fee + 4_000_000_000, + //gas + 80_000, + )); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::TransactHelper(penpal_runtime::pallets::transact_helper::Event::SentExportMessage { .. }) => {}, + ] + ); + }); + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued { .. }) => {}, + ] + ); + }); +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs index b7d170707cfd..9819a01adc96 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/xcm_config.rs @@ -233,7 +233,9 @@ impl Contains for SafeCallFilter { snowbridge_pallet_system::Call::set_pricing_parameters { .. } | snowbridge_pallet_system::Call::force_update_channel { .. } | snowbridge_pallet_system::Call::force_transfer_native_from_agent { .. } | - snowbridge_pallet_system::Call::set_token_transfer_fees { .. }, + snowbridge_pallet_system::Call::set_token_transfer_fees { .. } | + snowbridge_pallet_system::Call::create_agent { .. } | + snowbridge_pallet_system::Call::create_channel { .. }, ) ) } diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index bf8dcbc24c8d..899318e9463c 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -29,6 +29,7 @@ #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +pub mod pallets; mod weights; pub mod xcm_config; @@ -660,6 +661,8 @@ construct_runtime!( Assets: pallet_assets:: = 50, ForeignAssets: pallet_assets:: = 51, + TransactHelper: crate::pallets::transact_helper = 52, + Sudo: pallet_sudo = 255, } ); diff --git a/cumulus/parachains/runtimes/testing/penpal/src/pallets/mod.rs b/cumulus/parachains/runtimes/testing/penpal/src/pallets/mod.rs new file mode 100644 index 000000000000..aa9825c8dad3 --- /dev/null +++ b/cumulus/parachains/runtimes/testing/penpal/src/pallets/mod.rs @@ -0,0 +1,18 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod transact_helper; diff --git a/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs b/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs new file mode 100644 index 000000000000..3532d92c1aff --- /dev/null +++ b/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs @@ -0,0 +1,118 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + use sp_core::H160; + use xcm::prelude::*; + use xcm_executor::traits::XcmAssetTransfers; + + #[pallet::config] + pub trait Config: frame_system::Config { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type XcmRouter: SendXcm; + type XcmExecutor: ExecuteXcm + XcmAssetTransfers; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// ExportMessage message was sent + SentExportMessage { message_id: XcmHash, sender_cost: Assets, message: Xcm<()> }, + } + + #[pallet::error] + pub enum Error { + InvalidMsg, + FeesNotMet, + SendFailure, + } + + #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] + pub struct TransactInfo { + pub call: Vec, + pub gas_limit: u64, + } + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet + where + [u8; 32]: From<::AccountId>, + { + #[pallet::call_index(0)] + #[pallet::weight(Weight::from_parts(100_000_000, 0))] + pub fn transact_to_ethereum( + origin: OriginFor, + contract: H160, + call: Vec, + fee: u128, + gas_limit: u64, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let dest = Location { + parents: 2, + interior: Junctions::from([GlobalConsensus(Ethereum { chain_id: 11155111 })]), + }; + let transact = TransactInfo { call, gas_limit }; + + let inner_message = Xcm(vec![ + DescendOrigin(Junctions::from(AccountKey20 { + network: None, + key: contract.into(), + })), + Transact { + origin_kind: OriginKind::SovereignAccount, + require_weight_at_most: Weight::default(), + call: transact.encode().into(), + }, + SetTopic([0; 32]), + ]); + + let (ticket, price) = + validate_send::(dest.clone(), inner_message.clone()) + .map_err(|_| Error::::InvalidMsg)?; + ensure!(price.len() > 0, Error::::FeesNotMet); + + let mut fees: Assets = (Parent, fee).into(); + fees.push(price.get(0).unwrap().clone()); + + let origin = Location::from(AccountId32 { network: None, id: who.into() }); + + T::XcmExecutor::charge_fees(origin, fees.clone().into()) + .map_err(|_| Error::::FeesNotMet)?; + + let message_id = T::XcmRouter::deliver(ticket).map_err(|_| Error::::SendFailure)?; + + Self::deposit_event(Event::SentExportMessage { + message_id, + sender_cost: fees.into(), + message: inner_message, + }); + Ok(()) + } + } +} diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index 3fad47576fd6..38b1670845f3 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -32,17 +32,19 @@ use frame_support::{ parameter_types, traits::{ fungibles::{self, Balanced, Credit}, - ConstU32, Contains, ContainsPair, Everything, Get, Nothing, + ConstU32, Contains, ContainsPair, Equals, Everything, Get, Nothing, }, weights::Weight, + PalletId, }; use frame_system::EnsureRoot; use pallet_asset_tx_payment::HandleCredit; use pallet_assets::Instance1; use pallet_xcm::XcmPassthrough; +use parachains_common::xcm_config::{AllSiblingSystemParachains, RelayOrOtherSystemParachains}; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::impls::ToAuthor; -use sp_runtime::traits::Zero; +use sp_runtime::traits::{AccountIdConversion, Zero}; use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; use xcm::latest::prelude::*; #[allow(deprecated)] @@ -56,13 +58,15 @@ use xcm_builder::{ SovereignSignedViaLocation, StartsWith, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, }; +use xcm_builder::{SovereignPaidRemoteExporter, XcmFeeManagerFromComponents, XcmFeeToAccount}; use xcm_executor::{traits::JustTry, XcmExecutor}; parameter_types! { pub const RelayLocation: Location = Location::parent(); - pub const RelayNetwork: Option = None; + pub const RelayNetwork: NetworkId = NetworkId::Rococo; pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); - pub UniversalLocation: InteriorLocation = [Parachain(ParachainInfo::parachain_id().into())].into(); + pub UniversalLocation: InteriorLocation = + [GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into(); } /// Type for specifying how a `Location` can be converted into an `AccountId`. This is used @@ -298,6 +302,9 @@ parameter_types! { [xcm::v3::Junction::PalletInstance(50), xcm::v3::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into())] ); pub EthereumLocation: Location = Location::new(2, [GlobalConsensus(EthereumNetwork::get())]); + pub RelayTreasuryLocation: Location = (Parent, PalletInstance(18)).into(); + pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry"); + pub TreasuryAccount: AccountId = TreasuryPalletId::get().into_account_truncating(); } /// Accepts asset with ID `AssetLocation` and is coming from `Origin` chain. @@ -321,6 +328,11 @@ pub type Reserves = ( pub type TrustedTeleporters = (AssetFromChain,); +pub type WaivedLocations = ( + RelayOrOtherSystemParachains, + Equals, +); + pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; @@ -344,7 +356,10 @@ impl xcm_executor::Config for XcmConfig { type MaxAssetsIntoHolding = MaxAssetsIntoHolding; type AssetLocker = (); type AssetExchanger = (); - type FeeManager = (); + type FeeManager = XcmFeeManagerFromComponents< + WaivedLocations, + XcmFeeToAccount, + >; type MessageExporter = (); type UniversalAliases = Nothing; type CallDispatcher = RuntimeCall; @@ -408,3 +423,43 @@ impl pallet_assets::BenchmarkHelper for XcmBenchmarkHelper { xcm::v3::Location::new(1, [xcm::v3::Junction::Parachain(id)]) } } + +impl crate::pallets::transact_helper::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type XcmRouter = SovereignPaidRemoteExporter< + to_ethereum::EthereumNetworkExportTable, + XcmpQueue, + UniversalLocation, + >; + type XcmExecutor = XcmExecutor; +} + +pub mod to_ethereum { + use super::*; + use xcm_builder::NetworkExportTableItem; + + parameter_types! { + pub const DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000; + pub storage BridgeHubEthereumBaseFee: Balance = DefaultBridgeHubEthereumBaseFee::get(); + pub SiblingBridgeHub: Location = Location::new(1, [Parachain(1013)]); + + pub BridgeTable: Vec = sp_std::vec![ + NetworkExportTableItem::new( + EthereumNetwork::get(), + Some(sp_std::vec![Here]), + SiblingBridgeHub::get(), + Some(( + RelayLocation::get(), + BridgeHubEthereumBaseFee::get(), + ).into()) + ), + ]; + + pub EthereumBridgeTable: Vec = + Vec::new().into_iter() + .chain(BridgeTable::get()) + .collect(); + } + + pub type EthereumNetworkExportTable = xcm_builder::NetworkExportTable; +} From 8d98aa3dbc45e1c0a39b06abc5752f8edf1bebc9 Mon Sep 17 00:00:00 2001 From: ron Date: Wed, 21 Feb 2024 09:41:03 +0800 Subject: [PATCH 02/16] Fix ci --- bridges/snowbridge/primitives/core/src/outbound.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bridges/snowbridge/primitives/core/src/outbound.rs b/bridges/snowbridge/primitives/core/src/outbound.rs index d1c6b5bb3f82..ad13d19c684a 100644 --- a/bridges/snowbridge/primitives/core/src/outbound.rs +++ b/bridges/snowbridge/primitives/core/src/outbound.rs @@ -3,6 +3,7 @@ use frame_support::PalletError; use scale_info::TypeInfo; use sp_arithmetic::traits::{BaseArithmetic, Unsigned}; use sp_core::{RuntimeDebug, H256}; +use sp_std::vec::Vec; pub use v1::{AgentExecuteCommand, Command, Initializer, Message, OperatingMode, QueuedMessage}; /// Enqueued outbound messages need to be versioned to prevent data corruption From 993848edcac7b1831436b0cecdc375e305d61fe0 Mon Sep 17 00:00:00 2001 From: ron Date: Wed, 21 Feb 2024 10:52:58 +0800 Subject: [PATCH 03/16] Remove descendOrigin and add target to TransactInfo --- .../primitives/core/src/outbound.rs | 3 +- .../primitives/router/src/outbound/mod.rs | 31 ++++++------------- .../penpal/src/pallets/transact_helper.rs | 10 +++--- 3 files changed, 16 insertions(+), 28 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/outbound.rs b/bridges/snowbridge/primitives/core/src/outbound.rs index ad13d19c684a..ef08b330f56a 100644 --- a/bridges/snowbridge/primitives/core/src/outbound.rs +++ b/bridges/snowbridge/primitives/core/src/outbound.rs @@ -2,7 +2,7 @@ use codec::{Decode, Encode}; use frame_support::PalletError; use scale_info::TypeInfo; use sp_arithmetic::traits::{BaseArithmetic, Unsigned}; -use sp_core::{RuntimeDebug, H256}; +use sp_core::{RuntimeDebug, H160, H256}; use sp_std::vec::Vec; pub use v1::{AgentExecuteCommand, Command, Initializer, Message, OperatingMode, QueuedMessage}; @@ -434,6 +434,7 @@ pub const ETHER_DECIMALS: u8 = 18; #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] pub struct TransactInfo { + pub target: H160, pub call: Vec, pub gas_limit: u64, } diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index 997aba6678c0..d6c5888bca75 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -9,13 +9,13 @@ use core::slice::Iter; use codec::{Decode, DecodeAll, Encode}; -use crate::outbound::XcmConverterError::SetTopicExpected; use frame_support::{ensure, traits::Get}; +use frame_system::unique; use snowbridge_core::{ outbound::{AgentExecuteCommand, Command, Message, SendMessage, TransactInfo}, ChannelId, ParaId, }; -use sp_core::{hexdisplay::AsBytesRef, H160, H256}; +use sp_core::{H160, H256}; use sp_std::{iter::Peekable, marker::PhantomData, prelude::*}; use xcm::prelude::*; use xcm_executor::traits::{ConvertLocation, ExportXcm}; @@ -155,10 +155,9 @@ enum XcmConverterError { AssetResolutionFailed, InvalidFeeAsset, SetTopicExpected, - TransactDescendOriginExpected, - TransactInvalidContract, - TransactSovereignAccountExpected, TransactExpected, + TransactSovereignAccountExpected, + TransactDecodeFailed, UnexpectedInstruction, } @@ -182,7 +181,7 @@ impl<'a, Call> XcmConverter<'a, Call> { fn convert(&mut self) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { let result = match self.peek() { - Ok(DescendOrigin { .. }) => self.transact_message()?, + Ok(Transact { .. }) => self.transact_message()?, // Get withdraw/deposit and make native tokens create message. Ok(WithdrawAsset { .. }) => self.native_tokens_unlock_message()?, _ => return Err(XcmConverterError::UnexpectedInstruction), @@ -197,16 +196,6 @@ impl<'a, Call> XcmConverter<'a, Call> { } fn transact_message(&mut self) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { - let contract_address = if let DescendOrigin(location) = self.next()? { - if let Some(AccountKey20 { key: contract_address, .. }) = location.first() { - contract_address - } else { - return Err(XcmConverterError::TransactInvalidContract) - } - } else { - return Err(XcmConverterError::TransactDescendOriginExpected) - }; - let call_data = if let Transact { origin_kind, call, .. } = self.next()? { ensure!( *origin_kind == OriginKind::SovereignAccount, @@ -217,15 +206,15 @@ impl<'a, Call> XcmConverter<'a, Call> { return Err(XcmConverterError::TransactExpected) }; - let message = - TransactInfo::decode_all(&mut call_data.clone().into_encoded().as_bytes_ref()) - .map_err(|_| XcmConverterError::TransactExpected)?; + let message = TransactInfo::decode_all(&mut call_data.clone().into_encoded().as_slice()) + .map_err(|_| XcmConverterError::TransactDecodeFailed)?; // Check if there is a SetTopic and skip over it if found. - let topic_id = match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; + let message_hash = unique(&message); + let topic_id = match_expression!(self.next()?, SetTopic(id), id).unwrap_or(&message_hash); Ok(( AgentExecuteCommand::Transact { - target: contract_address.into(), + target: message.target, payload: message.call, gas_limit: message.gas_limit, }, diff --git a/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs b/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs index 3532d92c1aff..f01b6fa75f37 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs @@ -50,6 +50,7 @@ pub mod pallet { #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] pub struct TransactInfo { + pub target: H160, pub call: Vec, pub gas_limit: u64, } @@ -66,7 +67,7 @@ pub mod pallet { #[pallet::weight(Weight::from_parts(100_000_000, 0))] pub fn transact_to_ethereum( origin: OriginFor, - contract: H160, + target: H160, call: Vec, fee: u128, gas_limit: u64, @@ -77,18 +78,15 @@ pub mod pallet { parents: 2, interior: Junctions::from([GlobalConsensus(Ethereum { chain_id: 11155111 })]), }; - let transact = TransactInfo { call, gas_limit }; + let transact = TransactInfo { target, call, gas_limit }; let inner_message = Xcm(vec![ - DescendOrigin(Junctions::from(AccountKey20 { - network: None, - key: contract.into(), - })), Transact { origin_kind: OriginKind::SovereignAccount, require_weight_at_most: Weight::default(), call: transact.encode().into(), }, + // Optional only for trace SetTopic([0; 32]), ]); From ad38a2eb88b6a5adecd412074ecfc7a51168d6fb Mon Sep 17 00:00:00 2001 From: ron Date: Wed, 21 Feb 2024 11:11:30 +0800 Subject: [PATCH 04/16] Fix breaking test --- .../snowbridge/primitives/router/src/outbound/tests.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/outbound/tests.rs b/bridges/snowbridge/primitives/router/src/outbound/tests.rs index 111243bb45a7..0033f466e897 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/tests.rs @@ -631,7 +631,7 @@ fn xcm_converter_with_fees_greater_than_reserve_fails() { } #[test] -fn xcm_converter_convert_with_empty_xcm_yields_unexpected_end_of_xcm() { +fn xcm_converter_convert_with_empty_xcm_yields_unexpected_instruction() { let network = BridgedNetwork::get(); let message: Xcm<()> = vec![].into(); @@ -639,7 +639,7 @@ fn xcm_converter_convert_with_empty_xcm_yields_unexpected_end_of_xcm() { let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); - assert_eq!(result.err(), Some(XcmConverterError::UnexpectedEndOfXcm)); + assert_eq!(result.err(), Some(XcmConverterError::UnexpectedInstruction)); } #[test] @@ -675,7 +675,7 @@ fn xcm_converter_convert_with_extra_instructions_yields_end_of_xcm_message_expec } #[test] -fn xcm_converter_convert_without_withdraw_asset_yields_withdraw_expected() { +fn xcm_converter_convert_without_withdraw_asset_yields_unexpected_instruction() { let network = BridgedNetwork::get(); let token_address: [u8; 20] = hex!("1000000000000000000000000000000000000000"); @@ -701,7 +701,7 @@ fn xcm_converter_convert_without_withdraw_asset_yields_withdraw_expected() { let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); - assert_eq!(result.err(), Some(XcmConverterError::WithdrawAssetExpected)); + assert_eq!(result.err(), Some(XcmConverterError::UnexpectedInstruction)); } #[test] From 39f3a0df7d000459b532e668d251539e77b0f394 Mon Sep 17 00:00:00 2001 From: ron Date: Wed, 21 Feb 2024 13:46:58 +0800 Subject: [PATCH 05/16] Fix ci --- cumulus/parachains/runtimes/testing/penpal/src/pallets/mod.rs | 3 --- .../runtimes/testing/penpal/src/pallets/transact_helper.rs | 1 + cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs | 1 + 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cumulus/parachains/runtimes/testing/penpal/src/pallets/mod.rs b/cumulus/parachains/runtimes/testing/penpal/src/pallets/mod.rs index aa9825c8dad3..06ec58b9a1d8 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/pallets/mod.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/pallets/mod.rs @@ -12,7 +12,4 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -#![cfg_attr(not(feature = "std"), no_std)] - pub mod transact_helper; diff --git a/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs b/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs index f01b6fa75f37..12bc08d0d4bf 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs @@ -21,6 +21,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use sp_core::H160; + use sp_std::{vec, vec::Vec}; use xcm::prelude::*; use xcm_executor::traits::XcmAssetTransfers; diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index 38b1670845f3..91bcf16b7a68 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -45,6 +45,7 @@ use parachains_common::xcm_config::{AllSiblingSystemParachains, RelayOrOtherSyst use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::impls::ToAuthor; use sp_runtime::traits::{AccountIdConversion, Zero}; +use sp_std::vec::Vec; use testnet_parachains_constants::rococo::snowbridge::EthereumNetwork; use xcm::latest::prelude::*; #[allow(deprecated)] From 07ea7e80dbf5164ccc4635d6e584f213e6716376 Mon Sep 17 00:00:00 2001 From: ron Date: Wed, 21 Feb 2024 20:40:18 +0800 Subject: [PATCH 06/16] No waived locations --- .../bridges/bridge-hub-rococo/src/tests/snowbridge.rs | 4 ++-- .../runtimes/testing/penpal/src/xcm_config.rs | 11 ++--------- 2 files changed, 4 insertions(+), 11 deletions(-) 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 6e22ec0d5b72..03670eb0866d 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 @@ -544,7 +544,7 @@ fn transact_from_penpal_to_ethereum() { let destination: VersionedLocation = Rococo::child_location_of(BridgeHubRococo::para_id()).into(); - // Construct XCM to create an agent for para 2000 + // Construct XCM to create an agent for penpal let create_agent_xcm = VersionedXcm::from(Xcm(vec![ UnpaidExecution { weight_limit: Unlimited, check_origin: None }, DescendOrigin(Parachain(PenpalA::para_id().into()).into()), @@ -555,7 +555,7 @@ fn transact_from_penpal_to_ethereum() { }, ])); - // Construct XCM to create a channel for para 2000 + // Construct XCM to create a channel for penpal let create_channel_xcm = VersionedXcm::from(Xcm(vec![ UnpaidExecution { weight_limit: Unlimited, check_origin: None }, DescendOrigin(Parachain(PenpalA::para_id().into()).into()), diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index 91bcf16b7a68..0d4a6aa97f5a 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -32,7 +32,7 @@ use frame_support::{ parameter_types, traits::{ fungibles::{self, Balanced, Credit}, - ConstU32, Contains, ContainsPair, Equals, Everything, Get, Nothing, + ConstU32, Contains, ContainsPair, Everything, Get, Nothing, }, weights::Weight, PalletId, @@ -41,7 +41,6 @@ use frame_system::EnsureRoot; use pallet_asset_tx_payment::HandleCredit; use pallet_assets::Instance1; use pallet_xcm::XcmPassthrough; -use parachains_common::xcm_config::{AllSiblingSystemParachains, RelayOrOtherSystemParachains}; use polkadot_parachain_primitives::primitives::Sibling; use polkadot_runtime_common::impls::ToAuthor; use sp_runtime::traits::{AccountIdConversion, Zero}; @@ -303,7 +302,6 @@ parameter_types! { [xcm::v3::Junction::PalletInstance(50), xcm::v3::Junction::GeneralIndex(TELEPORTABLE_ASSET_ID.into())] ); pub EthereumLocation: Location = Location::new(2, [GlobalConsensus(EthereumNetwork::get())]); - pub RelayTreasuryLocation: Location = (Parent, PalletInstance(18)).into(); pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry"); pub TreasuryAccount: AccountId = TreasuryPalletId::get().into_account_truncating(); } @@ -329,11 +327,6 @@ pub type Reserves = ( pub type TrustedTeleporters = (AssetFromChain,); -pub type WaivedLocations = ( - RelayOrOtherSystemParachains, - Equals, -); - pub struct XcmConfig; impl xcm_executor::Config for XcmConfig { type RuntimeCall = RuntimeCall; @@ -358,7 +351,7 @@ impl xcm_executor::Config for XcmConfig { type AssetLocker = (); type AssetExchanger = (); type FeeManager = XcmFeeManagerFromComponents< - WaivedLocations, + Nothing, XcmFeeToAccount, >; type MessageExporter = (); From e8c9324ec4b9f93ce9594644a35257d979f83b3a Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 29 Feb 2024 23:51:37 +0800 Subject: [PATCH 07/16] Improve error handling --- bridges/snowbridge/primitives/router/src/outbound/mod.rs | 7 ++++--- bridges/snowbridge/primitives/router/src/outbound/tests.rs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index d6c5888bca75..d8b6d5f87f3a 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -181,11 +181,12 @@ impl<'a, Call> XcmConverter<'a, Call> { fn convert(&mut self) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { let result = match self.peek() { - Ok(Transact { .. }) => self.transact_message()?, + Ok(Transact { .. }) => self.transact_message(), // Get withdraw/deposit and make native tokens create message. - Ok(WithdrawAsset { .. }) => self.native_tokens_unlock_message()?, + Ok(WithdrawAsset { .. }) => self.native_tokens_unlock_message(), + Err(e) => Err(e), _ => return Err(XcmConverterError::UnexpectedInstruction), - }; + }?; // All xcm instructions must be consumed before exit. if self.next().is_ok() { diff --git a/bridges/snowbridge/primitives/router/src/outbound/tests.rs b/bridges/snowbridge/primitives/router/src/outbound/tests.rs index 0033f466e897..63a241ebf291 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/tests.rs @@ -639,7 +639,7 @@ fn xcm_converter_convert_with_empty_xcm_yields_unexpected_instruction() { let mut converter = XcmConverter::new(&message, &network); let result = converter.convert(); - assert_eq!(result.err(), Some(XcmConverterError::UnexpectedInstruction)); + assert_eq!(result.err(), Some(XcmConverterError::UnexpectedEndOfXcm)); } #[test] From b1ec15c395785807e14c91671b128f1b3ba30d31 Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 4 Mar 2024 09:53:33 +0800 Subject: [PATCH 08/16] Fix format --- .../parachains/runtimes/testing/penpal/src/xcm_config.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index bbfd7eb5311b..fa111d14f33a 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -54,10 +54,10 @@ use xcm_builder::{ FungibleAdapter, FungiblesAdapter, IsConcrete, LocalMint, NativeAsset, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, - SovereignSignedViaLocation, StartsWith, TakeWeightCredit, TrailingSetTopicAsId, - UsingComponents, WithComputedOrigin, WithUniqueTopic, + SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WithComputedOrigin, WithUniqueTopic, + XcmFeeManagerFromComponents, XcmFeeToAccount, }; -use xcm_builder::{SovereignPaidRemoteExporter, XcmFeeManagerFromComponents, XcmFeeToAccount}; use xcm_executor::{traits::JustTry, XcmExecutor}; parameter_types! { From c05d2c31ac3bc9e10146c149aad36add220a9f4c Mon Sep 17 00:00:00 2001 From: ron Date: Tue, 16 Apr 2024 08:51:40 +0800 Subject: [PATCH 09/16] Separate remote fee from BridgeHubEthereumBaseFee --- .../primitives/core/src/outbound.rs | 20 +++++++++++-------- .../primitives/router/src/outbound/mod.rs | 15 +++++++++----- .../bridge-hub-rococo/src/tests/snowbridge.rs | 2 +- .../penpal/src/pallets/transact_helper.rs | 3 ++- .../runtimes/testing/penpal/src/xcm_config.rs | 2 +- 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/outbound.rs b/bridges/snowbridge/primitives/core/src/outbound.rs index 25943d21440d..557ffe77a017 100644 --- a/bridges/snowbridge/primitives/core/src/outbound.rs +++ b/bridges/snowbridge/primitives/core/src/outbound.rs @@ -247,6 +247,8 @@ mod v1 { payload: Vec, /// The dynamic gas component that needs to be specified when executing the contract gas_limit: u64, + /// The fee to cover the delivery cost + fee: u128, }, } @@ -270,14 +272,15 @@ mod v1 { Token::Uint(U256::from(*amount)), ])), ]), - AgentExecuteCommand::Transact { target, payload, gas_limit } => ethabi::encode(&[ - Token::Uint(self.index().into()), - Token::Bytes(ethabi::encode(&[ - Token::Address(*target), - Token::Bytes(payload.clone()), - Token::Uint(U256::from(*gas_limit)), - ])), - ]), + AgentExecuteCommand::Transact { target, payload, gas_limit, .. } => + ethabi::encode(&[ + Token::Uint(self.index().into()), + Token::Bytes(ethabi::encode(&[ + Token::Address(*target), + Token::Bytes(payload.clone()), + Token::Uint(U256::from(*gas_limit)), + ])), + ]), } } } @@ -442,4 +445,5 @@ pub struct TransactInfo { pub target: H160, pub call: Vec, pub gas_limit: u64, + pub fee: u128, } diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index 6060fec29a4b..6c3b8c71ff2d 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -108,17 +108,21 @@ where let outbound_message = Message { id: Some(message_id.into()), channel_id, - command: Command::AgentExecute { agent_id, command: agent_execute_command }, + command: Command::AgentExecute { agent_id, command: agent_execute_command.clone() }, }; // validate the message - let (ticket, fee) = OutboundQueue::validate(&outbound_message).map_err(|err| { + let (ticket, fees) = OutboundQueue::validate(&outbound_message).map_err(|err| { log::error!(target: "xcm::ethereum_blob_exporter", "OutboundQueue validation of message failed. {err:?}"); SendError::Unroutable })?; - - // convert fee to Asset - let fee = Asset::from((Location::parent(), fee.total())).into(); + let fee = match agent_execute_command { + AgentExecuteCommand::Transact { fee, .. } => { + ensure!(fee > fees.remote, SendError::Fees); + Ok::(Asset::from((Location::parent(), fees.local)).into()) + }, + _ => Ok::(Asset::from((Location::parent(), fees.total())).into()), + }?; Ok(((ticket.encode(), message_id), fee)) } @@ -216,6 +220,7 @@ impl<'a, Call> XcmConverter<'a, Call> { target: message.target, payload: message.call, gas_limit: message.gas_limit, + fee: message.fee, }, *topic_id, )) 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 6c5fefbf023c..147681f16de6 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 @@ -650,7 +650,7 @@ fn transact_from_penpal_to_ethereum() { //call hex!("00071468656c6c6f").to_vec(), //fee - 4_000_000_000, + 2_750_872_500_000, //gas 80_000, )); diff --git a/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs b/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs index 07948d13fef2..6ca0011eb84d 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs @@ -54,6 +54,7 @@ pub mod pallet { pub target: H160, pub call: Vec, pub gas_limit: u64, + pub fee: u128, } #[pallet::hooks] @@ -79,7 +80,7 @@ pub mod pallet { parents: 2, interior: Junctions::from([GlobalConsensus(Ethereum { chain_id: 11155111 })]), }; - let transact = TransactInfo { target, call, gas_limit }; + let transact = TransactInfo { target, call, gas_limit, fee }; let inner_message = Xcm(vec![ Transact { diff --git a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs index 05481b13f950..2816918476b5 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/xcm_config.rs @@ -437,7 +437,7 @@ pub mod to_ethereum { use xcm_builder::NetworkExportTableItem; parameter_types! { - pub storage DefaultBridgeHubEthereumBaseFee: Balance = 2_750_872_500_000; + pub storage DefaultBridgeHubEthereumBaseFee: Balance = 4_000_000_000; pub storage BridgeHubEthereumBaseFee: Balance = DefaultBridgeHubEthereumBaseFee::get(); pub storage SiblingBridgeHub: Location = Location::new(1, [Parachain(1013)]); pub storage EthereumNetwork: NetworkId = NetworkId::Ethereum { chain_id: 11155111 }; From 569d638d429944fc8d4e2d7db3b653d565879f86 Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 22 Apr 2024 16:27:27 +0800 Subject: [PATCH 10/16] Improve test with balance check --- bridges/snowbridge/pallets/system/src/lib.rs | 2 +- .../bridge-hub-rococo/src/tests/snowbridge.rs | 98 +++++-------------- 2 files changed, 28 insertions(+), 72 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 39c73e3630e7..715bb764cd34 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -99,7 +99,7 @@ where } /// Hash the location to produce an agent id -fn agent_id_of(location: &Location) -> Result { +pub fn agent_id_of(location: &Location) -> Result { T::AgentIdOf::convert_location(location).ok_or(Error::::LocationConversionFailed.into()) } 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 147681f16de6..b2fd0db3264e 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 @@ -18,9 +18,10 @@ use codec::{Decode, Encode}; use emulated_integration_tests_common::xcm_emulator::ConvertLocation; use frame_support::{pallet_prelude::TypeInfo, traits::fungibles::Mutate}; use hex_literal::hex; +use penpal_runtime::xcm_config::to_ethereum::BridgeHubEthereumBaseFee; use rococo_system_emulated_network::penpal_emulated_chain::CustomizableAssetFromSystemAssetHub; use rococo_westend_system_emulated_network::BridgeHubRococoParaSender as BridgeHubRococoSender; -use snowbridge_core::{inbound::InboundQueueFixture, outbound::OperatingMode}; +use snowbridge_core::{inbound::InboundQueueFixture, outbound::OperatingMode, Channel, ChannelId}; use snowbridge_pallet_inbound_queue_fixtures::{ register_token::make_register_token_message, send_token::make_send_token_message, send_token_to_penpal::make_send_token_to_penpal_message, @@ -566,95 +567,50 @@ fn register_weth_token_in_asset_hub_fail_for_insufficient_fee() { fn transact_from_penpal_to_ethereum() { BridgeHubRococo::fund_para_sovereign(PenpalA::para_id().into(), INITIAL_FUND); - let sudo_origin = ::RuntimeOrigin::root(); - let destination: VersionedLocation = - Rococo::child_location_of(BridgeHubRococo::para_id()).into(); - - // Construct XCM to create an agent for penpal - let create_agent_xcm = VersionedXcm::from(Xcm(vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - DescendOrigin(Parachain(PenpalA::para_id().into()).into()), - Transact { - require_weight_at_most: 3000000000.into(), - origin_kind: OriginKind::Xcm, - call: SnowbridgeControl::Control(ControlCall::CreateAgent {}).encode().into(), - }, - ])); - - // Construct XCM to create a channel for penpal - let create_channel_xcm = VersionedXcm::from(Xcm(vec![ - UnpaidExecution { weight_limit: Unlimited, check_origin: None }, - DescendOrigin(Parachain(PenpalA::para_id().into()).into()), - Transact { - require_weight_at_most: 3000000000.into(), - origin_kind: OriginKind::Xcm, - call: SnowbridgeControl::Control(ControlCall::CreateChannel { - mode: OperatingMode::Normal, - }) - .encode() - .into(), - }, - ])); - - // Send XCM message from Relay Chain to create agent/channel for penpal on BH - Rococo::execute_with(|| { - assert_ok!(::XcmPallet::send( - sudo_origin.clone(), - bx!(destination.clone()), - bx!(create_agent_xcm), - )); - - assert_ok!(::XcmPallet::send( - sudo_origin, - bx!(destination), - bx!(create_channel_xcm), - )); - - type RuntimeEvent = ::RuntimeEvent; - - assert_expected_events!( - Rococo, - vec![ - RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {}, - ] - ); - }); - + // Create agent and channel BridgeHubRococo::execute_with(|| { - type RuntimeEvent = ::RuntimeEvent; - - // Check that the Channel was created - assert_expected_events!( - BridgeHubRococo, - vec![ - RuntimeEvent::EthereumSystem(snowbridge_pallet_system::Event::CreateChannel { - .. - }) => {}, - ] - ); + type Runtime = ::Runtime; + let agent_id = snowbridge_pallet_system::agent_id_of::(&Location::new( + 1, + [Parachain(PenpalA::para_id().into())], + )) + .unwrap(); + snowbridge_pallet_system::Agents::::insert(agent_id, ()); + let channel_id: ChannelId = PenpalA::para_id().into(); + snowbridge_pallet_system::Channels::::insert( + channel_id, + Channel { agent_id, para_id: PenpalA::para_id() }, + ) }); PenpalA::execute_with(|| { type RuntimeEvent = ::RuntimeEvent; type RuntimeOrigin = ::RuntimeOrigin; let sender = PenpalASender::get(); + let initial_fund = 4_000_000_000_000; assert_ok!(::ForeignAssets::mint_into( xcm::v3::Location::parent(), (&sender.clone()).into(), - 4_000_000_000_000, + initial_fund, )); + let remote_cost = 2_750_872_500_000; assert_ok!(::TransactHelper::transact_to_ethereum( - RuntimeOrigin::signed(sender), + RuntimeOrigin::signed(sender.clone()), //contract hex!("ee9170abfbf9421ad6dd07f6bdec9d89f2b581e0").into(), //call hex!("00071468656c6c6f").to_vec(), - //fee - 2_750_872_500_000, + //The fee here in DOT should cover the remote execution cost on Ethereum + remote_cost, //gas 80_000, )); - + let balance_after = ::ForeignAssets::balance( + xcm::v3::Location::parent(), + sender.clone(), + ); + let total_cost = initial_fund - balance_after; + assert_eq!(total_cost > remote_cost + BridgeHubEthereumBaseFee::get(), true); assert_expected_events!( PenpalA, vec![ From f4b6dba04744f6c31a589fe7ccb83cdc3be886f5 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 25 Apr 2024 13:18:03 +0800 Subject: [PATCH 11/16] AgentId as user input --- .../primitives/core/src/outbound.rs | 45 +++++----- .../primitives/router/src/outbound/mod.rs | 77 +++++++++------- .../primitives/router/src/outbound/tests.rs | 87 +++++++++++-------- .../bridge-hub-rococo/src/tests/snowbridge.rs | 12 +-- .../penpal/src/pallets/transact_helper.rs | 7 +- 5 files changed, 131 insertions(+), 97 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/outbound.rs b/bridges/snowbridge/primitives/core/src/outbound.rs index 557ffe77a017..988d3eadad37 100644 --- a/bridges/snowbridge/primitives/core/src/outbound.rs +++ b/bridges/snowbridge/primitives/core/src/outbound.rs @@ -140,6 +140,19 @@ mod v1 { // Fee multiplier multiplier: UD60x18, }, + /// Execute a contract on the target chain + Transact { + /// ID of the agent + agent_id: H256, + /// Target contract address + target: H160, + /// The call data to the contract + payload: Vec, + /// The dynamic gas component that needs to be specified when executing the contract + gas_limit: u64, + /// The fee to cover the delivery cost + fee: u128, + }, } impl Command { @@ -155,6 +168,7 @@ mod v1 { Command::TransferNativeFromAgent { .. } => 6, Command::SetTokenTransferFees { .. } => 7, Command::SetPricingParameters { .. } => 8, + Command::Transact { .. } => 9, } } @@ -212,6 +226,13 @@ mod v1 { Token::Uint(U256::from(*delivery_cost)), Token::Uint(multiplier.clone().into_inner()), ])]), + Command::Transact { agent_id, target, payload, gas_limit, .. } => + ethabi::encode(&[Token::Tuple(vec![ + Token::FixedBytes(agent_id.as_bytes().to_owned()), + Token::Address(*target), + Token::Bytes(payload.clone()), + Token::Uint(U256::from(*gas_limit)), + ])]), } } } @@ -239,24 +260,12 @@ mod v1 { /// The amount of tokens to transfer amount: u128, }, - /// Execute a contract on the target chain - Transact { - /// Target contract address - target: H160, - /// The call data to the contract - payload: Vec, - /// The dynamic gas component that needs to be specified when executing the contract - gas_limit: u64, - /// The fee to cover the delivery cost - fee: u128, - }, } impl AgentExecuteCommand { fn index(&self) -> u8 { match self { AgentExecuteCommand::TransferToken { .. } => 0, - AgentExecuteCommand::Transact { .. } => 1, } } @@ -272,15 +281,6 @@ mod v1 { Token::Uint(U256::from(*amount)), ])), ]), - AgentExecuteCommand::Transact { target, payload, gas_limit, .. } => - ethabi::encode(&[ - Token::Uint(self.index().into()), - Token::Bytes(ethabi::encode(&[ - Token::Address(*target), - Token::Bytes(payload.clone()), - Token::Uint(U256::from(*gas_limit)), - ])), - ]), } } } @@ -413,7 +413,6 @@ impl GasMeter for ConstantGasMeter { // * Assume dest account in ERC20 contract does not yet have a storage slot // * ERC20.transferFrom possibly does other business logic besides updating balances AgentExecuteCommand::TransferToken { .. } => 100_000, - AgentExecuteCommand::Transact { gas_limit, .. } => *gas_limit, }, Command::Upgrade { initializer, .. } => { let initializer_max_gas = match *initializer { @@ -426,6 +425,7 @@ impl GasMeter for ConstantGasMeter { }, Command::SetTokenTransferFees { .. } => 60_000, Command::SetPricingParameters { .. } => 60_000, + Command::Transact { gas_limit, .. } => *gas_limit, } } } @@ -442,6 +442,7 @@ pub const ETHER_DECIMALS: u8 = 18; #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] pub struct TransactInfo { + pub agent_id: H256, pub target: H160, pub call: Vec, pub gas_limit: u64, diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index 6c3b8c71ff2d..f5f28cedb903 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -88,36 +88,28 @@ where SendError::MissingArgument })?; - let mut converter = XcmConverter::new(&message, &expected_network); - let (agent_execute_command, message_id) = converter.convert().map_err(|err|{ + let mut converter = XcmConverter::::new( + &message, + expected_network, + para_id.into(), + ); + let (command, message_id) = converter.convert().map_err(|err|{ log::error!(target: "xcm::ethereum_blob_exporter", "unroutable due to pattern matching error '{err:?}'."); SendError::Unroutable })?; - let source_location = Location::new(1, local_sub.clone()); - let agent_id = match AgentHashedDescription::convert_location(&source_location) { - Some(id) => id, - None => { - log::error!(target: "xcm::ethereum_blob_exporter", "unroutable due to not being able to create agent id. '{source_location:?}'"); - return Err(SendError::Unroutable) - }, - }; - let channel_id: ChannelId = ParaId::from(para_id).into(); - let outbound_message = Message { - id: Some(message_id.into()), - channel_id, - command: Command::AgentExecute { agent_id, command: agent_execute_command.clone() }, - }; + let outbound_message = + Message { id: Some(message_id.into()), channel_id, command: command.clone() }; // validate the message let (ticket, fees) = OutboundQueue::validate(&outbound_message).map_err(|err| { log::error!(target: "xcm::ethereum_blob_exporter", "OutboundQueue validation of message failed. {err:?}"); SendError::Unroutable })?; - let fee = match agent_execute_command { - AgentExecuteCommand::Transact { fee, .. } => { + let fee = match command { + Command::Transact { fee, .. } => { ensure!(fee > fees.remote, SendError::Fees); Ok::(Asset::from((Location::parent(), fees.local)).into()) }, @@ -163,6 +155,7 @@ enum XcmConverterError { TransactSovereignAccountExpected, TransactDecodeFailed, UnexpectedInstruction, + Unroutable, } macro_rules! match_expression { @@ -174,17 +167,28 @@ macro_rules! match_expression { }; } -struct XcmConverter<'a, Call> { +struct XcmConverter<'a, AgentHashedDescription, Call> { iter: Peekable>>, - ethereum_network: &'a NetworkId, + ethereum_network: NetworkId, + para_id: ParaId, + _marker: PhantomData, } -impl<'a, Call> XcmConverter<'a, Call> { - fn new(message: &'a Xcm, ethereum_network: &'a NetworkId) -> Self { - Self { iter: message.inner().iter().peekable(), ethereum_network } +impl<'a, AgentHashedDescription, Call> XcmConverter<'a, AgentHashedDescription, Call> +where + AgentHashedDescription: ConvertLocation, +{ + fn new(message: &'a Xcm, ethereum_network: NetworkId, para_id: ParaId) -> Self { + Self { + iter: message.inner().iter().peekable(), + ethereum_network, + para_id, + _marker: Default::default(), + } } - fn convert(&mut self) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { + fn convert(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { let result = match self.peek() { + // Transact message Ok(Transact { .. }) => self.transact_message(), // Get withdraw/deposit and make native tokens create message. Ok(WithdrawAsset { .. }) => self.native_tokens_unlock_message(), @@ -200,7 +204,7 @@ impl<'a, Call> XcmConverter<'a, Call> { Ok(result) } - fn transact_message(&mut self) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { + fn transact_message(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { let call_data = if let Transact { origin_kind, call, .. } = self.next()? { ensure!( *origin_kind == OriginKind::SovereignAccount, @@ -216,7 +220,8 @@ impl<'a, Call> XcmConverter<'a, Call> { let topic_id = match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; Ok(( - AgentExecuteCommand::Transact { + Command::Transact { + agent_id: message.agent_id, target: message.target, payload: message.call, gas_limit: message.gas_limit, @@ -226,9 +231,7 @@ impl<'a, Call> XcmConverter<'a, Call> { )) } - fn native_tokens_unlock_message( - &mut self, - ) -> Result<(AgentExecuteCommand, [u8; 32]), XcmConverterError> { + fn native_tokens_unlock_message(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { use XcmConverterError::*; // Get the reserve assets from WithdrawAsset. @@ -302,7 +305,19 @@ impl<'a, Call> XcmConverter<'a, Call> { // Check if there is a SetTopic and skip over it if found. let topic_id = match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; - Ok((AgentExecuteCommand::TransferToken { token, recipient, amount }, *topic_id)) + let agent_id = AgentHashedDescription::convert_location(&Location::new( + 1, + Parachain(self.para_id.into()), + )) + .ok_or(Unroutable)?; + + Ok(( + Command::AgentExecute { + agent_id, + command: AgentExecuteCommand::TransferToken { token, recipient, amount }, + }, + *topic_id, + )) } fn next(&mut self) -> Result<&'a Instruction, XcmConverterError> { @@ -315,7 +330,7 @@ impl<'a, Call> XcmConverter<'a, Call> { fn network_matches(&self, network: &Option) -> bool { if let Some(network) = network { - network == self.ethereum_network + network.clone() == self.ethereum_network } else { true } diff --git a/bridges/snowbridge/primitives/router/src/outbound/tests.rs b/bridges/snowbridge/primitives/router/src/outbound/tests.rs index 63a241ebf291..a861453ce059 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/tests.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/tests.rs @@ -410,11 +410,14 @@ fn xcm_converter_convert_success() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + let mut converter = XcmConverter::::new(&message, network, 1000.into()); + let expected_payload = Command::AgentExecute { + agent_id: hex!("81c5ab2571199e3188135178f3c2c8e2d268be1313d029b30f534fa579b69b79").into(), + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -443,11 +446,14 @@ fn xcm_converter_convert_without_buy_execution_yields_success() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + let mut converter = XcmConverter::::new(&message, network, 1000.into()); + let expected_payload = Command::AgentExecute { + agent_id: hex!("81c5ab2571199e3188135178f3c2c8e2d268be1313d029b30f534fa579b69b79").into(), + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -478,11 +484,14 @@ fn xcm_converter_convert_with_wildcard_all_asset_filter_succeeds() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + let mut converter = XcmConverter::::new(&message, network, 1000.into()); + let expected_payload = Command::AgentExecute { + agent_id: hex!("81c5ab2571199e3188135178f3c2c8e2d268be1313d029b30f534fa579b69b79").into(), + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -513,11 +522,14 @@ fn xcm_converter_convert_with_fees_less_than_reserve_yields_success() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); - let expected_payload = AgentExecuteCommand::TransferToken { - token: token_address.into(), - recipient: beneficiary_address.into(), - amount: 1000, + let mut converter = XcmConverter::::new(&message, network, 1000.into()); + let expected_payload = Command::AgentExecute { + agent_id: hex!("81c5ab2571199e3188135178f3c2c8e2d268be1313d029b30f534fa579b69b79").into(), + command: AgentExecuteCommand::TransferToken { + token: token_address.into(), + recipient: beneficiary_address.into(), + amount: 1000, + }, }; let result = converter.convert(); assert_eq!(result, Ok((expected_payload, [0; 32]))); @@ -547,7 +559,7 @@ fn xcm_converter_convert_without_set_topic_yields_set_topic_expected() { ClearTopic, ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = XcmConverter::::new(&message, network, 1000.into()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::SetTopicExpected)); } @@ -564,7 +576,8 @@ fn xcm_converter_convert_with_partial_message_yields_unexpected_end_of_xcm() { .into(); let message: Xcm<()> = vec![WithdrawAsset(assets)].into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = XcmConverter::::new(&message, network, 1000.into()); + let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::UnexpectedEndOfXcm)); } @@ -595,7 +608,7 @@ fn xcm_converter_with_different_fee_asset_fails() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = XcmConverter::::new(&message, network, 1000.into()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::InvalidFeeAsset)); } @@ -625,7 +638,7 @@ fn xcm_converter_with_fees_greater_than_reserve_fails() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = XcmConverter::::new(&message, network, 1000.into()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::InvalidFeeAsset)); } @@ -636,7 +649,7 @@ fn xcm_converter_convert_with_empty_xcm_yields_unexpected_instruction() { let message: Xcm<()> = vec![].into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = XcmConverter::::new(&message, network, 1000.into()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::UnexpectedEndOfXcm)); @@ -668,7 +681,7 @@ fn xcm_converter_convert_with_extra_instructions_yields_end_of_xcm_message_expec ClearError, ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = XcmConverter::::new(&message, network, 1000.into()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::EndOfXcmMessageExpected)); @@ -698,7 +711,7 @@ fn xcm_converter_convert_without_withdraw_asset_yields_unexpected_instruction() SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = XcmConverter::::new(&message, network, 1000.into()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::UnexpectedInstruction)); @@ -723,7 +736,7 @@ fn xcm_converter_convert_without_withdraw_asset_yields_deposit_expected() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = XcmConverter::::new(&message, network, 1000.into()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::DepositAssetExpected)); @@ -756,7 +769,7 @@ fn xcm_converter_convert_without_assets_yields_no_reserve_assets() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = XcmConverter::::new(&message, network, 1000.into()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::NoReserveAssets)); @@ -794,7 +807,7 @@ fn xcm_converter_convert_with_two_assets_yields_too_many_assets() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = XcmConverter::::new(&message, network, 1000.into()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::TooManyAssets)); @@ -825,7 +838,7 @@ fn xcm_converter_convert_without_consuming_filter_yields_filter_does_not_consume SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = XcmConverter::::new(&message, network, 1000.into()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::FilterDoesNotConsumeAllAssets)); @@ -856,7 +869,7 @@ fn xcm_converter_convert_with_zero_amount_asset_yields_zero_asset_transfer() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = XcmConverter::::new(&message, network, 1000.into()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::ZeroAssetTransfer)); @@ -886,7 +899,7 @@ fn xcm_converter_convert_non_ethereum_asset_yields_asset_resolution_failed() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = XcmConverter::::new(&message, network, 1000.into()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); @@ -919,7 +932,7 @@ fn xcm_converter_convert_non_ethereum_chain_asset_yields_asset_resolution_failed SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = XcmConverter::::new(&message, network, 1000.into()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); @@ -952,7 +965,7 @@ fn xcm_converter_convert_non_ethereum_chain_yields_asset_resolution_failed() { SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = XcmConverter::::new(&message, network, 1000.into()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::AssetResolutionFailed)); @@ -989,7 +1002,7 @@ fn xcm_converter_convert_with_non_ethereum_beneficiary_yields_beneficiary_resolu SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = XcmConverter::::new(&message, network, 1000.into()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::BeneficiaryResolutionFailed)); @@ -1025,7 +1038,7 @@ fn xcm_converter_convert_with_non_ethereum_chain_beneficiary_yields_beneficiary_ SetTopic([0; 32]), ] .into(); - let mut converter = XcmConverter::new(&message, &network); + let mut converter = XcmConverter::::new(&message, network, 1000.into()); let result = converter.convert(); assert_eq!(result.err(), Some(XcmConverterError::BeneficiaryResolutionFailed)); 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 b2fd0db3264e..e63e98496807 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 @@ -567,14 +567,15 @@ fn register_weth_token_in_asset_hub_fail_for_insufficient_fee() { fn transact_from_penpal_to_ethereum() { BridgeHubRococo::fund_para_sovereign(PenpalA::para_id().into(), INITIAL_FUND); + // Agent as PalletInstance + let agent_id = snowbridge_pallet_system::agent_id_of::<::Runtime>( + &Location::new(1, [Parachain(PenpalA::para_id().into()), PalletInstance(52)]), + ) + .unwrap(); + // Create agent and channel BridgeHubRococo::execute_with(|| { type Runtime = ::Runtime; - let agent_id = snowbridge_pallet_system::agent_id_of::(&Location::new( - 1, - [Parachain(PenpalA::para_id().into())], - )) - .unwrap(); snowbridge_pallet_system::Agents::::insert(agent_id, ()); let channel_id: ChannelId = PenpalA::para_id().into(); snowbridge_pallet_system::Channels::::insert( @@ -596,6 +597,7 @@ fn transact_from_penpal_to_ethereum() { let remote_cost = 2_750_872_500_000; assert_ok!(::TransactHelper::transact_to_ethereum( RuntimeOrigin::signed(sender.clone()), + agent_id, //contract hex!("ee9170abfbf9421ad6dd07f6bdec9d89f2b581e0").into(), //call diff --git a/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs b/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs index 6ca0011eb84d..487d788de370 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs @@ -20,7 +20,7 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - use sp_core::H160; + use sp_core::{H160, H256}; use sp_std::{vec, vec::Vec}; use xcm::prelude::*; use xcm_executor::traits::XcmAssetTransfers; @@ -51,6 +51,7 @@ pub mod pallet { #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] pub struct TransactInfo { + pub agent_id: H256, pub target: H160, pub call: Vec, pub gas_limit: u64, @@ -69,6 +70,7 @@ pub mod pallet { #[pallet::weight(Weight::from_parts(100_000_000, 0))] pub fn transact_to_ethereum( origin: OriginFor, + agent_id: H256, target: H160, call: Vec, fee: u128, @@ -80,7 +82,8 @@ pub mod pallet { parents: 2, interior: Junctions::from([GlobalConsensus(Ethereum { chain_id: 11155111 })]), }; - let transact = TransactInfo { target, call, gas_limit, fee }; + + let transact = TransactInfo { agent_id, target, call, gas_limit, fee }; let inner_message = Xcm(vec![ Transact { From 235582a755365edf74048b4e48e58f5276810f24 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 26 Apr 2024 08:37:10 +0800 Subject: [PATCH 12/16] Agent for a child sovereign of the Parachain(e.g. PalletInstance, Account32, Account20) --- bridges/snowbridge/pallets/system/src/lib.rs | 4 +- bridges/snowbridge/primitives/core/src/lib.rs | 5 +- .../bridge-hub-rococo/src/tests/snowbridge.rs | 54 +++++++++++++++++++ .../penpal/src/pallets/transact_helper.rs | 41 +++++++++++--- 4 files changed, 94 insertions(+), 10 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/lib.rs b/bridges/snowbridge/pallets/system/src/lib.rs index 715bb764cd34..50493c089cab 100644 --- a/bridges/snowbridge/pallets/system/src/lib.rs +++ b/bridges/snowbridge/pallets/system/src/lib.rs @@ -89,8 +89,8 @@ fn ensure_sibling(location: &Location) -> Result<(ParaId, H256), DispatchErro where T: Config, { - match location.unpack() { - (1, [Parachain(para_id)]) => { + match (location.parent_count(), location.first_interior()) { + (1, Some(Parachain(para_id))) => { let agent_id = agent_id_of::(location)?; Ok(((*para_id).into(), agent_id)) }, diff --git a/bridges/snowbridge/primitives/core/src/lib.rs b/bridges/snowbridge/primitives/core/src/lib.rs index ed1af4225d24..6f2d5c759198 100644 --- a/bridges/snowbridge/primitives/core/src/lib.rs +++ b/bridges/snowbridge/primitives/core/src/lib.rs @@ -47,7 +47,10 @@ where pub struct AllowSiblingsOnly; impl Contains for AllowSiblingsOnly { fn contains(location: &Location) -> bool { - matches!(location.unpack(), (1, [Parachain(_)])) + return match (location.parent_count(), location.first_interior()) { + (1, Some(Parachain(_))) => true, + _ => false, + } } } 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 e63e98496807..6d48fae7021f 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 @@ -632,3 +632,57 @@ fn transact_from_penpal_to_ethereum() { ); }); } + +#[test] +fn create_agent_for_penpal_pallet_instance() { + BridgeHubRococo::fund_para_sovereign(PenpalA::para_id().into(), INITIAL_FUND); + + let destination = VersionedLocation::V4(Location { + parents: 1, + interior: Junctions::from([Parachain(BridgeHubRococo::para_id().into())]), + }); + + let fee_asset = Asset::from((Location::parent(), 40_000_000_000_000_u128)); + + let create_agent_call = SnowbridgeControl::Control(ControlCall::CreateAgent {}); + + let remote_xcm = VersionedXcm::from(Xcm::<()>(vec![ + WithdrawAsset(fee_asset.clone().into()), + BuyExecution { fees: fee_asset, weight_limit: Unlimited }, + DescendOrigin(PalletInstance(52).into()), + Transact { + require_weight_at_most: 4_000_000_000.into(), + origin_kind: OriginKind::Xcm, + call: create_agent_call.encode().into(), + }, + ])); + + PenpalA::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + type RuntimeOrigin = ::RuntimeOrigin; + + assert_ok!(::TransactHelper::send_as_sovereign( + RuntimeOrigin::root(), + bx!(destination), + bx!(remote_xcm), + )); + + assert_expected_events!( + PenpalA, + vec![ + RuntimeEvent::TransactHelper(penpal_runtime::pallets::transact_helper::Event::Sent { .. }) => {}, + ] + ); + }); + + BridgeHubRococo::execute_with(|| { + type RuntimeEvent = ::RuntimeEvent; + + assert_expected_events!( + BridgeHubRococo, + vec![ + RuntimeEvent::EthereumSystem(snowbridge_pallet_system::Event::CreateAgent { .. }) => {}, + ] + ); + }); +} diff --git a/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs b/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs index 487d788de370..e6e3bee2d024 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs @@ -21,15 +21,16 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use sp_core::{H160, H256}; - use sp_std::{vec, vec::Vec}; + use sp_std::{boxed::Box, vec, vec::Vec}; use xcm::prelude::*; use xcm_executor::traits::XcmAssetTransfers; #[pallet::config] - pub trait Config: frame_system::Config { + pub trait Config: frame_system::Config + pallet_xcm::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; type XcmRouter: SendXcm; - type XcmExecutor: ExecuteXcm + XcmAssetTransfers; + type XcmExecutor: ExecuteXcm<::RuntimeCall> + + XcmAssetTransfers; } #[pallet::pallet] @@ -38,8 +39,10 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// ExportMessage message was sent + /// ExportMessage was sent SentExportMessage { message_id: XcmHash, sender_cost: Assets, message: Xcm<()> }, + /// XCM message sent. \[to, message\] + Sent { to: Location, message: Xcm<()> }, } #[pallet::error] @@ -47,6 +50,8 @@ pub mod pallet { InvalidMsg, FeesNotMet, SendFailure, + BadVersion, + Unreachable, } #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] @@ -68,6 +73,27 @@ pub mod pallet { { #[pallet::call_index(0)] #[pallet::weight(Weight::from_parts(100_000_000, 0))] + pub fn send_as_sovereign( + origin: OriginFor, + dest: Box, + message: Box>, + ) -> DispatchResult { + ensure_root(origin)?; + let dest = Location::try_from(*dest).map_err(|()| Error::::BadVersion)?; + let message: Xcm<()> = (*message).try_into().map_err(|()| Error::::BadVersion)?; + + pallet_xcm::Pallet::::send_xcm(Here, dest.clone(), message.clone()).map_err( + |e| match e { + SendError::Unroutable => Error::::Unreachable, + _ => Error::::SendFailure, + }, + )?; + Self::deposit_event(Event::Sent { to: dest, message }); + Ok(()) + } + + #[pallet::call_index(1)] + #[pallet::weight(Weight::from_parts(100_000_000, 0))] pub fn transact_to_ethereum( origin: OriginFor, agent_id: H256, @@ -95,7 +121,7 @@ pub mod pallet { ]); let (ticket, price) = - validate_send::(dest.clone(), inner_message.clone()) + validate_send::<::XcmRouter>(dest.clone(), inner_message.clone()) .map_err(|_| Error::::InvalidMsg)?; ensure!(price.len() > 0, Error::::FeesNotMet); @@ -104,10 +130,11 @@ pub mod pallet { let origin = Location::from(AccountId32 { network: None, id: who.into() }); - T::XcmExecutor::charge_fees(origin, fees.clone().into()) + ::XcmExecutor::charge_fees(origin, fees.clone().into()) .map_err(|_| Error::::FeesNotMet)?; - let message_id = T::XcmRouter::deliver(ticket).map_err(|_| Error::::SendFailure)?; + let message_id = + ::XcmRouter::deliver(ticket).map_err(|_| Error::::SendFailure)?; Self::deposit_event(Event::SentExportMessage { message_id, From 6cc2175ff5063178260cfa5e243aa981d32a3622 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 26 Apr 2024 10:14:53 +0800 Subject: [PATCH 13/16] Fix breaking tests --- bridges/snowbridge/pallets/system/src/tests.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bridges/snowbridge/pallets/system/src/tests.rs b/bridges/snowbridge/pallets/system/src/tests.rs index 09f24195a30a..d88704c5c87c 100644 --- a/bridges/snowbridge/pallets/system/src/tests.rs +++ b/bridges/snowbridge/pallets/system/src/tests.rs @@ -285,7 +285,7 @@ fn create_channel_bad_origin() { BadOrigin, ); - // child of sibling location not allowed + // child of sibling location is allowed assert_noop!( EthereumSystem::create_channel( make_xcm_origin(Location::new( @@ -294,7 +294,7 @@ fn create_channel_bad_origin() { )), OperatingMode::Normal, ), - BadOrigin, + Error::::NoAgent, ); // local account location not allowed @@ -357,7 +357,7 @@ fn update_channel_bad_origin() { BadOrigin, ); - // child of sibling location not allowed + // child of sibling location is allowed assert_noop!( EthereumSystem::update_channel( make_xcm_origin(Location::new( @@ -366,7 +366,7 @@ fn update_channel_bad_origin() { )), mode, ), - BadOrigin, + Error::::NoChannel, ); // local account location not allowed From 3eee96db4c8785e61aaaf256776348b31af468ab Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 2 May 2024 08:20:59 +0800 Subject: [PATCH 14/16] Make agent_id generated from DescendOrigin --- Cargo.lock | 1 + .../primitives/core/src/outbound.rs | 1 - .../primitives/router/src/outbound/mod.rs | 54 +++++++++------ .../runtime/runtime-common/src/lib.rs | 3 +- .../bridge-hub-rococo/src/tests/snowbridge.rs | 7 +- .../runtimes/testing/penpal/Cargo.toml | 2 + .../penpal/src/pallets/transact_helper.rs | 66 ++++++++----------- 7 files changed, 69 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3734f00b6cc3..0525135b157e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12230,6 +12230,7 @@ dependencies = [ "sp-core", "sp-genesis-builder", "sp-inherents", + "sp-io", "sp-offchain", "sp-runtime", "sp-session", diff --git a/bridges/snowbridge/primitives/core/src/outbound.rs b/bridges/snowbridge/primitives/core/src/outbound.rs index 988d3eadad37..cf4ecd761af5 100644 --- a/bridges/snowbridge/primitives/core/src/outbound.rs +++ b/bridges/snowbridge/primitives/core/src/outbound.rs @@ -442,7 +442,6 @@ pub const ETHER_DECIMALS: u8 = 18; #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] pub struct TransactInfo { - pub agent_id: H256, pub target: H160, pub call: Vec, pub gas_limit: u64, diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index f5f28cedb903..ddaa3b261d3f 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -9,11 +9,10 @@ use core::slice::Iter; use codec::{Decode, DecodeAll, Encode}; -use crate::outbound::XcmConverterError::SetTopicExpected; use frame_support::{ensure, traits::Get}; use snowbridge_core::{ outbound::{AgentExecuteCommand, Command, Message, SendMessage, TransactInfo}, - ChannelId, ParaId, + AgentIdOf, ChannelId, ParaId, }; use sp_core::{H160, H256}; use sp_std::{iter::Peekable, marker::PhantomData, prelude::*}; @@ -155,7 +154,8 @@ enum XcmConverterError { TransactSovereignAccountExpected, TransactDecodeFailed, UnexpectedInstruction, - Unroutable, + ConvertAgentFailed, + TransactDescendOriginExpected, } macro_rules! match_expression { @@ -168,6 +168,7 @@ macro_rules! match_expression { } struct XcmConverter<'a, AgentHashedDescription, Call> { + xcm: &'a Xcm, iter: Peekable>>, ethereum_network: NetworkId, para_id: ParaId, @@ -177,21 +178,17 @@ impl<'a, AgentHashedDescription, Call> XcmConverter<'a, AgentHashedDescription, where AgentHashedDescription: ConvertLocation, { - fn new(message: &'a Xcm, ethereum_network: NetworkId, para_id: ParaId) -> Self { - Self { - iter: message.inner().iter().peekable(), - ethereum_network, - para_id, - _marker: Default::default(), - } + fn new(xcm: &'a Xcm, ethereum_network: NetworkId, para_id: ParaId) -> Self { + let iter = xcm.inner().iter().peekable(); + Self { xcm, iter, ethereum_network, para_id, _marker: Default::default() } } fn convert(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { - let result = match self.peek() { + let result = match self.peek2() { // Transact message - Ok(Transact { .. }) => self.transact_message(), + Ok([DescendOrigin { .. }, Transact { .. }]) => self.transact_message(), // Get withdraw/deposit and make native tokens create message. - Ok(WithdrawAsset { .. }) => self.native_tokens_unlock_message(), + Ok([WithdrawAsset { .. }, ..]) => self.native_tokens_unlock_message(), Err(e) => Err(e), _ => return Err(XcmConverterError::UnexpectedInstruction), }?; @@ -205,23 +202,30 @@ where } fn transact_message(&mut self) -> Result<(Command, [u8; 32]), XcmConverterError> { + use XcmConverterError::*; + let interior_origin = match_expression!(self.next()?, DescendOrigin(origin), origin) + .ok_or(TransactDescendOriginExpected)? + .first() + .ok_or(TransactDescendOriginExpected)?; + + let origin = + Location::new(1, [Parachain(self.para_id.into()), interior_origin.clone().into()]); + let agent_id = AgentIdOf::convert_location(&origin).ok_or(ConvertAgentFailed)?; + let call_data = if let Transact { origin_kind, call, .. } = self.next()? { - ensure!( - *origin_kind == OriginKind::SovereignAccount, - XcmConverterError::TransactSovereignAccountExpected - ); + ensure!(*origin_kind == OriginKind::SovereignAccount, TransactSovereignAccountExpected); call } else { - return Err(XcmConverterError::TransactExpected) + return Err(TransactExpected) }; let message = TransactInfo::decode_all(&mut call_data.clone().into_encoded().as_slice()) - .map_err(|_| XcmConverterError::TransactDecodeFailed)?; + .map_err(|_| TransactDecodeFailed)?; let topic_id = match_expression!(self.next()?, SetTopic(id), id).ok_or(SetTopicExpected)?; Ok(( Command::Transact { - agent_id: message.agent_id, + agent_id, target: message.target, payload: message.call, gas_limit: message.gas_limit, @@ -309,7 +313,7 @@ where 1, Parachain(self.para_id.into()), )) - .ok_or(Unroutable)?; + .ok_or(ConvertAgentFailed)?; Ok(( Command::AgentExecute { @@ -335,4 +339,12 @@ where true } } + + fn peek2(&self) -> Result<[Instruction; 2], XcmConverterError> { + let instructions = self.xcm.clone().into_inner(); + ensure!(instructions.len() >= 2, XcmConverterError::UnexpectedEndOfXcm); + let first = instructions.get(0).ok_or(XcmConverterError::UnexpectedEndOfXcm)?; + let second = instructions.get(1).ok_or(XcmConverterError::UnexpectedEndOfXcm)?; + Ok([first.clone(), second.clone()]) + } } diff --git a/bridges/snowbridge/runtime/runtime-common/src/lib.rs b/bridges/snowbridge/runtime/runtime-common/src/lib.rs index aae45520ff4b..d2acd7f9fc2c 100644 --- a/bridges/snowbridge/runtime/runtime-common/src/lib.rs +++ b/bridges/snowbridge/runtime/runtime-common/src/lib.rs @@ -119,7 +119,8 @@ impl::TransactHelper::transact_to_ethereum( RuntimeOrigin::signed(sender.clone()), - agent_id, //contract hex!("ee9170abfbf9421ad6dd07f6bdec9d89f2b581e0").into(), //call hex!("00071468656c6c6f").to_vec(), //The fee here in DOT should cover the remote execution cost on Ethereum remote_cost, - //gas + //gas cost on Ethereum 80_000, )); let balance_after = ::ForeignAssets::balance( @@ -612,7 +612,8 @@ fn transact_from_penpal_to_ethereum() { sender.clone(), ); let total_cost = initial_fund - balance_after; - assert_eq!(total_cost > remote_cost + BridgeHubEthereumBaseFee::get(), true); + // Only the delivery cost + assert_eq!(total_cost > BridgeHubEthereumBaseFee::get(), true); assert_expected_events!( PenpalA, vec![ diff --git a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml index 028aa002a91e..311d11d5525e 100644 --- a/cumulus/parachains/runtimes/testing/penpal/Cargo.toml +++ b/cumulus/parachains/runtimes/testing/penpal/Cargo.toml @@ -55,6 +55,7 @@ sp-std = { path = "../../../../../substrate/primitives/std", default-features = sp-storage = { path = "../../../../../substrate/primitives/storage", default-features = false } sp-transaction-pool = { path = "../../../../../substrate/primitives/transaction-pool", default-features = false } sp-version = { path = "../../../../../substrate/primitives/version", default-features = false } +sp-io = { path = "../../../../../substrate/primitives/io", default-features = false } # Polkadot polkadot-primitives = { path = "../../../../../polkadot/primitives", default-features = false } @@ -124,6 +125,7 @@ std = [ "sp-core/std", "sp-genesis-builder/std", "sp-inherents/std", + "sp-io/std", "sp-offchain/std", "sp-runtime/std", "sp-session/std", diff --git a/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs b/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs index e6e3bee2d024..762ebdb2d0ce 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs @@ -17,10 +17,9 @@ pub use pallet::*; #[frame_support::pallet] pub mod pallet { - use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - use sp_core::{H160, H256}; + use sp_core::H160; use sp_std::{boxed::Box, vec, vec::Vec}; use xcm::prelude::*; use xcm_executor::traits::XcmAssetTransfers; @@ -40,7 +39,7 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// ExportMessage was sent - SentExportMessage { message_id: XcmHash, sender_cost: Assets, message: Xcm<()> }, + SentExportMessage { message_id: XcmHash, message: Xcm<()> }, /// XCM message sent. \[to, message\] Sent { to: Location, message: Xcm<()> }, } @@ -51,12 +50,10 @@ pub mod pallet { FeesNotMet, SendFailure, BadVersion, - Unreachable, } #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] pub struct TransactInfo { - pub agent_id: H256, pub target: H160, pub call: Vec, pub gas_limit: u64, @@ -84,7 +81,6 @@ pub mod pallet { pallet_xcm::Pallet::::send_xcm(Here, dest.clone(), message.clone()).map_err( |e| match e { - SendError::Unroutable => Error::::Unreachable, _ => Error::::SendFailure, }, )?; @@ -96,7 +92,6 @@ pub mod pallet { #[pallet::weight(Weight::from_parts(100_000_000, 0))] pub fn transact_to_ethereum( origin: OriginFor, - agent_id: H256, target: H160, call: Vec, fee: u128, @@ -104,43 +99,36 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin)?; + // Ethereum as destination, hard-code chain_id for demonstration only. let dest = Location { parents: 2, interior: Junctions::from([GlobalConsensus(Ethereum { chain_id: 11155111 })]), }; - let transact = TransactInfo { agent_id, target, call, gas_limit, fee }; - - let inner_message = Xcm(vec![ - Transact { - origin_kind: OriginKind::SovereignAccount, - require_weight_at_most: Weight::default(), - call: transact.encode().into(), - }, - SetTopic([0; 32]), - ]); - - let (ticket, price) = - validate_send::<::XcmRouter>(dest.clone(), inner_message.clone()) - .map_err(|_| Error::::InvalidMsg)?; - ensure!(price.len() > 0, Error::::FeesNotMet); - - let mut fees: Assets = (Parent, fee).into(); - fees.push(price.get(0).unwrap().clone()); - - let origin = Location::from(AccountId32 { network: None, id: who.into() }); - - ::XcmExecutor::charge_fees(origin, fees.clone().into()) - .map_err(|_| Error::::FeesNotMet)?; - - let message_id = - ::XcmRouter::deliver(ticket).map_err(|_| Error::::SendFailure)?; - - Self::deposit_event(Event::SentExportMessage { - message_id, - sender_cost: fees.into(), - message: inner_message, - }); + // construct the inner xcm of ExportMessage + let transact = TransactInfo { target, call, gas_limit, fee }; + let mut message = Xcm(vec![Transact { + origin_kind: OriginKind::SovereignAccount, + require_weight_at_most: Weight::default(), + call: transact.encode().into(), + }]); + let message_clone = message.clone(); + // Add SetTopic only for tracing + let _ = &message + .inner_mut() + .push(SetTopic(message_clone.using_encoded(sp_io::hashing::blake2_256))); + + // Send the xcm + let message_id = pallet_xcm::Pallet::::send_xcm( + AccountId32 { network: None, id: who.into() }, + dest.clone(), + message.clone(), + ) + .map_err(|e| match e { + _ => Error::::SendFailure, + })?; + + Self::deposit_event(Event::SentExportMessage { message_id, message }); Ok(()) } } From d8e4424de5a38c9bfcbb4d1920ef5ad873460a35 Mon Sep 17 00:00:00 2001 From: ron Date: Sun, 5 May 2024 23:53:19 +0800 Subject: [PATCH 15/16] Remove fee from TransactInfo --- .../snowbridge/primitives/core/src/outbound.rs | 3 --- .../primitives/router/src/outbound/mod.rs | 7 ++----- .../bridge-hub-rococo/src/tests/snowbridge.rs | 15 +++++++++------ .../testing/penpal/src/pallets/transact_helper.rs | 4 +--- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/bridges/snowbridge/primitives/core/src/outbound.rs b/bridges/snowbridge/primitives/core/src/outbound.rs index cf4ecd761af5..d85671f323f9 100644 --- a/bridges/snowbridge/primitives/core/src/outbound.rs +++ b/bridges/snowbridge/primitives/core/src/outbound.rs @@ -150,8 +150,6 @@ mod v1 { payload: Vec, /// The dynamic gas component that needs to be specified when executing the contract gas_limit: u64, - /// The fee to cover the delivery cost - fee: u128, }, } @@ -445,5 +443,4 @@ pub struct TransactInfo { pub target: H160, pub call: Vec, pub gas_limit: u64, - pub fee: u128, } diff --git a/bridges/snowbridge/primitives/router/src/outbound/mod.rs b/bridges/snowbridge/primitives/router/src/outbound/mod.rs index ddaa3b261d3f..1383aaa3319e 100644 --- a/bridges/snowbridge/primitives/router/src/outbound/mod.rs +++ b/bridges/snowbridge/primitives/router/src/outbound/mod.rs @@ -108,10 +108,8 @@ where SendError::Unroutable })?; let fee = match command { - Command::Transact { fee, .. } => { - ensure!(fee > fees.remote, SendError::Fees); - Ok::(Asset::from((Location::parent(), fees.local)).into()) - }, + Command::Transact { .. } => + Ok::(Asset::from((Location::parent(), fees.local)).into()), _ => Ok::(Asset::from((Location::parent(), fees.total())).into()), }?; @@ -229,7 +227,6 @@ where target: message.target, payload: message.call, gas_limit: message.gas_limit, - fee: message.fee, }, *topic_id, )) 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 dfb7fc0701f5..1f6aa2585917 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 @@ -569,7 +569,13 @@ fn transact_from_penpal_to_ethereum() { // Agent as PalletInstance let agent_id = snowbridge_pallet_system::agent_id_of::<::Runtime>( - &Location::new(1, [Parachain(PenpalA::para_id().into()), PalletInstance(52)]), + &Location::new( + 1, + [ + Parachain(PenpalA::para_id().into()), + AccountId32 { network: None, id: PenpalASender::get().into() }, + ], + ), ) .unwrap(); @@ -595,16 +601,13 @@ fn transact_from_penpal_to_ethereum() { initial_fund, )); - let remote_cost = 2_750_872_500_000; assert_ok!(::TransactHelper::transact_to_ethereum( RuntimeOrigin::signed(sender.clone()), //contract hex!("ee9170abfbf9421ad6dd07f6bdec9d89f2b581e0").into(), //call hex!("00071468656c6c6f").to_vec(), - //The fee here in DOT should cover the remote execution cost on Ethereum - remote_cost, - //gas cost on Ethereum + //gas limit on Ethereum 80_000, )); let balance_after = ::ForeignAssets::balance( @@ -650,7 +653,7 @@ fn create_agent_for_penpal_pallet_instance() { let remote_xcm = VersionedXcm::from(Xcm::<()>(vec![ WithdrawAsset(fee_asset.clone().into()), BuyExecution { fees: fee_asset, weight_limit: Unlimited }, - DescendOrigin(PalletInstance(52).into()), + DescendOrigin([AccountId32 { network: None, id: PenpalASender::get().into() }].into()), Transact { require_weight_at_most: 4_000_000_000.into(), origin_kind: OriginKind::Xcm, diff --git a/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs b/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs index 762ebdb2d0ce..374f0b9147dd 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/pallets/transact_helper.rs @@ -57,7 +57,6 @@ pub mod pallet { pub target: H160, pub call: Vec, pub gas_limit: u64, - pub fee: u128, } #[pallet::hooks] @@ -94,7 +93,6 @@ pub mod pallet { origin: OriginFor, target: H160, call: Vec, - fee: u128, gas_limit: u64, ) -> DispatchResult { let who = ensure_signed(origin)?; @@ -106,7 +104,7 @@ pub mod pallet { }; // construct the inner xcm of ExportMessage - let transact = TransactInfo { target, call, gas_limit, fee }; + let transact = TransactInfo { target, call, gas_limit }; let mut message = Xcm(vec![Transact { origin_kind: OriginKind::SovereignAccount, require_weight_at_most: Weight::default(), From ebcff31baa9703a6200ef889aac80180e7b0af58 Mon Sep 17 00:00:00 2001 From: ron Date: Mon, 6 May 2024 00:16:02 +0800 Subject: [PATCH 16/16] Rename the test --- .../tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1f6aa2585917..3c7b6686a978 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 @@ -638,7 +638,7 @@ fn transact_from_penpal_to_ethereum() { } #[test] -fn create_agent_for_penpal_pallet_instance() { +fn create_agent_for_penpal_sender() { BridgeHubRococo::fund_para_sovereign(PenpalA::para_id().into(), INITIAL_FUND); let destination = VersionedLocation::V4(Location {