From 2d7bdcbbe24178aaaee1983f15def427fc6a070a Mon Sep 17 00:00:00 2001 From: tgmichel Date: Wed, 8 Jun 2022 18:06:02 +0200 Subject: [PATCH 01/15] wip (cherry picked from commit c0a6fab03866c2e0744f1d621ace009a5e7e4fd8) --- frame/ethereum/src/lib.rs | 55 ++++++++ frame/ethereum/src/xcm.rs | 266 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 321 insertions(+) create mode 100644 frame/ethereum/src/xcm.rs diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index 356a070df4..f9bbbac36b 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -29,6 +29,8 @@ mod mock; #[cfg(all(feature = "std", test))] mod tests; +mod xcm; + use ethereum_types::{Bloom, BloomInput, H160, H256, H64, U256}; use evm::ExitReason; use fp_consensus::{PostLog, PreLog, FRONTIER_ENGINE_ID}; @@ -301,6 +303,59 @@ pub mod pallet { Self::apply_validated_transaction(source, transaction) } + + /// Xcm Transact an Ethereum transaction. + #[pallet::weight(::GasWeightMapping::gas_to_weight( + xcm_transaction.gas_limit.unique_saturated_into() + ))] + pub fn transact_xcm( + origin: OriginFor, + xcm_transaction: xcm::EthereumXcmTransaction, + ) -> DispatchResultWithPostInfo { + use xcm::XcmToEthereum; + // let source = frame_system::ensure_signed(origin)?; + let source = ensure_ethereum_transaction(origin)?; + + let (base_fee, base_fee_weight) = T::FeeCalculator::min_gas_price(); + let (who, account_weight) = pallet_evm::Pallet::::account_basic(&source); + + let transaction: Option = xcm_transaction.into_transaction_v2(base_fee, who.nonce); + if let Some(transaction) = transaction { + let transaction_data = Pallet::::transaction_data(&transaction); + + let _ = CheckEvmTransaction::::new( + CheckEvmTransactionConfig { + evm_config: T::config(), + block_gas_limit: T::BlockGasLimit::get(), + base_fee, + chain_id: T::ChainId::get(), + is_transactional: true, + }, + transaction_data.into(), + ) + .validate_in_block_for(&who) + .and_then(|v| v.with_base_fee()) + .and_then(|v| v.with_balance_for(&who)) + .map_err(|e| sp_runtime::DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: Some(base_fee_weight.saturating_add(account_weight)), + pays_fee: Pays::Yes, + }, + error: sp_runtime::DispatchError::Other("Failed to validate ethereum transaction") + })?; + + Self::apply_validated_transaction(source, transaction) + + } else { + Err(sp_runtime::DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: Some(base_fee_weight.saturating_add(account_weight)), + pays_fee: Pays::Yes, + }, + error: sp_runtime::DispatchError::Other("Cannot convert xcm payload to known type") + }) + } + } } #[pallet::event] diff --git a/frame/ethereum/src/xcm.rs b/frame/ethereum/src/xcm.rs new file mode 100644 index 0000000000..de608d7224 --- /dev/null +++ b/frame/ethereum/src/xcm.rs @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: Apache-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2020-2022 Parity Technologies (UK) Ltd. +// +// 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. +use ethereum::{ + AccessList, AccessListItem, EIP1559Transaction, EIP2930Transaction, LegacyTransaction, + TransactionAction, TransactionRecoveryId, TransactionSignature, TransactionV2, +}; +use ethereum_types::{H160, H256, U256}; +use fp_evm::FeeCalculator; +use frame_support::codec::{Decode, Encode}; +use sp_std::vec::Vec; + +#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo)] +/// Manually sets a gas fee. +pub struct ManualEthereumXcmFee { + /// Legacy or Eip-2930 + pub gas_price: Option, + /// Eip-1559 + pub max_fee_per_gas: Option, + /// Eip-1559 + pub max_priority_fee_per_gas: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo)] +/// Authomatic gas fee based on the current on-chain values. +/// Will always produce an Eip-1559 transaction. +pub enum AutoEthereumXcmFee { + /// base_fee_per_gas = BaseFee + Low, + /// max_fee_per_gas = 2 * BaseFee, max_priority_fee_per_gas = BaseFee + Medium, + /// max_fee_per_gas = 3 * BaseFee, max_priority_fee_per_gas = 2 * BaseFee + High, +} + +#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo)] +pub enum EthereumXcmFee { + Manual(ManualEthereumXcmFee), + Auto(AutoEthereumXcmFee), +} + +/// Xcm transact's Ethereum transaction. +#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo)] +pub struct EthereumXcmTransaction { + /// Gas limit to be consumed by EVM execution. + pub gas_limit: U256, + /// Fee configuration of choice. + pub fee_payment: EthereumXcmFee, + /// Either a Call (the callee, account or contract address) or Create (currently unsupported). + pub action: TransactionAction, + /// Value to be transfered. + pub value: U256, + /// Input data for a contract call. + pub input: Vec, + /// Map of addresses to be pre-paid to warm storage. + pub access_list: Option)>>, +} + +pub trait XcmToEthereum { + fn into_transaction_v2(&self, base_fee: U256, nonce: U256) -> Option; +} + +impl XcmToEthereum for EthereumXcmTransaction { + fn into_transaction_v2(&self, base_fee: U256, nonce: U256) -> Option { + let from_tuple_to_access_list = |t: Vec<(H160, Vec)>| -> AccessList { + t.iter() + .map(|item| AccessListItem { + address: item.0.clone(), + storage_keys: item.1.clone(), + }) + .collect::>() + }; + + let (gas_price, max_fee, max_priority_fee) = match &self.fee_payment { + EthereumXcmFee::Manual(fee_config) => ( + fee_config.gas_price, + fee_config.max_fee_per_gas, + fee_config.max_priority_fee_per_gas, + ), + EthereumXcmFee::Auto(auto_mode) => { + let (max_fee, max_priority_fee) = match auto_mode { + AutoEthereumXcmFee::Low => (Some(base_fee), None), + AutoEthereumXcmFee::Medium => ( + Some(base_fee.saturating_mul(U256::from(2u64))), + Some(base_fee), + ), + AutoEthereumXcmFee::High => ( + Some(base_fee.saturating_mul(U256::from(3u64))), + Some(base_fee.saturating_mul(U256::from(2u64))), + ), + }; + (None, max_fee, max_priority_fee) + } + }; + match (gas_price, max_fee, max_priority_fee) { + (Some(gas_price), None, None) => { + // Legacy or Eip-2930 + if let Some(ref access_list) = self.access_list { + // Eip-2930 + Some(TransactionV2::EIP2930(EIP2930Transaction { + chain_id: 0, + nonce, + gas_price, + gas_limit: self.gas_limit, + action: self.action, + value: self.value, + input: self.input.clone(), + access_list: from_tuple_to_access_list(access_list.to_vec()), + odd_y_parity: true, + r: H256::default(), + s: H256::default(), + })) + } else { + // Legacy + Some(TransactionV2::Legacy(LegacyTransaction { + nonce, + gas_price, + gas_limit: self.gas_limit, + action: self.action, + value: self.value, + input: self.input.clone(), + signature: TransactionSignature::new(42, H256::from_low_u64_be(1u64), H256::from_low_u64_be(1u64)).unwrap(), // TODO + })) + } + } + (None, Some(max_fee), _) => { + // Eip-1559 + Some(TransactionV2::EIP1559(EIP1559Transaction { + chain_id: 0, + nonce, + max_fee_per_gas: max_fee, + max_priority_fee_per_gas: max_priority_fee.unwrap_or_else(U256::zero), + gas_limit: self.gas_limit, + action: self.action, + value: self.value, + input: self.input.clone(), + access_list: if let Some(ref access_list) = self.access_list { + from_tuple_to_access_list(access_list.to_vec()) + } else { + Vec::new() + }, + odd_y_parity: true, + r: H256::default(), + s: H256::default(), + })) + } + _ => return None, + } + } +} + +// /// Xcm transact's Ethereum transaction. +// #[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo)] +// #[scale_info(skip_type_params(T))] +// pub struct EthereumXcmTransaction { +// /// Gas limit to be consumed by EVM execution. +// pub gas_limit: U256, +// /// Fee configuration of choice. +// pub fee_payment: EthereumXcmFee, +// /// Either a Call (the callee, account or contract address) or Create (currently unsupported). +// pub action: TransactionAction, +// /// Value to be transfered. +// pub value: U256, +// /// Input data for a contract call. +// pub input: Vec, +// /// Map of addresses to be pre-paid to warm storage. +// pub access_list: Option)>>, +// _marker: sp_std::marker::PhantomData, +// } + +// impl From> for Option { +// fn from(t: EthereumXcmTransaction) -> Self { + +// let from_tuple_to_access_list = |t: Vec<(H160, Vec)>| -> AccessList { +// t.iter().map(|item| AccessListItem { +// address: item.0, +// storage_keys: item.1, + +// }).collect::>() +// }; + +// let (gas_price, max_fee, max_priority_fee) = match t.fee_payment { +// EthereumXcmFee::Manual(fee_config) => { +// (fee_config.gas_price, fee_config.max_fee_per_gas, fee_config.max_priority_fee_per_gas) +// }, +// EthereumXcmFee::Auto(auto_mode) => { +// let (base_fee, _) = T::FeeCalculator::min_gas_price(); +// let (max_fee, max_priority_fee) = match auto_mode { +// AutoEthereumXcmFee::Low => (Some(base_fee), None), +// AutoEthereumXcmFee::Medium => (Some(base_fee.saturating_mul(U256::from(2))), Some(base_fee)), +// AutoEthereumXcmFee::High => (Some(base_fee.saturating_mul(U256::from(3))), Some(base_fee.saturating_mul(U256::from(2)))), +// }; +// (None, max_fee, max_priority_fee) +// } +// }; +// match (gas_price, max_fee, max_priority_fee) { +// (Some(gas_price), None, None) => { +// // Legacy or Eip-2930 +// if let Some(access_list) = t.access_list { +// // Eip-2930 +// Some(TransactionV2::EIP2930(EIP2930Transaction { +// chain_id: 0, +// nonce: U256::MAX, // To be set at pallet level +// gas_price, +// gas_limit: t.gas_limit, +// action: t.action, +// value: t.value, +// input: t.input, +// access_list: from_tuple_to_access_list(access_list), +// odd_y_parity: true, +// r: H256::default(), +// s: H256::default(), +// })) +// } else { +// // Legacy +// Some(TransactionV2::Legacy(LegacyTransaction { +// nonce: U256::MAX, // To be set at pallet level +// gas_price, +// gas_limit: t.gas_limit, +// action: t.action, +// value: t.value, +// input: t.input, +// signature: TransactionSignature { +// v: TransactionRecoveryId(0), +// r: H256::default(), +// s: H256::default(), +// }, +// })) +// } +// } +// (None, Some(max_fee), _) => { +// // Eip-1559 +// Some(TransactionV2::EIP1559( +// EIP1559Transaction { +// chain_id: 0, +// nonce: U256::MAX, // To be set at pallet level +// max_fee_per_gas: max_fee, +// max_priority_fee_per_gas: max_priority_fee.unwrap_or_else(U256::zero), +// gas_limit: t.gas_limit, +// action: t.action, +// value: t.value, +// input: t.input, +// access_list: from_tuple_to_access_list(t.access_list.unwrap_or_default()), +// odd_y_parity: true, +// r: H256::default(), +// s: H256::default(), +// } +// )) +// } +// _ => return None +// } +// } +// } From 8ba20167b2d6007ff3b08653cc7ae22040e81431 Mon Sep 17 00:00:00 2001 From: tgmichel Date: Thu, 9 Jun 2022 09:25:54 +0200 Subject: [PATCH 02/15] Chain id is not needed (cherry picked from commit 83e503b233c9e712543f55a133edc5ee5b7459e9) --- frame/ethereum/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index f9bbbac36b..3e816329fe 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -328,7 +328,7 @@ pub mod pallet { evm_config: T::config(), block_gas_limit: T::BlockGasLimit::get(), base_fee, - chain_id: T::ChainId::get(), + chain_id: 0u64, is_transactional: true, }, transaction_data.into(), From 89837d7355e4cbec016a35c7fce378800b21d74a Mon Sep 17 00:00:00 2001 From: tgmichel Date: Thu, 9 Jun 2022 09:26:11 +0200 Subject: [PATCH 03/15] Pass reference instead cloning (cherry picked from commit 4adc37ebfa2e18e3d8b947d71e033897e761f9c5) --- frame/ethereum/src/xcm.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frame/ethereum/src/xcm.rs b/frame/ethereum/src/xcm.rs index de608d7224..4a33b2cc37 100644 --- a/frame/ethereum/src/xcm.rs +++ b/frame/ethereum/src/xcm.rs @@ -75,7 +75,7 @@ pub trait XcmToEthereum { impl XcmToEthereum for EthereumXcmTransaction { fn into_transaction_v2(&self, base_fee: U256, nonce: U256) -> Option { - let from_tuple_to_access_list = |t: Vec<(H160, Vec)>| -> AccessList { + let from_tuple_to_access_list = |t: &Vec<(H160, Vec)>| -> AccessList { t.iter() .map(|item| AccessListItem { address: item.0.clone(), @@ -118,7 +118,7 @@ impl XcmToEthereum for EthereumXcmTransaction { action: self.action, value: self.value, input: self.input.clone(), - access_list: from_tuple_to_access_list(access_list.to_vec()), + access_list: from_tuple_to_access_list(access_list), odd_y_parity: true, r: H256::default(), s: H256::default(), @@ -148,7 +148,7 @@ impl XcmToEthereum for EthereumXcmTransaction { value: self.value, input: self.input.clone(), access_list: if let Some(ref access_list) = self.access_list { - from_tuple_to_access_list(access_list.to_vec()) + from_tuple_to_access_list(access_list) } else { Vec::new() }, From d3f4cb81827bfb95646067dda455b6fffbda5d52 Mon Sep 17 00:00:00 2001 From: gorka Date: Thu, 9 Jun 2022 18:59:47 +0200 Subject: [PATCH 04/15] origin check (cherry picked from commit b683911c42aa8d2b9fe77fd087ac031cfd55c900) # Conflicts: # frame/ethereum/src/lib.rs --- frame/ethereum/src/lib.rs | 32 ++++++++++++++++++++++---------- frame/ethereum/src/xcm.rs | 7 ++++++- template/runtime/src/lib.rs | 1 + 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index 3e816329fe..91c0a874e4 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -49,7 +49,8 @@ use frame_support::{ weights::{DispatchInfo, Pays, PostDispatchInfo, Weight}, }; use frame_system::{pallet_prelude::OriginFor, CheckWeight, WeightInfo}; -use pallet_evm::{BlockHashMapping, FeeCalculator, GasWeightMapping, Runner}; +use pallet_evm::{BlockHashMapping, EnsureAddressOrigin, FeeCalculator, GasWeightMapping, Runner}; +use sha3::{Digest, Keccak256}; use sp_runtime::{ generic::DigestItem, traits::{DispatchInfoOf, Dispatchable, One, Saturating, UniqueSaturatedInto, Zero}, @@ -208,6 +209,8 @@ pub mod pallet { type Event: From + IsType<::Event>; /// How Ethereum state root is calculated. type StateRoot: Get; + /// Origin for xcm transact + type XcmTransactOrigin: EnsureAddressOrigin; } #[pallet::pallet] @@ -310,16 +313,22 @@ pub mod pallet { ))] pub fn transact_xcm( origin: OriginFor, + from: H160, xcm_transaction: xcm::EthereumXcmTransaction, ) -> DispatchResultWithPostInfo { use xcm::XcmToEthereum; + + T::XcmTransactOrigin::ensure_address_origin(&from, origin)?; // let source = frame_system::ensure_signed(origin)?; - let source = ensure_ethereum_transaction(origin)?; + // let source = ensure_ethereum_transaction(origin)?; + + let source = from; let (base_fee, base_fee_weight) = T::FeeCalculator::min_gas_price(); let (who, account_weight) = pallet_evm::Pallet::::account_basic(&source); - let transaction: Option = xcm_transaction.into_transaction_v2(base_fee, who.nonce); + let transaction: Option = + xcm_transaction.into_transaction_v2(base_fee, who.nonce); if let Some(transaction) = transaction { let transaction_data = Pallet::::transaction_data(&transaction); @@ -338,21 +347,24 @@ pub mod pallet { .and_then(|v| v.with_balance_for(&who)) .map_err(|e| sp_runtime::DispatchErrorWithPostInfo { post_info: PostDispatchInfo { - actual_weight: Some(base_fee_weight.saturating_add(account_weight)), - pays_fee: Pays::Yes, + actual_weight: Some(base_fee_weight.saturating_add(account_weight)), + pays_fee: Pays::Yes, }, - error: sp_runtime::DispatchError::Other("Failed to validate ethereum transaction") + error: sp_runtime::DispatchError::Other( + "Failed to validate ethereum transaction", + ), })?; Self::apply_validated_transaction(source, transaction) - } else { Err(sp_runtime::DispatchErrorWithPostInfo { post_info: PostDispatchInfo { - actual_weight: Some(base_fee_weight.saturating_add(account_weight)), - pays_fee: Pays::Yes, + actual_weight: Some(base_fee_weight.saturating_add(account_weight)), + pays_fee: Pays::Yes, }, - error: sp_runtime::DispatchError::Other("Cannot convert xcm payload to known type") + error: sp_runtime::DispatchError::Other( + "Cannot convert xcm payload to known type", + ), }) } } diff --git a/frame/ethereum/src/xcm.rs b/frame/ethereum/src/xcm.rs index 4a33b2cc37..1696257562 100644 --- a/frame/ethereum/src/xcm.rs +++ b/frame/ethereum/src/xcm.rs @@ -132,7 +132,12 @@ impl XcmToEthereum for EthereumXcmTransaction { action: self.action, value: self.value, input: self.input.clone(), - signature: TransactionSignature::new(42, H256::from_low_u64_be(1u64), H256::from_low_u64_be(1u64)).unwrap(), // TODO + signature: TransactionSignature::new( + 42, + H256::from_low_u64_be(1u64), + H256::from_low_u64_be(1u64), + ) + .unwrap(), // TODO })) } } diff --git a/template/runtime/src/lib.rs b/template/runtime/src/lib.rs index d372d5e43e..7540680421 100644 --- a/template/runtime/src/lib.rs +++ b/template/runtime/src/lib.rs @@ -347,6 +347,7 @@ impl pallet_evm::Config for Runtime { impl pallet_ethereum::Config for Runtime { type Event = Event; type StateRoot = pallet_ethereum::IntermediateStateRoot; + type XcmTransactOrigin = pallet_evm::EnsureAddressTruncated; } frame_support::parameter_types! { From fef197c0e15ce6d6f0e0a8a342245dc9f6cec688 Mon Sep 17 00:00:00 2001 From: gorka Date: Fri, 10 Jun 2022 11:06:41 +0200 Subject: [PATCH 05/15] versioned xcm ethereum transaction (cherry picked from commit cce4c61ff400525171b16c0c5998b19976d0eb2b) --- frame/ethereum/src/lib.rs | 17 +++++++++-------- frame/ethereum/src/xcm.rs | 25 ++++++++++++++++++++----- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index 91c0a874e4..5ea99763b1 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -308,21 +308,22 @@ pub mod pallet { } /// Xcm Transact an Ethereum transaction. - #[pallet::weight(::GasWeightMapping::gas_to_weight( - xcm_transaction.gas_limit.unique_saturated_into() - ))] + #[pallet::weight(::GasWeightMapping::gas_to_weight( { + match xcm_transaction { + xcm::EthereumXcmTransaction::V1(v1_tx) => v1_tx.gas_limit.unique_saturated_into() + } + }))] pub fn transact_xcm( origin: OriginFor, - from: H160, xcm_transaction: xcm::EthereumXcmTransaction, ) -> DispatchResultWithPostInfo { use xcm::XcmToEthereum; - T::XcmTransactOrigin::ensure_address_origin(&from, origin)?; - // let source = frame_system::ensure_signed(origin)?; - // let source = ensure_ethereum_transaction(origin)?; + let source = match &xcm_transaction { + xcm::EthereumXcmTransaction::V1(v1_tx) => v1_tx.from, + }; - let source = from; + T::XcmTransactOrigin::ensure_address_origin(&source, origin)?; let (base_fee, base_fee_weight) = T::FeeCalculator::min_gas_price(); let (who, account_weight) = pallet_evm::Pallet::::account_basic(&source); diff --git a/frame/ethereum/src/xcm.rs b/frame/ethereum/src/xcm.rs index 1696257562..daf44f25f2 100644 --- a/frame/ethereum/src/xcm.rs +++ b/frame/ethereum/src/xcm.rs @@ -52,9 +52,14 @@ pub enum EthereumXcmFee { Auto(AutoEthereumXcmFee), } +#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo)] +pub enum EthereumXcmTransaction { + V1(EthereumXcmTransactionV1), +} + /// Xcm transact's Ethereum transaction. #[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo)] -pub struct EthereumXcmTransaction { +pub struct EthereumXcmTransactionV1 { /// Gas limit to be consumed by EVM execution. pub gas_limit: U256, /// Fee configuration of choice. @@ -67,6 +72,8 @@ pub struct EthereumXcmTransaction { pub input: Vec, /// Map of addresses to be pre-paid to warm storage. pub access_list: Option)>>, + /// account calling the dispatchable + pub from: H160, } pub trait XcmToEthereum { @@ -74,6 +81,14 @@ pub trait XcmToEthereum { } impl XcmToEthereum for EthereumXcmTransaction { + fn into_transaction_v2(&self, base_fee: U256, nonce: U256) -> Option { + match self { + EthereumXcmTransaction::V1(v1_tx) => v1_tx.into_transaction_v2(base_fee, nonce), + } + } +} + +impl XcmToEthereum for EthereumXcmTransactionV1 { fn into_transaction_v2(&self, base_fee: U256, nonce: U256) -> Option { let from_tuple_to_access_list = |t: &Vec<(H160, Vec)>| -> AccessList { t.iter() @@ -120,8 +135,8 @@ impl XcmToEthereum for EthereumXcmTransaction { input: self.input.clone(), access_list: from_tuple_to_access_list(access_list), odd_y_parity: true, - r: H256::default(), - s: H256::default(), + r: H256::from_low_u64_be(1u64), + s: H256::from_low_u64_be(1u64), })) } else { // Legacy @@ -158,8 +173,8 @@ impl XcmToEthereum for EthereumXcmTransaction { Vec::new() }, odd_y_parity: true, - r: H256::default(), - s: H256::default(), + r: H256::from_low_u64_be(1u64), + s: H256::from_low_u64_be(1u64), })) } _ => return None, From 5373a44b80b7f9461221c4ad221c88ea635eb28b Mon Sep 17 00:00:00 2001 From: tgmichel Date: Fri, 10 Jun 2022 12:54:16 +0200 Subject: [PATCH 06/15] Move xcm to primitives (cherry picked from commit 55eb01b533dea6b909794e0c0955f7b63423e975) --- Cargo.lock | 12 ++ frame/ethereum/Cargo.toml | 2 + frame/ethereum/src/lib.rs | 14 +- frame/ethereum/src/xcm.rs | 286 -------------------------------------- primitives/xcm/Cargo.toml | 26 ++++ primitives/xcm/src/lib.rs | 191 +++++++++++++++++++++++++ 6 files changed, 237 insertions(+), 294 deletions(-) delete mode 100644 frame/ethereum/src/xcm.rs create mode 100644 primitives/xcm/Cargo.toml create mode 100644 primitives/xcm/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index bd93416cee..5e551ccf2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1884,6 +1884,17 @@ dependencies = [ "serde", ] +[[package]] +name = "fp-xcm" +version = "1.0.0-dev" +dependencies = [ + "ethereum", + "ethereum-types", + "parity-scale-codec", + "scale-info", + "sp-std", +] + [[package]] name = "frame-benchmarking" version = "4.0.0-dev" @@ -4414,6 +4425,7 @@ dependencies = [ "fp-rpc", "fp-self-contained", "fp-storage", + "fp-xcm", "frame-support", "frame-system", "hex", diff --git a/frame/ethereum/Cargo.toml b/frame/ethereum/Cargo.toml index 3be5c48357..99309146b9 100644 --- a/frame/ethereum/Cargo.toml +++ b/frame/ethereum/Cargo.toml @@ -36,6 +36,7 @@ fp-evm = { version = "3.0.0-dev", path = "../../primitives/evm", default-feature fp-rpc = { version = "3.0.0-dev", path = "../../primitives/rpc", default-features = false } fp-self-contained = { version = "1.0.0-dev", path = "../../primitives/self-contained", default-features = false } fp-storage = { version = "2.0.0", path = "../../primitives/storage", default-features = false } +fp-xcm = { version = "1.0.0-dev", path = "../../primitives/xcm", default-features = false } pallet-evm = { version = "6.0.0-dev", path = "../evm", default-features = false } [dev-dependencies] @@ -72,6 +73,7 @@ std = [ "fp-rpc/std", "fp-self-contained/std", "fp-storage/std", + "fp-xcm/std", "pallet-evm/std", ] runtime-benchmarks = [ diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index 5ea99763b1..847e58dc85 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -29,8 +29,6 @@ mod mock; #[cfg(all(feature = "std", test))] mod tests; -mod xcm; - use ethereum_types::{Bloom, BloomInput, H160, H256, H64, U256}; use evm::ExitReason; use fp_consensus::{PostLog, PreLog, FRONTIER_ENGINE_ID}; @@ -66,6 +64,7 @@ pub use ethereum::{ TransactionAction, TransactionV2 as Transaction, }; pub use fp_rpc::TransactionStatus; +pub use fp_xcm::{EthereumXcmTransaction, XcmToEthereum}; #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] pub enum RawOrigin { @@ -308,19 +307,18 @@ pub mod pallet { } /// Xcm Transact an Ethereum transaction. - #[pallet::weight(::GasWeightMapping::gas_to_weight( { + #[pallet::weight(::GasWeightMapping::gas_to_weight({ match xcm_transaction { - xcm::EthereumXcmTransaction::V1(v1_tx) => v1_tx.gas_limit.unique_saturated_into() + EthereumXcmTransaction::V1(v1_tx) => v1_tx.gas_limit.unique_saturated_into() } }))] pub fn transact_xcm( origin: OriginFor, - xcm_transaction: xcm::EthereumXcmTransaction, + xcm_transaction: EthereumXcmTransaction, ) -> DispatchResultWithPostInfo { - use xcm::XcmToEthereum; let source = match &xcm_transaction { - xcm::EthereumXcmTransaction::V1(v1_tx) => v1_tx.from, + EthereumXcmTransaction::V1(v1_tx) => v1_tx.from, }; T::XcmTransactOrigin::ensure_address_origin(&source, origin)?; @@ -346,7 +344,7 @@ pub mod pallet { .validate_in_block_for(&who) .and_then(|v| v.with_base_fee()) .and_then(|v| v.with_balance_for(&who)) - .map_err(|e| sp_runtime::DispatchErrorWithPostInfo { + .map_err(|_| sp_runtime::DispatchErrorWithPostInfo { post_info: PostDispatchInfo { actual_weight: Some(base_fee_weight.saturating_add(account_weight)), pays_fee: Pays::Yes, diff --git a/frame/ethereum/src/xcm.rs b/frame/ethereum/src/xcm.rs deleted file mode 100644 index daf44f25f2..0000000000 --- a/frame/ethereum/src/xcm.rs +++ /dev/null @@ -1,286 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// This file is part of Frontier. -// -// Copyright (c) 2020-2022 Parity Technologies (UK) Ltd. -// -// 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. -use ethereum::{ - AccessList, AccessListItem, EIP1559Transaction, EIP2930Transaction, LegacyTransaction, - TransactionAction, TransactionRecoveryId, TransactionSignature, TransactionV2, -}; -use ethereum_types::{H160, H256, U256}; -use fp_evm::FeeCalculator; -use frame_support::codec::{Decode, Encode}; -use sp_std::vec::Vec; - -#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo)] -/// Manually sets a gas fee. -pub struct ManualEthereumXcmFee { - /// Legacy or Eip-2930 - pub gas_price: Option, - /// Eip-1559 - pub max_fee_per_gas: Option, - /// Eip-1559 - pub max_priority_fee_per_gas: Option, -} - -#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo)] -/// Authomatic gas fee based on the current on-chain values. -/// Will always produce an Eip-1559 transaction. -pub enum AutoEthereumXcmFee { - /// base_fee_per_gas = BaseFee - Low, - /// max_fee_per_gas = 2 * BaseFee, max_priority_fee_per_gas = BaseFee - Medium, - /// max_fee_per_gas = 3 * BaseFee, max_priority_fee_per_gas = 2 * BaseFee - High, -} - -#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo)] -pub enum EthereumXcmFee { - Manual(ManualEthereumXcmFee), - Auto(AutoEthereumXcmFee), -} - -#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo)] -pub enum EthereumXcmTransaction { - V1(EthereumXcmTransactionV1), -} - -/// Xcm transact's Ethereum transaction. -#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo)] -pub struct EthereumXcmTransactionV1 { - /// Gas limit to be consumed by EVM execution. - pub gas_limit: U256, - /// Fee configuration of choice. - pub fee_payment: EthereumXcmFee, - /// Either a Call (the callee, account or contract address) or Create (currently unsupported). - pub action: TransactionAction, - /// Value to be transfered. - pub value: U256, - /// Input data for a contract call. - pub input: Vec, - /// Map of addresses to be pre-paid to warm storage. - pub access_list: Option)>>, - /// account calling the dispatchable - pub from: H160, -} - -pub trait XcmToEthereum { - fn into_transaction_v2(&self, base_fee: U256, nonce: U256) -> Option; -} - -impl XcmToEthereum for EthereumXcmTransaction { - fn into_transaction_v2(&self, base_fee: U256, nonce: U256) -> Option { - match self { - EthereumXcmTransaction::V1(v1_tx) => v1_tx.into_transaction_v2(base_fee, nonce), - } - } -} - -impl XcmToEthereum for EthereumXcmTransactionV1 { - fn into_transaction_v2(&self, base_fee: U256, nonce: U256) -> Option { - let from_tuple_to_access_list = |t: &Vec<(H160, Vec)>| -> AccessList { - t.iter() - .map(|item| AccessListItem { - address: item.0.clone(), - storage_keys: item.1.clone(), - }) - .collect::>() - }; - - let (gas_price, max_fee, max_priority_fee) = match &self.fee_payment { - EthereumXcmFee::Manual(fee_config) => ( - fee_config.gas_price, - fee_config.max_fee_per_gas, - fee_config.max_priority_fee_per_gas, - ), - EthereumXcmFee::Auto(auto_mode) => { - let (max_fee, max_priority_fee) = match auto_mode { - AutoEthereumXcmFee::Low => (Some(base_fee), None), - AutoEthereumXcmFee::Medium => ( - Some(base_fee.saturating_mul(U256::from(2u64))), - Some(base_fee), - ), - AutoEthereumXcmFee::High => ( - Some(base_fee.saturating_mul(U256::from(3u64))), - Some(base_fee.saturating_mul(U256::from(2u64))), - ), - }; - (None, max_fee, max_priority_fee) - } - }; - match (gas_price, max_fee, max_priority_fee) { - (Some(gas_price), None, None) => { - // Legacy or Eip-2930 - if let Some(ref access_list) = self.access_list { - // Eip-2930 - Some(TransactionV2::EIP2930(EIP2930Transaction { - chain_id: 0, - nonce, - gas_price, - gas_limit: self.gas_limit, - action: self.action, - value: self.value, - input: self.input.clone(), - access_list: from_tuple_to_access_list(access_list), - odd_y_parity: true, - r: H256::from_low_u64_be(1u64), - s: H256::from_low_u64_be(1u64), - })) - } else { - // Legacy - Some(TransactionV2::Legacy(LegacyTransaction { - nonce, - gas_price, - gas_limit: self.gas_limit, - action: self.action, - value: self.value, - input: self.input.clone(), - signature: TransactionSignature::new( - 42, - H256::from_low_u64_be(1u64), - H256::from_low_u64_be(1u64), - ) - .unwrap(), // TODO - })) - } - } - (None, Some(max_fee), _) => { - // Eip-1559 - Some(TransactionV2::EIP1559(EIP1559Transaction { - chain_id: 0, - nonce, - max_fee_per_gas: max_fee, - max_priority_fee_per_gas: max_priority_fee.unwrap_or_else(U256::zero), - gas_limit: self.gas_limit, - action: self.action, - value: self.value, - input: self.input.clone(), - access_list: if let Some(ref access_list) = self.access_list { - from_tuple_to_access_list(access_list) - } else { - Vec::new() - }, - odd_y_parity: true, - r: H256::from_low_u64_be(1u64), - s: H256::from_low_u64_be(1u64), - })) - } - _ => return None, - } - } -} - -// /// Xcm transact's Ethereum transaction. -// #[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, scale_info::TypeInfo)] -// #[scale_info(skip_type_params(T))] -// pub struct EthereumXcmTransaction { -// /// Gas limit to be consumed by EVM execution. -// pub gas_limit: U256, -// /// Fee configuration of choice. -// pub fee_payment: EthereumXcmFee, -// /// Either a Call (the callee, account or contract address) or Create (currently unsupported). -// pub action: TransactionAction, -// /// Value to be transfered. -// pub value: U256, -// /// Input data for a contract call. -// pub input: Vec, -// /// Map of addresses to be pre-paid to warm storage. -// pub access_list: Option)>>, -// _marker: sp_std::marker::PhantomData, -// } - -// impl From> for Option { -// fn from(t: EthereumXcmTransaction) -> Self { - -// let from_tuple_to_access_list = |t: Vec<(H160, Vec)>| -> AccessList { -// t.iter().map(|item| AccessListItem { -// address: item.0, -// storage_keys: item.1, - -// }).collect::>() -// }; - -// let (gas_price, max_fee, max_priority_fee) = match t.fee_payment { -// EthereumXcmFee::Manual(fee_config) => { -// (fee_config.gas_price, fee_config.max_fee_per_gas, fee_config.max_priority_fee_per_gas) -// }, -// EthereumXcmFee::Auto(auto_mode) => { -// let (base_fee, _) = T::FeeCalculator::min_gas_price(); -// let (max_fee, max_priority_fee) = match auto_mode { -// AutoEthereumXcmFee::Low => (Some(base_fee), None), -// AutoEthereumXcmFee::Medium => (Some(base_fee.saturating_mul(U256::from(2))), Some(base_fee)), -// AutoEthereumXcmFee::High => (Some(base_fee.saturating_mul(U256::from(3))), Some(base_fee.saturating_mul(U256::from(2)))), -// }; -// (None, max_fee, max_priority_fee) -// } -// }; -// match (gas_price, max_fee, max_priority_fee) { -// (Some(gas_price), None, None) => { -// // Legacy or Eip-2930 -// if let Some(access_list) = t.access_list { -// // Eip-2930 -// Some(TransactionV2::EIP2930(EIP2930Transaction { -// chain_id: 0, -// nonce: U256::MAX, // To be set at pallet level -// gas_price, -// gas_limit: t.gas_limit, -// action: t.action, -// value: t.value, -// input: t.input, -// access_list: from_tuple_to_access_list(access_list), -// odd_y_parity: true, -// r: H256::default(), -// s: H256::default(), -// })) -// } else { -// // Legacy -// Some(TransactionV2::Legacy(LegacyTransaction { -// nonce: U256::MAX, // To be set at pallet level -// gas_price, -// gas_limit: t.gas_limit, -// action: t.action, -// value: t.value, -// input: t.input, -// signature: TransactionSignature { -// v: TransactionRecoveryId(0), -// r: H256::default(), -// s: H256::default(), -// }, -// })) -// } -// } -// (None, Some(max_fee), _) => { -// // Eip-1559 -// Some(TransactionV2::EIP1559( -// EIP1559Transaction { -// chain_id: 0, -// nonce: U256::MAX, // To be set at pallet level -// max_fee_per_gas: max_fee, -// max_priority_fee_per_gas: max_priority_fee.unwrap_or_else(U256::zero), -// gas_limit: t.gas_limit, -// action: t.action, -// value: t.value, -// input: t.input, -// access_list: from_tuple_to_access_list(t.access_list.unwrap_or_default()), -// odd_y_parity: true, -// r: H256::default(), -// s: H256::default(), -// } -// )) -// } -// _ => return None -// } -// } -// } diff --git a/primitives/xcm/Cargo.toml b/primitives/xcm/Cargo.toml new file mode 100644 index 0000000000..9ab1ccb750 --- /dev/null +++ b/primitives/xcm/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "fp-xcm" +version = "1.0.0-dev" +authors = ["Parity Technologies , + /// Eip-1559 + pub max_fee_per_gas: Option, + /// Eip-1559 + pub max_priority_fee_per_gas: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, TypeInfo)] +/// Authomatic gas fee based on the current on-chain values. +/// Will always produce an Eip-1559 transaction. +pub enum AutoEthereumXcmFee { + /// base_fee_per_gas = BaseFee + Low, + /// max_fee_per_gas = 2 * BaseFee, max_priority_fee_per_gas = BaseFee + Medium, + /// max_fee_per_gas = 3 * BaseFee, max_priority_fee_per_gas = 2 * BaseFee + High, +} + +/// Xcm transact's Ethereum transaction configurable fee. +#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, TypeInfo)] +pub enum EthereumXcmFee { + Manual(ManualEthereumXcmFee), + Auto(AutoEthereumXcmFee), +} + +/// Xcm transact's Ethereum transaction. +#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, TypeInfo)] +pub enum EthereumXcmTransaction { + V1(EthereumXcmTransactionV1), +} + +/// Value for `r` and `s` for the invalid signature included in Xcm transact's Ethereum transaction. +pub fn rs_id() -> H256 { + H256::from_low_u64_be(1u64) +} + +#[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, TypeInfo)] +pub struct EthereumXcmTransactionV1 { + /// Gas limit to be consumed by EVM execution. + pub gas_limit: U256, + /// Fee configuration of choice. + pub fee_payment: EthereumXcmFee, + /// Either a Call (the callee, account or contract address) or Create (currently unsupported). + pub action: TransactionAction, + /// Value to be transfered. + pub value: U256, + /// Input data for a contract call. + pub input: Vec, + /// Map of addresses to be pre-paid to warm storage. + pub access_list: Option)>>, + /// account calling the dispatchable + pub from: H160, +} + +pub trait XcmToEthereum { + fn into_transaction_v2(&self, base_fee: U256, nonce: U256) -> Option; +} + +impl XcmToEthereum for EthereumXcmTransaction { + fn into_transaction_v2(&self, base_fee: U256, nonce: U256) -> Option { + match self { + EthereumXcmTransaction::V1(v1_tx) => v1_tx.into_transaction_v2(base_fee, nonce), + } + } +} + +impl XcmToEthereum for EthereumXcmTransactionV1 { + fn into_transaction_v2(&self, base_fee: U256, nonce: U256) -> Option { + let from_tuple_to_access_list = |t: &Vec<(H160, Vec)>| -> AccessList { + t.iter() + .map(|item| AccessListItem { + address: item.0.clone(), + storage_keys: item.1.clone(), + }) + .collect::>() + }; + + let (gas_price, max_fee, max_priority_fee) = match &self.fee_payment { + EthereumXcmFee::Manual(fee_config) => ( + fee_config.gas_price, + fee_config.max_fee_per_gas, + fee_config.max_priority_fee_per_gas, + ), + EthereumXcmFee::Auto(auto_mode) => { + let (max_fee, max_priority_fee) = match auto_mode { + AutoEthereumXcmFee::Low => (Some(base_fee), None), + AutoEthereumXcmFee::Medium => ( + Some(base_fee.saturating_mul(U256::from(2u64))), + Some(base_fee), + ), + AutoEthereumXcmFee::High => ( + Some(base_fee.saturating_mul(U256::from(3u64))), + Some(base_fee.saturating_mul(U256::from(2u64))), + ), + }; + (None, max_fee, max_priority_fee) + } + }; + match (gas_price, max_fee, max_priority_fee) { + (Some(gas_price), None, None) => { + // Legacy or Eip-2930 + if let Some(ref access_list) = self.access_list { + // Eip-2930 + Some(TransactionV2::EIP2930(EIP2930Transaction { + chain_id: 0, + nonce, + gas_price, + gas_limit: self.gas_limit, + action: self.action, + value: self.value, + input: self.input.clone(), + access_list: from_tuple_to_access_list(access_list), + odd_y_parity: true, + r: rs_id(), + s: rs_id(), + })) + } else { + // Legacy + Some(TransactionV2::Legacy(LegacyTransaction { + nonce, + gas_price, + gas_limit: self.gas_limit, + action: self.action, + value: self.value, + input: self.input.clone(), + signature: TransactionSignature::new( + 42, + rs_id(), + rs_id(), + ) + .unwrap(), // TODO + })) + } + } + (None, Some(max_fee), _) => { + // Eip-1559 + Some(TransactionV2::EIP1559(EIP1559Transaction { + chain_id: 0, + nonce, + max_fee_per_gas: max_fee, + max_priority_fee_per_gas: max_priority_fee.unwrap_or_else(U256::zero), + gas_limit: self.gas_limit, + action: self.action, + value: self.value, + input: self.input.clone(), + access_list: if let Some(ref access_list) = self.access_list { + from_tuple_to_access_list(access_list) + } else { + Vec::new() + }, + odd_y_parity: true, + r: rs_id(), + s: rs_id(), + })) + } + _ => return None, + } + } +} From 8dd891eb34563b5b04efe2e3a8920090c5961522 Mon Sep 17 00:00:00 2001 From: gorka Date: Fri, 10 Jun 2022 13:10:36 +0200 Subject: [PATCH 07/15] tests for primitives (cherry picked from commit dcc01ce07df0e0650c7865b6a6edbbc99218f569) --- frame/ethereum/src/lib.rs | 1 - frame/ethereum/src/mock.rs | 1 + primitives/xcm/src/lib.rs | 119 ++++++++++++++++++++++++++++++++++--- 3 files changed, 111 insertions(+), 10 deletions(-) diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index 847e58dc85..1c52bf33db 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -316,7 +316,6 @@ pub mod pallet { origin: OriginFor, xcm_transaction: EthereumXcmTransaction, ) -> DispatchResultWithPostInfo { - let source = match &xcm_transaction { EthereumXcmTransaction::V1(v1_tx) => v1_tx.from, }; diff --git a/frame/ethereum/src/mock.rs b/frame/ethereum/src/mock.rs index 3b413d3424..b5f614fdd8 100644 --- a/frame/ethereum/src/mock.rs +++ b/frame/ethereum/src/mock.rs @@ -173,6 +173,7 @@ impl pallet_evm::Config for Test { impl crate::Config for Test { type Event = Event; type StateRoot = IntermediateStateRoot; + type XcmTransactOrigin = pallet_evm::EnsureAddressTruncated; } impl fp_self_contained::SelfContainedCall for Call { diff --git a/primitives/xcm/src/lib.rs b/primitives/xcm/src/lib.rs index 839cabc2d7..b8e8cebdab 100644 --- a/primitives/xcm/src/lib.rs +++ b/primitives/xcm/src/lib.rs @@ -16,14 +16,14 @@ // limitations under the License. #![cfg_attr(not(feature = "std"), no_std)] +use codec::{Decode, Encode}; use ethereum::{ AccessList, AccessListItem, EIP1559Transaction, EIP2930Transaction, LegacyTransaction, TransactionAction, TransactionSignature, TransactionV2, }; use ethereum_types::{H160, H256, U256}; -use codec::{Decode, Encode}; -use sp_std::vec::Vec; use scale_info::TypeInfo; +use sp_std::vec::Vec; #[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, TypeInfo)] /// Manually sets a gas fee. @@ -63,7 +63,7 @@ pub enum EthereumXcmTransaction { /// Value for `r` and `s` for the invalid signature included in Xcm transact's Ethereum transaction. pub fn rs_id() -> H256 { - H256::from_low_u64_be(1u64) + H256::from_low_u64_be(1u64) } #[derive(Clone, Debug, Eq, PartialEq, Encode, Decode, TypeInfo)] @@ -155,12 +155,7 @@ impl XcmToEthereum for EthereumXcmTransactionV1 { action: self.action, value: self.value, input: self.input.clone(), - signature: TransactionSignature::new( - 42, - rs_id(), - rs_id(), - ) - .unwrap(), // TODO + signature: TransactionSignature::new(42, rs_id(), rs_id()).unwrap(), // TODO })) } } @@ -189,3 +184,109 @@ impl XcmToEthereum for EthereumXcmTransactionV1 { } } } + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_into_ethereum_tx_with_low_fee() { + let xcm_transaction = EthereumXcmTransactionV1 { + gas_limit: U256::from(1), + fee_payment: EthereumXcmFee::Auto(AutoEthereumXcmFee::Low), + action: TransactionAction::Create, + value: U256::from(0), + input: vec![1u8], + access_list: None, + from: H160::default(), + }; + let nonce = U256::from(0); + let base_fee = U256::from(1); + let expected_tx = Some(TransactionV2::EIP1559(EIP1559Transaction { + chain_id: 0, + nonce, + max_fee_per_gas: base_fee, + max_priority_fee_per_gas: U256::from(0), + gas_limit: U256::from(1), + action: TransactionAction::Create, + value: U256::from(0), + input: vec![1u8], + access_list: vec![], + odd_y_parity: true, + r: H256::from_low_u64_be(1u64), + s: H256::from_low_u64_be(1u64), + })); + + assert_eq!( + xcm_transaction.into_transaction_v2(base_fee, nonce), + expected_tx + ); + } + + #[test] + fn test_into_ethereum_tx_with_medium_fee() { + let xcm_transaction = EthereumXcmTransactionV1 { + gas_limit: U256::from(1), + fee_payment: EthereumXcmFee::Auto(AutoEthereumXcmFee::Medium), + action: TransactionAction::Create, + value: U256::from(0), + input: vec![1u8], + access_list: None, + from: H160::default(), + }; + let nonce = U256::from(0); + let base_fee = U256::from(1); + let expected_tx = Some(TransactionV2::EIP1559(EIP1559Transaction { + chain_id: 0, + nonce, + max_fee_per_gas: base_fee * 2, + max_priority_fee_per_gas: base_fee, + gas_limit: U256::from(1), + action: TransactionAction::Create, + value: U256::from(0), + input: vec![1u8], + access_list: vec![], + odd_y_parity: true, + r: H256::from_low_u64_be(1u64), + s: H256::from_low_u64_be(1u64), + })); + + assert_eq!( + xcm_transaction.into_transaction_v2(base_fee, nonce), + expected_tx + ); + } + + #[test] + fn test_into_ethereum_tx_with_high_fee() { + let xcm_transaction = EthereumXcmTransactionV1 { + gas_limit: U256::from(1), + fee_payment: EthereumXcmFee::Auto(AutoEthereumXcmFee::High), + action: TransactionAction::Create, + value: U256::from(0), + input: vec![1u8], + access_list: None, + from: H160::default(), + }; + let nonce = U256::from(0); + let base_fee = U256::from(1); + let expected_tx = Some(TransactionV2::EIP1559(EIP1559Transaction { + chain_id: 0, + nonce, + max_fee_per_gas: base_fee * 3, + max_priority_fee_per_gas: base_fee * 2, + gas_limit: U256::from(1), + action: TransactionAction::Create, + value: U256::from(0), + input: vec![1u8], + access_list: vec![], + odd_y_parity: true, + r: H256::from_low_u64_be(1u64), + s: H256::from_low_u64_be(1u64), + })); + + assert_eq!( + xcm_transaction.into_transaction_v2(base_fee, nonce), + expected_tx + ); + } +} From ad1746c9e1e6e366fd06d0a4a93f40e927ab7a14 Mon Sep 17 00:00:00 2001 From: gorka Date: Fri, 10 Jun 2022 16:11:55 +0200 Subject: [PATCH 08/15] Change to XCM Origin (cherry picked from commit 6d6713f2f2688b873eea10e551c11bbd01b3b6a9) --- frame/ethereum/src/lib.rs | 40 ++++++++++++++++++++++++++++++++------- primitives/xcm/src/lib.rs | 5 ----- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index 1c52bf33db..f6e642fd64 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -69,6 +69,7 @@ pub use fp_xcm::{EthereumXcmTransaction, XcmToEthereum}; #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] pub enum RawOrigin { EthereumTransaction(H160), + XcmEthereumTransaction(H160), } pub fn ensure_ethereum_transaction(o: OuterOrigin) -> Result @@ -81,6 +82,16 @@ where } } +pub fn ensure_xcm_ethereum_transaction(o: OuterOrigin) -> Result +where + OuterOrigin: Into>, +{ + match o.into() { + Ok(RawOrigin::XcmEthereumTransaction(n)) => Ok(n), + _ => Err("bad origin: expected to be a xcm Ethereum transaction"), + } +} + #[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)] struct TransactionData { action: TransactionAction, @@ -122,8 +133,9 @@ impl> + From> EnsureOrigin { type Success = H160; fn try_origin(o: O) -> Result { - o.into().map(|o| match o { - RawOrigin::EthereumTransaction(id) => id, + o.into().and_then(|o| match o { + RawOrigin::EthereumTransaction(id) => Ok(id), + _ => Err(o.into()), }) } @@ -133,6 +145,24 @@ impl> + From> EnsureOrigin } } +pub struct EnsureXcmEthereumTransaction; +impl> + From> EnsureOrigin + for EnsureXcmEthereumTransaction +{ + type Success = H160; + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + RawOrigin::XcmEthereumTransaction(id) => Ok(id), + _ => Err(o.into()), + }) + } + + #[cfg(feature = "runtime-benchmarks")] + fn successful_origin() -> O { + O::from(RawOrigin::XcmEthereumTransaction(Default::default())) + } +} + impl Call where OriginFor: Into>>, @@ -316,11 +346,7 @@ pub mod pallet { origin: OriginFor, xcm_transaction: EthereumXcmTransaction, ) -> DispatchResultWithPostInfo { - let source = match &xcm_transaction { - EthereumXcmTransaction::V1(v1_tx) => v1_tx.from, - }; - - T::XcmTransactOrigin::ensure_address_origin(&source, origin)?; + let source = ensure_xcm_ethereum_transaction(origin)?; let (base_fee, base_fee_weight) = T::FeeCalculator::min_gas_price(); let (who, account_weight) = pallet_evm::Pallet::::account_basic(&source); diff --git a/primitives/xcm/src/lib.rs b/primitives/xcm/src/lib.rs index b8e8cebdab..e58a46ce68 100644 --- a/primitives/xcm/src/lib.rs +++ b/primitives/xcm/src/lib.rs @@ -80,8 +80,6 @@ pub struct EthereumXcmTransactionV1 { pub input: Vec, /// Map of addresses to be pre-paid to warm storage. pub access_list: Option)>>, - /// account calling the dispatchable - pub from: H160, } pub trait XcmToEthereum { @@ -197,7 +195,6 @@ mod tests { value: U256::from(0), input: vec![1u8], access_list: None, - from: H160::default(), }; let nonce = U256::from(0); let base_fee = U256::from(1); @@ -231,7 +228,6 @@ mod tests { value: U256::from(0), input: vec![1u8], access_list: None, - from: H160::default(), }; let nonce = U256::from(0); let base_fee = U256::from(1); @@ -265,7 +261,6 @@ mod tests { value: U256::from(0), input: vec![1u8], access_list: None, - from: H160::default(), }; let nonce = U256::from(0); let base_fee = U256::from(1); From c63d0d3de038ca4fc30c4bf4884ee1a1d0cae201 Mon Sep 17 00:00:00 2001 From: gorka Date: Fri, 10 Jun 2022 16:22:11 +0200 Subject: [PATCH 09/15] make origin configurable (cherry picked from commit df7d606893ccb06b5c16f5caad720270340af579) --- frame/ethereum/src/lib.rs | 4 ++-- frame/ethereum/src/mock.rs | 2 +- template/runtime/src/lib.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index f6e642fd64..5bccc0de37 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -47,7 +47,7 @@ use frame_support::{ weights::{DispatchInfo, Pays, PostDispatchInfo, Weight}, }; use frame_system::{pallet_prelude::OriginFor, CheckWeight, WeightInfo}; -use pallet_evm::{BlockHashMapping, EnsureAddressOrigin, FeeCalculator, GasWeightMapping, Runner}; +use pallet_evm::{BlockHashMapping, FeeCalculator, GasWeightMapping, Runner}; use sha3::{Digest, Keccak256}; use sp_runtime::{ generic::DigestItem, @@ -239,7 +239,7 @@ pub mod pallet { /// How Ethereum state root is calculated. type StateRoot: Get; /// Origin for xcm transact - type XcmTransactOrigin: EnsureAddressOrigin; + type XcmEthereumOrigin: EnsureOrigin; } #[pallet::pallet] diff --git a/frame/ethereum/src/mock.rs b/frame/ethereum/src/mock.rs index b5f614fdd8..a604417328 100644 --- a/frame/ethereum/src/mock.rs +++ b/frame/ethereum/src/mock.rs @@ -173,7 +173,7 @@ impl pallet_evm::Config for Test { impl crate::Config for Test { type Event = Event; type StateRoot = IntermediateStateRoot; - type XcmTransactOrigin = pallet_evm::EnsureAddressTruncated; + type XcmEthereumOrigin = crate::EnsureXcmEthereumTransaction; } impl fp_self_contained::SelfContainedCall for Call { diff --git a/template/runtime/src/lib.rs b/template/runtime/src/lib.rs index 7540680421..c1ceb98af0 100644 --- a/template/runtime/src/lib.rs +++ b/template/runtime/src/lib.rs @@ -347,7 +347,7 @@ impl pallet_evm::Config for Runtime { impl pallet_ethereum::Config for Runtime { type Event = Event; type StateRoot = pallet_ethereum::IntermediateStateRoot; - type XcmTransactOrigin = pallet_evm::EnsureAddressTruncated; + type XcmEthereumOrigin = pallet_ethereum::EnsureXcmEthereumTransaction; } frame_support::parameter_types! { From d8b4f56684ca59b7b1f0443962a23da62ec66097 Mon Sep 17 00:00:00 2001 From: gorka Date: Mon, 13 Jun 2022 11:12:19 +0200 Subject: [PATCH 10/15] add more tests to primitives (cherry picked from commit f83e027d40c96a838fddd1dd3fb122c0a5ac7657) --- primitives/xcm/src/lib.rs | 77 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/primitives/xcm/src/lib.rs b/primitives/xcm/src/lib.rs index e58a46ce68..81ad5cbb1d 100644 --- a/primitives/xcm/src/lib.rs +++ b/primitives/xcm/src/lib.rs @@ -284,4 +284,81 @@ mod tests { expected_tx ); } + #[test] + fn test_legacy() { + let xcm_transaction = EthereumXcmTransactionV1 { + gas_limit: U256::from(1), + fee_payment: EthereumXcmFee::Manual(ManualEthereumXcmFee { + gas_price: Some(U256::from(1)), + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + }), + action: TransactionAction::Create, + value: U256::from(0), + input: vec![1u8], + access_list: None, + }; + let nonce = U256::from(0); + let gas_price = U256::from(1); + let expected_tx = Some(TransactionV2::Legacy(LegacyTransaction { + nonce, + gas_price, + gas_limit: U256::from(1), + action: TransactionAction::Create, + value: U256::from(0), + input: vec![1u8], + signature: TransactionSignature::new(42, rs_id(), rs_id()).unwrap(), + })); + + assert_eq!( + xcm_transaction.into_transaction_v2(gas_price, nonce), + expected_tx + ); + } + #[test] + fn test_eip_2930() { + let access_list = Some(vec![(H160::default(), vec![H256::default()])]); + let from_tuple_to_access_list = |t: &Vec<(H160, Vec)>| -> AccessList { + t.iter() + .map(|item| AccessListItem { + address: item.0.clone(), + storage_keys: item.1.clone(), + }) + .collect::>() + }; + + let xcm_transaction = EthereumXcmTransactionV1 { + gas_limit: U256::from(1), + fee_payment: EthereumXcmFee::Manual(ManualEthereumXcmFee { + gas_price: Some(U256::from(1)), + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + }), + action: TransactionAction::Create, + value: U256::from(0), + input: vec![1u8], + access_list: access_list.clone(), + }; + + let nonce = U256::from(0); + let gas_price = U256::from(1); + let expected_tx = Some(TransactionV2::EIP2930(EIP2930Transaction { + chain_id: 0, + nonce, + gas_price, + gas_limit: U256::from(1), + action: TransactionAction::Create, + value: U256::from(0), + input: vec![1u8], + access_list: from_tuple_to_access_list(&access_list.unwrap()), + odd_y_parity: true, + r: H256::from_low_u64_be(1u64), + s: H256::from_low_u64_be(1u64), + })); + + assert_eq!( + xcm_transaction.into_transaction_v2(gas_price, nonce), + expected_tx + ); + } } From 0245ede95f09dd4473fb3f5399e8e2dab765d0e5 Mon Sep 17 00:00:00 2001 From: gorka Date: Mon, 13 Jun 2022 12:49:35 +0200 Subject: [PATCH 11/15] Add more tests (cherry picked from commit b8043b3c48d4d3587a09ea336da28cff25c12ebf) --- frame/ethereum/src/tests/eip1559.rs | 78 ++++++++++++++++++++++++++ frame/ethereum/src/tests/eip2930.rs | 85 +++++++++++++++++++++++++++++ frame/ethereum/src/tests/legacy.rs | 81 +++++++++++++++++++++++++++ primitives/xcm/src/lib.rs | 4 ++ 4 files changed, 248 insertions(+) diff --git a/frame/ethereum/src/tests/eip1559.rs b/frame/ethereum/src/tests/eip1559.rs index fa8c1b9836..2cb336df18 100644 --- a/frame/ethereum/src/tests/eip1559.rs +++ b/frame/ethereum/src/tests/eip1559.rs @@ -18,6 +18,14 @@ //! Consensus extension module tests for BABE consensus. use super::*; +use fp_xcm::{ + AutoEthereumXcmFee, EthereumXcmFee, EthereumXcmTransaction, EthereumXcmTransactionV1, +}; +use frame_support::{ + assert_noop, + weights::{Pays, PostDispatchInfo}, +}; +use sp_runtime::{DispatchError, DispatchErrorWithPostInfo}; fn eip1559_erc20_creation_unsigned_transaction() -> EIP1559UnsignedTransaction { EIP1559UnsignedTransaction { @@ -31,6 +39,33 @@ fn eip1559_erc20_creation_unsigned_transaction() -> EIP1559UnsignedTransaction { } } +fn xcm_evm_transfer_eip_1559_transaction(destination: H160, value: U256) -> EthereumXcmTransaction { + let access_list = Some(vec![(H160::default(), vec![H256::default()])]); + + EthereumXcmTransaction::V1(EthereumXcmTransactionV1 { + fee_payment: EthereumXcmFee::Auto(AutoEthereumXcmFee::Low), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Call(destination), + value, + input: vec![], + access_list: access_list.clone(), + }) +} + +fn xcm_erc20_creation_eip_1559_transaction() -> EthereumXcmTransaction { + let access_list = Some(vec![(H160::default(), vec![H256::default()])]); + + EthereumXcmTransaction::V1(EthereumXcmTransactionV1 { + fee_payment: EthereumXcmFee::Auto(AutoEthereumXcmFee::Low), + + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Create, + value: U256::zero(), + input: hex::decode(ERC20_CONTRACT_BYTECODE.trim_end()).unwrap(), + access_list: access_list.clone(), + }) +} + fn eip1559_erc20_creation_transaction(account: &AccountInfo) -> Transaction { eip1559_erc20_creation_unsigned_transaction().sign(&account.private_key, None) } @@ -329,3 +364,46 @@ fn call_should_handle_errors() { Ethereum::execute(alice.address, &t3, None).ok().unwrap(); }); } + +#[test] +fn test_transact_xcm_evm_transfer() { + let (pairs, mut ext) = new_test_ext(2); + let alice = &pairs[0]; + let bob = &pairs[1]; + + ext.execute_with(|| { + let balances_before = System::account(&bob.account_id); + Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(alice.address).into(), + xcm_evm_transfer_eip_1559_transaction(bob.address, U256::from(100)), + ) + .expect("Failed to execute transaction"); + + assert_eq!( + System::account(&bob.account_id).data.free, + balances_before.data.free + 100 + ); + }); +} + +#[test] +fn test_transact_xcm_create() { + let (pairs, mut ext) = new_test_ext(1); + let alice = &pairs[0]; + + ext.execute_with(|| { + assert_noop!( + Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(alice.address).into(), + xcm_erc20_creation_eip_1559_transaction() + ), + DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: Some(0), + pays_fee: Pays::Yes, + }, + error: DispatchError::Other("Cannot convert xcm payload to known type"), + } + ); + }); +} diff --git a/frame/ethereum/src/tests/eip2930.rs b/frame/ethereum/src/tests/eip2930.rs index e64fcd950f..3090c55205 100644 --- a/frame/ethereum/src/tests/eip2930.rs +++ b/frame/ethereum/src/tests/eip2930.rs @@ -18,6 +18,14 @@ //! Consensus extension module tests for BABE consensus. use super::*; +use fp_xcm::{ + EthereumXcmFee, EthereumXcmTransaction, EthereumXcmTransactionV1, ManualEthereumXcmFee, +}; +use frame_support::{ + assert_noop, + weights::{Pays, PostDispatchInfo}, +}; +use sp_runtime::{DispatchError, DispatchErrorWithPostInfo}; fn eip2930_erc20_creation_unsigned_transaction() -> EIP2930UnsignedTransaction { EIP2930UnsignedTransaction { @@ -30,6 +38,40 @@ fn eip2930_erc20_creation_unsigned_transaction() -> EIP2930UnsignedTransaction { } } +fn xcm_evm_transfer_eip_2930_transaction(destination: H160, value: U256) -> EthereumXcmTransaction { + let access_list = Some(vec![(H160::default(), vec![H256::default()])]); + + EthereumXcmTransaction::V1(EthereumXcmTransactionV1 { + fee_payment: EthereumXcmFee::Manual(ManualEthereumXcmFee { + gas_price: Some(U256::from(1)), + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + }), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Call(destination), + value, + input: vec![], + access_list: access_list.clone(), + }) +} + +fn xcm_erc20_creation_eip_2930_transaction() -> EthereumXcmTransaction { + let access_list = Some(vec![(H160::default(), vec![H256::default()])]); + + EthereumXcmTransaction::V1(EthereumXcmTransactionV1 { + fee_payment: EthereumXcmFee::Manual(ManualEthereumXcmFee { + gas_price: Some(U256::from(1)), + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + }), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Create, + value: U256::zero(), + input: hex::decode(ERC20_CONTRACT_BYTECODE.trim_end()).unwrap(), + access_list: access_list.clone(), + }) +} + fn eip2930_erc20_creation_transaction(account: &AccountInfo) -> Transaction { eip2930_erc20_creation_unsigned_transaction().sign(&account.private_key, None) } @@ -330,3 +372,46 @@ fn call_should_handle_errors() { Ethereum::execute(alice.address, &t3, None).ok().unwrap(); }); } + +#[test] +fn test_transact_xcm_evm_transfer() { + let (pairs, mut ext) = new_test_ext(2); + let alice = &pairs[0]; + let bob = &pairs[1]; + + ext.execute_with(|| { + let balances_before = System::account(&bob.account_id); + Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(alice.address).into(), + xcm_evm_transfer_eip_2930_transaction(bob.address, U256::from(100)), + ) + .expect("Failed to execute transaction"); + + assert_eq!( + System::account(&bob.account_id).data.free, + balances_before.data.free + 100 + ); + }); +} + +#[test] +fn test_transact_xcm_create() { + let (pairs, mut ext) = new_test_ext(1); + let alice = &pairs[0]; + + ext.execute_with(|| { + assert_noop!( + Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(alice.address).into(), + xcm_erc20_creation_eip_2930_transaction() + ), + DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: Some(0), + pays_fee: Pays::Yes, + }, + error: DispatchError::Other("Cannot convert xcm payload to known type"), + } + ); + }); +} diff --git a/frame/ethereum/src/tests/legacy.rs b/frame/ethereum/src/tests/legacy.rs index 5af2e2fe02..a1c4a7c422 100644 --- a/frame/ethereum/src/tests/legacy.rs +++ b/frame/ethereum/src/tests/legacy.rs @@ -18,6 +18,14 @@ //! Consensus extension module tests for BABE consensus. use super::*; +use fp_xcm::{ + EthereumXcmFee, EthereumXcmTransaction, EthereumXcmTransactionV1, ManualEthereumXcmFee, +}; +use frame_support::{ + assert_noop, + weights::{Pays, PostDispatchInfo}, +}; +use sp_runtime::{DispatchError, DispatchErrorWithPostInfo}; fn legacy_erc20_creation_unsigned_transaction() -> LegacyUnsignedTransaction { LegacyUnsignedTransaction { @@ -30,6 +38,36 @@ fn legacy_erc20_creation_unsigned_transaction() -> LegacyUnsignedTransaction { } } +fn xcm_evm_transfer_legacy_transaction(destination: H160, value: U256) -> EthereumXcmTransaction { + EthereumXcmTransaction::V1(EthereumXcmTransactionV1 { + fee_payment: EthereumXcmFee::Manual(ManualEthereumXcmFee { + gas_price: Some(U256::from(1)), + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + }), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Call(destination), + value, + input: vec![], + access_list: None, + }) +} + +fn xcm_erc20_creation_legacy_transaction() -> EthereumXcmTransaction { + EthereumXcmTransaction::V1(EthereumXcmTransactionV1 { + fee_payment: EthereumXcmFee::Manual(ManualEthereumXcmFee { + gas_price: Some(U256::from(1)), + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + }), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Create, + value: U256::zero(), + input: hex::decode(ERC20_CONTRACT_BYTECODE.trim_end()).unwrap(), + access_list: None, + }) +} + fn legacy_erc20_creation_transaction(account: &AccountInfo) -> Transaction { legacy_erc20_creation_unsigned_transaction().sign(&account.private_key) } @@ -330,3 +368,46 @@ fn call_should_handle_errors() { Ethereum::execute(alice.address, &t3, None).ok().unwrap(); }); } + +#[test] +fn test_transact_xcm_evm_transfer() { + let (pairs, mut ext) = new_test_ext(2); + let alice = &pairs[0]; + let bob = &pairs[1]; + + ext.execute_with(|| { + let balances_before = System::account(&bob.account_id); + Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(alice.address).into(), + xcm_evm_transfer_legacy_transaction(bob.address, U256::from(100)), + ) + .expect("Failed to execute transaction"); + + assert_eq!( + System::account(&bob.account_id).data.free, + balances_before.data.free + 100 + ); + }); +} + +#[test] +fn test_transact_xcm_create() { + let (pairs, mut ext) = new_test_ext(1); + let alice = &pairs[0]; + + ext.execute_with(|| { + assert_noop!( + Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(alice.address).into(), + xcm_erc20_creation_legacy_transaction() + ), + DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: Some(0), + pays_fee: Pays::Yes, + }, + error: DispatchError::Other("Cannot convert xcm payload to known type"), + } + ); + }); +} diff --git a/primitives/xcm/src/lib.rs b/primitives/xcm/src/lib.rs index 81ad5cbb1d..80e3ce9d5c 100644 --- a/primitives/xcm/src/lib.rs +++ b/primitives/xcm/src/lib.rs @@ -96,6 +96,10 @@ impl XcmToEthereum for EthereumXcmTransaction { impl XcmToEthereum for EthereumXcmTransactionV1 { fn into_transaction_v2(&self, base_fee: U256, nonce: U256) -> Option { + // We dont support creates for now + if self.action == TransactionAction::Create { + return None; + } let from_tuple_to_access_list = |t: &Vec<(H160, Vec)>| -> AccessList { t.iter() .map(|item| AccessListItem { From 329cb5e97d3dd3c157e3ab892ca522a1302ca7db Mon Sep 17 00:00:00 2001 From: tgmichel Date: Wed, 15 Jun 2022 10:27:23 +0200 Subject: [PATCH 12/15] Add test xcm contract call works (cherry picked from commit f3d73cd9dee70f3a3e82987720adf2f780a5e09f) --- frame/ethereum/src/tests/eip1559.rs | 102 ++++++++++++++++++++++----- frame/ethereum/src/tests/eip2930.rs | 103 ++++++++++++++++++++++++---- frame/ethereum/src/tests/legacy.rs | 97 ++++++++++++++++++++++---- 3 files changed, 258 insertions(+), 44 deletions(-) diff --git a/frame/ethereum/src/tests/eip1559.rs b/frame/ethereum/src/tests/eip1559.rs index 2cb336df18..02737a88cd 100644 --- a/frame/ethereum/src/tests/eip1559.rs +++ b/frame/ethereum/src/tests/eip1559.rs @@ -27,6 +27,17 @@ use frame_support::{ }; use sp_runtime::{DispatchError, DispatchErrorWithPostInfo}; +// pragma solidity ^0.6.6; +// contract Test { +// function foo() external pure returns (bool) { +// return true; +// } +// function bar() external pure { +// require(false, "error_msg"); +// } +// } +const CONTRACT: &str = "608060405234801561001057600080fd5b50610113806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c8063c2985578146037578063febb0f7e146057575b600080fd5b603d605f565b604051808215151515815260200191505060405180910390f35b605d6068565b005b60006001905090565b600060db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260098152602001807f6572726f725f6d7367000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b56fea2646970667358221220fde68a3968e0e99b16fabf9b2997a78218b32214031f8e07e2c502daf603a69e64736f6c63430006060033"; + fn eip1559_erc20_creation_unsigned_transaction() -> EIP1559UnsignedTransaction { EIP1559UnsignedTransaction { nonce: U256::zero(), @@ -40,21 +51,28 @@ fn eip1559_erc20_creation_unsigned_transaction() -> EIP1559UnsignedTransaction { } fn xcm_evm_transfer_eip_1559_transaction(destination: H160, value: U256) -> EthereumXcmTransaction { - let access_list = Some(vec![(H160::default(), vec![H256::default()])]); - EthereumXcmTransaction::V1(EthereumXcmTransactionV1 { fee_payment: EthereumXcmFee::Auto(AutoEthereumXcmFee::Low), gas_limit: U256::from(0x100000), action: ethereum::TransactionAction::Call(destination), value, input: vec![], - access_list: access_list.clone(), + access_list: None }) } -fn xcm_erc20_creation_eip_1559_transaction() -> EthereumXcmTransaction { - let access_list = Some(vec![(H160::default(), vec![H256::default()])]); +fn xcm_evm_call_eip_1559_transaction(destination: H160, input: Vec) -> EthereumXcmTransaction { + EthereumXcmTransaction::V1(EthereumXcmTransactionV1 { + fee_payment: EthereumXcmFee::Auto(AutoEthereumXcmFee::Low), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Call(destination), + value: U256::zero(), + input, + access_list: None, + }) +} +fn xcm_erc20_creation_eip_1559_transaction() -> EthereumXcmTransaction { EthereumXcmTransaction::V1(EthereumXcmTransactionV1 { fee_payment: EthereumXcmFee::Auto(AutoEthereumXcmFee::Low), @@ -62,7 +80,7 @@ fn xcm_erc20_creation_eip_1559_transaction() -> EthereumXcmTransaction { action: ethereum::TransactionAction::Create, value: U256::zero(), input: hex::decode(ERC20_CONTRACT_BYTECODE.trim_end()).unwrap(), - access_list: access_list.clone(), + access_list: None, }) } @@ -294,17 +312,6 @@ fn transaction_should_generate_correct_gas_used() { #[test] fn call_should_handle_errors() { - // pragma solidity ^0.6.6; - // contract Test { - // function foo() external pure returns (bool) { - // return true; - // } - // function bar() external pure { - // require(false, "error_msg"); - // } - // } - let contract: &str = "608060405234801561001057600080fd5b50610113806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c8063c2985578146037578063febb0f7e146057575b600080fd5b603d605f565b604051808215151515815260200191505060405180910390f35b605d6068565b005b60006001905090565b600060db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260098152602001807f6572726f725f6d7367000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b56fea2646970667358221220fde68a3968e0e99b16fabf9b2997a78218b32214031f8e07e2c502daf603a69e64736f6c63430006060033"; - let (pairs, mut ext) = new_test_ext(1); let alice = &pairs[0]; @@ -316,7 +323,7 @@ fn call_should_handle_errors() { gas_limit: U256::from(0x100000), action: ethereum::TransactionAction::Create, value: U256::zero(), - input: hex::decode(contract).unwrap(), + input: hex::decode(CONTRACT).unwrap(), } .sign(&alice.private_key, None); assert_ok!(Ethereum::execute(alice.address, &t, None,)); @@ -407,3 +414,62 @@ fn test_transact_xcm_create() { ); }); } + +#[test] +fn test_transact_xcm_evm_call_works() { + let (pairs, mut ext) = new_test_ext(2); + let alice = &pairs[0]; + let bob = &pairs[1]; + + ext.execute_with(|| { + let t = EIP1559UnsignedTransaction { + nonce: U256::zero(), + max_priority_fee_per_gas: U256::from(1), + max_fee_per_gas: U256::from(1), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Create, + value: U256::zero(), + input: hex::decode(CONTRACT).unwrap(), + } + .sign(&alice.private_key, None); + assert_ok!(Ethereum::execute(alice.address, &t, None,)); + + let contract_address = hex::decode("32dcab0ef3fb2de2fce1d2e0799d36239671f04a").unwrap(); + let foo = hex::decode("c2985578").unwrap(); + let bar = hex::decode("febb0f7e").unwrap(); + + let _ = Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(bob.address).into(), + xcm_evm_call_eip_1559_transaction(H160::from_slice(&contract_address), foo), + ).expect("Failed to call `foo`"); + + // Evm call failing still succesfully dispatched + let _ = Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(bob.address).into(), + xcm_evm_call_eip_1559_transaction(H160::from_slice(&contract_address), bar), + ).expect("Failed to call `bar`"); + + let pending = crate::Pending::::get(); + assert!(pending.len() == 2); + + // Transaction is in Pending storage, with nonce 0 and status 1 (evm succeed). + let (transaction_0, _, receipt_0) = &pending[0]; + match (transaction_0, receipt_0) { + (&crate::Transaction::EIP1559(ref t), &crate::Receipt::EIP1559(ref r)) => { + assert!(t.nonce == U256::from(0u8)); + assert!(r.status_code == 1u8); + }, + _ => unreachable!(), + } + + // Transaction is in Pending storage, with nonce 1 and status 0 (evm failed). + let (transaction_1, _, receipt_1) = &pending[1]; + match (transaction_1, receipt_1) { + (&crate::Transaction::EIP1559(ref t), &crate::Receipt::EIP1559(ref r)) => { + assert!(t.nonce == U256::from(1u8)); + assert!(r.status_code == 0u8); + }, + _ => unreachable!(), + } + }); +} diff --git a/frame/ethereum/src/tests/eip2930.rs b/frame/ethereum/src/tests/eip2930.rs index 3090c55205..3e1d450996 100644 --- a/frame/ethereum/src/tests/eip2930.rs +++ b/frame/ethereum/src/tests/eip2930.rs @@ -27,6 +27,17 @@ use frame_support::{ }; use sp_runtime::{DispatchError, DispatchErrorWithPostInfo}; +// pragma solidity ^0.6.6; +// contract Test { +// function foo() external pure returns (bool) { +// return true; +// } +// function bar() external pure { +// require(false, "error_msg"); +// } +// } +const CONTRACT: &str = "608060405234801561001057600080fd5b50610113806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c8063c2985578146037578063febb0f7e146057575b600080fd5b603d605f565b604051808215151515815260200191505060405180910390f35b605d6068565b005b60006001905090565b600060db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260098152602001807f6572726f725f6d7367000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b56fea2646970667358221220fde68a3968e0e99b16fabf9b2997a78218b32214031f8e07e2c502daf603a69e64736f6c63430006060033"; + fn eip2930_erc20_creation_unsigned_transaction() -> EIP2930UnsignedTransaction { EIP2930UnsignedTransaction { nonce: U256::zero(), @@ -51,7 +62,24 @@ fn xcm_evm_transfer_eip_2930_transaction(destination: H160, value: U256) -> Ethe action: ethereum::TransactionAction::Call(destination), value, input: vec![], - access_list: access_list.clone(), + access_list, + }) +} + +fn xcm_evm_call_eip_2930_transaction(destination: H160, input: Vec) -> EthereumXcmTransaction { + let access_list = Some(vec![(H160::default(), vec![H256::default()])]); + + EthereumXcmTransaction::V1(EthereumXcmTransactionV1 { + fee_payment: EthereumXcmFee::Manual(ManualEthereumXcmFee { + gas_price: Some(U256::from(1)), + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + }), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Call(destination), + value: U256::zero(), + input, + access_list, }) } @@ -68,7 +96,7 @@ fn xcm_erc20_creation_eip_2930_transaction() -> EthereumXcmTransaction { action: ethereum::TransactionAction::Create, value: U256::zero(), input: hex::decode(ERC20_CONTRACT_BYTECODE.trim_end()).unwrap(), - access_list: access_list.clone(), + access_list, }) } @@ -305,17 +333,6 @@ fn transaction_should_generate_correct_gas_used() { #[test] fn call_should_handle_errors() { - // pragma solidity ^0.6.6; - // contract Test { - // function foo() external pure returns (bool) { - // return true; - // } - // function bar() external pure { - // require(false, "error_msg"); - // } - // } - let contract: &str = "608060405234801561001057600080fd5b50610113806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c8063c2985578146037578063febb0f7e146057575b600080fd5b603d605f565b604051808215151515815260200191505060405180910390f35b605d6068565b005b60006001905090565b600060db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260098152602001807f6572726f725f6d7367000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b56fea2646970667358221220fde68a3968e0e99b16fabf9b2997a78218b32214031f8e07e2c502daf603a69e64736f6c63430006060033"; - let (pairs, mut ext) = new_test_ext(1); let alice = &pairs[0]; @@ -326,7 +343,7 @@ fn call_should_handle_errors() { gas_limit: U256::from(0x100000), action: ethereum::TransactionAction::Create, value: U256::zero(), - input: hex::decode(contract).unwrap(), + input: hex::decode(CONTRACT).unwrap(), } .sign(&alice.private_key, None); assert_ok!(Ethereum::execute(alice.address, &t, None,)); @@ -415,3 +432,61 @@ fn test_transact_xcm_create() { ); }); } + +#[test] +fn test_transact_xcm_evm_call_works() { + let (pairs, mut ext) = new_test_ext(2); + let alice = &pairs[0]; + let bob = &pairs[1]; + + ext.execute_with(|| { + let t = EIP2930UnsignedTransaction { + nonce: U256::zero(), + gas_price: U256::from(1), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Create, + value: U256::zero(), + input: hex::decode(CONTRACT).unwrap(), + } + .sign(&alice.private_key, None); + assert_ok!(Ethereum::execute(alice.address, &t, None,)); + + let contract_address = hex::decode("32dcab0ef3fb2de2fce1d2e0799d36239671f04a").unwrap(); + let foo = hex::decode("c2985578").unwrap(); + let bar = hex::decode("febb0f7e").unwrap(); + + let _ = Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(bob.address).into(), + xcm_evm_call_eip_2930_transaction(H160::from_slice(&contract_address), foo), + ).expect("Failed to call `foo`"); + + // Evm call failing still succesfully dispatched + let _ = Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(bob.address).into(), + xcm_evm_call_eip_2930_transaction(H160::from_slice(&contract_address), bar), + ).expect("Failed to call `bar`"); + + let pending = crate::Pending::::get(); + assert!(pending.len() == 2); + + // Transaction is in Pending storage, with nonce 0 and status 1 (evm succeed). + let (transaction_0, _, receipt_0) = &pending[0]; + match (transaction_0, receipt_0) { + (&crate::Transaction::EIP2930(ref t), &crate::Receipt::EIP2930(ref r)) => { + assert!(t.nonce == U256::from(0u8)); + assert!(r.status_code == 1u8); + }, + _ => unreachable!(), + } + + // Transaction is in Pending storage, with nonce 1 and status 0 (evm failed). + let (transaction_1, _, receipt_1) = &pending[1]; + match (transaction_1, receipt_1) { + (&crate::Transaction::EIP2930(ref t), &crate::Receipt::EIP2930(ref r)) => { + assert!(t.nonce == U256::from(1u8)); + assert!(r.status_code == 0u8); + }, + _ => unreachable!(), + } + }); +} diff --git a/frame/ethereum/src/tests/legacy.rs b/frame/ethereum/src/tests/legacy.rs index a1c4a7c422..4ccce35640 100644 --- a/frame/ethereum/src/tests/legacy.rs +++ b/frame/ethereum/src/tests/legacy.rs @@ -27,6 +27,17 @@ use frame_support::{ }; use sp_runtime::{DispatchError, DispatchErrorWithPostInfo}; +// pragma solidity ^0.6.6; +// contract Test { +// function foo() external pure returns (bool) { +// return true; +// } +// function bar() external pure { +// require(false, "error_msg"); +// } +// } +const CONTRACT: &str = "608060405234801561001057600080fd5b50610113806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c8063c2985578146037578063febb0f7e146057575b600080fd5b603d605f565b604051808215151515815260200191505060405180910390f35b605d6068565b005b60006001905090565b600060db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260098152602001807f6572726f725f6d7367000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b56fea2646970667358221220fde68a3968e0e99b16fabf9b2997a78218b32214031f8e07e2c502daf603a69e64736f6c63430006060033"; + fn legacy_erc20_creation_unsigned_transaction() -> LegacyUnsignedTransaction { LegacyUnsignedTransaction { nonce: U256::zero(), @@ -53,6 +64,21 @@ fn xcm_evm_transfer_legacy_transaction(destination: H160, value: U256) -> Ethere }) } +fn xcm_evm_call_eip_legacy_transaction(destination: H160, input: Vec) -> EthereumXcmTransaction { + EthereumXcmTransaction::V1(EthereumXcmTransactionV1 { + fee_payment: EthereumXcmFee::Manual(ManualEthereumXcmFee { + gas_price: Some(U256::from(1)), + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + }), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Call(destination), + value: U256::zero(), + input, + access_list: None, + }) +} + fn xcm_erc20_creation_legacy_transaction() -> EthereumXcmTransaction { EthereumXcmTransaction::V1(EthereumXcmTransactionV1 { fee_payment: EthereumXcmFee::Manual(ManualEthereumXcmFee { @@ -301,17 +327,6 @@ fn transaction_should_generate_correct_gas_used() { #[test] fn call_should_handle_errors() { - // pragma solidity ^0.6.6; - // contract Test { - // function foo() external pure returns (bool) { - // return true; - // } - // function bar() external pure { - // require(false, "error_msg"); - // } - // } - let contract: &str = "608060405234801561001057600080fd5b50610113806100206000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c8063c2985578146037578063febb0f7e146057575b600080fd5b603d605f565b604051808215151515815260200191505060405180910390f35b605d6068565b005b60006001905090565b600060db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260098152602001807f6572726f725f6d7367000000000000000000000000000000000000000000000081525060200191505060405180910390fd5b56fea2646970667358221220fde68a3968e0e99b16fabf9b2997a78218b32214031f8e07e2c502daf603a69e64736f6c63430006060033"; - let (pairs, mut ext) = new_test_ext(1); let alice = &pairs[0]; @@ -322,7 +337,7 @@ fn call_should_handle_errors() { gas_limit: U256::from(0x100000), action: ethereum::TransactionAction::Create, value: U256::zero(), - input: hex::decode(contract).unwrap(), + input: hex::decode(CONTRACT).unwrap(), } .sign(&alice.private_key); assert_ok!(Ethereum::execute(alice.address, &t, None,)); @@ -411,3 +426,61 @@ fn test_transact_xcm_create() { ); }); } + +#[test] +fn test_transact_xcm_evm_call_works() { + let (pairs, mut ext) = new_test_ext(2); + let alice = &pairs[0]; + let bob = &pairs[1]; + + ext.execute_with(|| { + let t = LegacyUnsignedTransaction { + nonce: U256::zero(), + gas_price: U256::from(1), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Create, + value: U256::zero(), + input: hex::decode(CONTRACT).unwrap(), + } + .sign(&alice.private_key); + assert_ok!(Ethereum::execute(alice.address, &t, None,)); + + let contract_address = hex::decode("32dcab0ef3fb2de2fce1d2e0799d36239671f04a").unwrap(); + let foo = hex::decode("c2985578").unwrap(); + let bar = hex::decode("febb0f7e").unwrap(); + + let _ = Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(bob.address).into(), + xcm_evm_call_eip_legacy_transaction(H160::from_slice(&contract_address), foo), + ).expect("Failed to call `foo`"); + + // Evm call failing still succesfully dispatched + let _ = Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(bob.address).into(), + xcm_evm_call_eip_legacy_transaction(H160::from_slice(&contract_address), bar), + ).expect("Failed to call `bar`"); + + let pending = crate::Pending::::get(); + assert!(pending.len() == 2); + + // Transaction is in Pending storage, with nonce 0 and status 1 (evm succeed). + let (transaction_0, _, receipt_0) = &pending[0]; + match (transaction_0, receipt_0) { + (&crate::Transaction::Legacy(ref t), &crate::Receipt::Legacy(ref r)) => { + assert!(t.nonce == U256::from(0u8)); + assert!(r.status_code == 1u8); + }, + _ => unreachable!(), + } + + // Transaction is in Pending storage, with nonce 1 and status 0 (evm failed). + let (transaction_1, _, receipt_1) = &pending[1]; + match (transaction_1, receipt_1) { + (&crate::Transaction::Legacy(ref t), &crate::Receipt::Legacy(ref r)) => { + assert!(t.nonce == U256::from(1u8)); + assert!(r.status_code == 0u8); + }, + _ => unreachable!(), + } + }); +} From 2de61562bc8fd73d03eaee94bad43f4f59936f63 Mon Sep 17 00:00:00 2001 From: tgmichel Date: Wed, 15 Jun 2022 11:04:44 +0200 Subject: [PATCH 13/15] Add test validation (cherry picked from commit 8afb33f7f4de46d5c3814836931b12431df057d6) --- frame/ethereum/src/tests/eip1559.rs | 49 +++++++++++++++++++++++++++++ frame/ethereum/src/tests/eip2930.rs | 49 +++++++++++++++++++++++++++++ frame/ethereum/src/tests/legacy.rs | 49 +++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+) diff --git a/frame/ethereum/src/tests/eip1559.rs b/frame/ethereum/src/tests/eip1559.rs index 02737a88cd..0f03f47e3a 100644 --- a/frame/ethereum/src/tests/eip1559.rs +++ b/frame/ethereum/src/tests/eip1559.rs @@ -473,3 +473,52 @@ fn test_transact_xcm_evm_call_works() { } }); } + +#[test] +fn test_transact_xcm_validation_works() { + let (pairs, mut ext) = new_test_ext(2); + let alice = &pairs[0]; + let bob = &pairs[1]; + + ext.execute_with(|| { + // Not enough balance fails to validate. + assert_noop!( + Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(alice.address).into(), + xcm_evm_transfer_eip_1559_transaction(bob.address, U256::MAX), + ), + DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: Some(0), + pays_fee: Pays::Yes, + }, + error: DispatchError::Other("Failed to validate ethereum transaction"), + } + ); + // Not enough base fee fails to validate. + assert_noop!( + Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(alice.address).into(), + EthereumXcmTransaction::V1(EthereumXcmTransactionV1 { + fee_payment: EthereumXcmFee::Manual(fp_xcm::ManualEthereumXcmFee { + gas_price: Some(U256::from(0)), + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + }), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Call(bob.address), + value: U256::from(1), + input: vec![], + access_list: None, + }), + ), + DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: Some(0), + pays_fee: Pays::Yes, + }, + error: DispatchError::Other("Failed to validate ethereum transaction"), + } + ); + }); +} diff --git a/frame/ethereum/src/tests/eip2930.rs b/frame/ethereum/src/tests/eip2930.rs index 3e1d450996..24d1fdd0e2 100644 --- a/frame/ethereum/src/tests/eip2930.rs +++ b/frame/ethereum/src/tests/eip2930.rs @@ -490,3 +490,52 @@ fn test_transact_xcm_evm_call_works() { } }); } + +#[test] +fn test_transact_xcm_validation_works() { + let (pairs, mut ext) = new_test_ext(2); + let alice = &pairs[0]; + let bob = &pairs[1]; + + ext.execute_with(|| { + // Not enough balance fails to validate. + assert_noop!( + Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(alice.address).into(), + xcm_evm_transfer_eip_2930_transaction(bob.address, U256::MAX), + ), + DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: Some(0), + pays_fee: Pays::Yes, + }, + error: DispatchError::Other("Failed to validate ethereum transaction"), + } + ); + // Not enough base fee fails to validate. + assert_noop!( + Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(alice.address).into(), + EthereumXcmTransaction::V1(EthereumXcmTransactionV1 { + fee_payment: EthereumXcmFee::Manual(fp_xcm::ManualEthereumXcmFee { + gas_price: Some(U256::from(0)), + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + }), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Call(bob.address), + value: U256::from(1), + input: vec![], + access_list: Some(vec![(H160::default(), vec![H256::default()])]), + }), + ), + DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: Some(0), + pays_fee: Pays::Yes, + }, + error: DispatchError::Other("Failed to validate ethereum transaction"), + } + ); + }); +} diff --git a/frame/ethereum/src/tests/legacy.rs b/frame/ethereum/src/tests/legacy.rs index 4ccce35640..d09d077967 100644 --- a/frame/ethereum/src/tests/legacy.rs +++ b/frame/ethereum/src/tests/legacy.rs @@ -484,3 +484,52 @@ fn test_transact_xcm_evm_call_works() { } }); } + +#[test] +fn test_transact_xcm_validation_works() { + let (pairs, mut ext) = new_test_ext(2); + let alice = &pairs[0]; + let bob = &pairs[1]; + + ext.execute_with(|| { + // Not enough balance fails to validate. + assert_noop!( + Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(alice.address).into(), + xcm_evm_transfer_legacy_transaction(bob.address, U256::MAX), + ), + DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: Some(0), + pays_fee: Pays::Yes, + }, + error: DispatchError::Other("Failed to validate ethereum transaction"), + } + ); + // Not enough base fee fails to validate. + assert_noop!( + Ethereum::transact_xcm( + RawOrigin::XcmEthereumTransaction(alice.address).into(), + EthereumXcmTransaction::V1(EthereumXcmTransactionV1 { + fee_payment: EthereumXcmFee::Manual(fp_xcm::ManualEthereumXcmFee { + gas_price: Some(U256::from(0)), + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + }), + gas_limit: U256::from(0x100000), + action: ethereum::TransactionAction::Call(bob.address), + value: U256::from(1), + input: vec![], + access_list: None, + }), + ), + DispatchErrorWithPostInfo { + post_info: PostDispatchInfo { + actual_weight: Some(0), + pays_fee: Pays::Yes, + }, + error: DispatchError::Other("Failed to validate ethereum transaction"), + } + ); + }); +} From 38ca72ba46f4812061e1de55099e5dcaa824886e Mon Sep 17 00:00:00 2001 From: tgmichel Date: Wed, 15 Jun 2022 17:11:17 +0200 Subject: [PATCH 14/15] Oops --- frame/ethereum/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index 5bccc0de37..a65130f73f 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -48,7 +48,6 @@ use frame_support::{ }; use frame_system::{pallet_prelude::OriginFor, CheckWeight, WeightInfo}; use pallet_evm::{BlockHashMapping, FeeCalculator, GasWeightMapping, Runner}; -use sha3::{Digest, Keccak256}; use sp_runtime::{ generic::DigestItem, traits::{DispatchInfoOf, Dispatchable, One, Saturating, UniqueSaturatedInto, Zero}, From 10b54189214998863686003ad74961bf9dae9325 Mon Sep 17 00:00:00 2001 From: tgmichel Date: Wed, 15 Jun 2022 17:37:48 +0200 Subject: [PATCH 15/15] Use `XcmEthereumOrigin` assoc type to ensure origin --- frame/ethereum/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index a65130f73f..e394118e2b 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -238,7 +238,7 @@ pub mod pallet { /// How Ethereum state root is calculated. type StateRoot: Get; /// Origin for xcm transact - type XcmEthereumOrigin: EnsureOrigin; + type XcmEthereumOrigin: EnsureOrigin; } #[pallet::pallet] @@ -345,7 +345,7 @@ pub mod pallet { origin: OriginFor, xcm_transaction: EthereumXcmTransaction, ) -> DispatchResultWithPostInfo { - let source = ensure_xcm_ethereum_transaction(origin)?; + let source = T::XcmEthereumOrigin::ensure_origin(origin)?; let (base_fee, base_fee_weight) = T::FeeCalculator::min_gas_price(); let (who, account_weight) = pallet_evm::Pallet::::account_basic(&source);