diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index 4ab4cebf39..d2ce85d21a 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -256,10 +256,13 @@ pub mod pallet { OriginFor: Into>>, { /// Transact an Ethereum transaction. - #[pallet::weight(::GasWeightMapping::gas_to_weight({ - let transaction_data: TransactionData = transaction.into(); - transaction_data.gas_limit.unique_saturated_into() - }))] + #[pallet::weight({ + let without_base_extrinsic_weight = true; + ::GasWeightMapping::gas_to_weight({ + let transaction_data: TransactionData = transaction.into(); + transaction_data.gas_limit.unique_saturated_into() + }, without_base_extrinsic_weight) + })] pub fn transact( origin: OriginFor, transaction: Transaction, @@ -604,6 +607,7 @@ impl Pallet { Ok(PostDispatchInfo { actual_weight: Some(T::GasWeightMapping::gas_to_weight( used_gas.unique_saturated_into(), + true, )), pays_fee: Pays::No, }) diff --git a/frame/ethereum/src/mock.rs b/frame/ethereum/src/mock.rs index 666f2ba5de..4930549e7b 100644 --- a/frame/ethereum/src/mock.rs +++ b/frame/ethereum/src/mock.rs @@ -138,6 +138,7 @@ parameter_types! { pub const ChainId: u64 = 42; pub const EVMModuleId: PalletId = PalletId(*b"py/evmpa"); pub const BlockGasLimit: U256 = U256::MAX; + pub const WeightPerGas: u64 = 20_000; } pub struct HashedAddressMapping; @@ -152,7 +153,8 @@ impl AddressMapping for HashedAddressMapping { impl pallet_evm::Config for Test { type FeeCalculator = FixedGasPrice; - type GasWeightMapping = (); + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; type CallOrigin = EnsureAddressTruncated; type WithdrawOrigin = EnsureAddressTruncated; type AddressMapping = HashedAddressMapping; diff --git a/frame/ethereum/src/tests/eip1559.rs b/frame/ethereum/src/tests/eip1559.rs index a059cf3672..bde7272238 100644 --- a/frame/ethereum/src/tests/eip1559.rs +++ b/frame/ethereum/src/tests/eip1559.rs @@ -83,8 +83,10 @@ fn transaction_with_gas_limit_greater_than_max_extrinsic_should_fail_pre_dispatc let limits: frame_system::limits::BlockWeights = ::BlockWeights::get(); let max_extrinsic = limits.get(DispatchClass::Normal).max_extrinsic.unwrap(); - let max_extrinsic_gas = - ::GasWeightMapping::weight_to_gas(max_extrinsic); + let base_extrinsic = limits.get(DispatchClass::Normal).base_extrinsic; + let max_extrinsic_gas = ::GasWeightMapping::weight_to_gas( + max_extrinsic + base_extrinsic, + ); ext.execute_with(|| { let transaction = EIP1559UnsignedTransaction { diff --git a/frame/evm/precompile/dispatch/src/lib.rs b/frame/evm/precompile/dispatch/src/lib.rs index 513d3b4f1c..6c00c805af 100644 --- a/frame/evm/precompile/dispatch/src/lib.rs +++ b/frame/evm/precompile/dispatch/src/lib.rs @@ -73,7 +73,7 @@ where } if let Some(gas) = target_gas { - let valid_weight = info.weight <= T::GasWeightMapping::gas_to_weight(gas); + let valid_weight = info.weight <= T::GasWeightMapping::gas_to_weight(gas, false); if !valid_weight { return Err(PrecompileFailure::Error { exit_status: ExitError::OutOfGas, diff --git a/frame/evm/precompile/dispatch/src/mock.rs b/frame/evm/precompile/dispatch/src/mock.rs index 6f42b16d6f..e5d12b7280 100644 --- a/frame/evm/precompile/dispatch/src/mock.rs +++ b/frame/evm/precompile/dispatch/src/mock.rs @@ -136,10 +136,12 @@ impl FindAuthor for FindAuthorTruncated { } parameter_types! { pub BlockGasLimit: U256 = U256::max_value(); + pub WeightPerGas: u64 = 20_000; } impl pallet_evm::Config for Test { type FeeCalculator = FixedGasPrice; - type GasWeightMapping = (); + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; type CallOrigin = EnsureAddressRoot; type WithdrawOrigin = EnsureAddressNever; diff --git a/frame/evm/src/lib.rs b/frame/evm/src/lib.rs index f2c2a4255d..19b279a9db 100644 --- a/frame/evm/src/lib.rs +++ b/frame/evm/src/lib.rs @@ -114,6 +114,9 @@ pub mod pallet { /// Maps Ethereum gas to Substrate weight. type GasWeightMapping: GasWeightMapping; + /// Weight corresponding to a gas unit. + type WeightPerGas: Get; + /// Block number to block hash. type BlockHashMapping: BlockHashMapping; @@ -176,7 +179,10 @@ pub mod pallet { } /// Issue an EVM call operation. This is similar to a message call transaction in Ethereum. - #[pallet::weight(T::GasWeightMapping::gas_to_weight(*gas_limit))] + #[pallet::weight({ + let without_base_extrinsic_weight = true; + T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight) + })] pub fn call( origin: OriginFor, source: H160, @@ -231,6 +237,7 @@ pub mod pallet { Ok(PostDispatchInfo { actual_weight: Some(T::GasWeightMapping::gas_to_weight( info.used_gas.unique_saturated_into(), + true, )), pays_fee: Pays::No, }) @@ -238,7 +245,10 @@ pub mod pallet { /// Issue an EVM create operation. This is similar to a contract creation transaction in /// Ethereum. - #[pallet::weight(T::GasWeightMapping::gas_to_weight(*gas_limit))] + #[pallet::weight({ + let without_base_extrinsic_weight = true; + T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight) + })] pub fn create( origin: OriginFor, source: H160, @@ -303,13 +313,17 @@ pub mod pallet { Ok(PostDispatchInfo { actual_weight: Some(T::GasWeightMapping::gas_to_weight( info.used_gas.unique_saturated_into(), + true, )), pays_fee: Pays::No, }) } /// Issue an EVM create2 operation. - #[pallet::weight(T::GasWeightMapping::gas_to_weight(*gas_limit))] + #[pallet::weight({ + let without_base_extrinsic_weight = true; + T::GasWeightMapping::gas_to_weight(*gas_limit, without_base_extrinsic_weight) + })] pub fn create2( origin: OriginFor, source: H160, @@ -376,6 +390,7 @@ pub mod pallet { Ok(PostDispatchInfo { actual_weight: Some(T::GasWeightMapping::gas_to_weight( info.used_gas.unique_saturated_into(), + true, )), pays_fee: Pays::No, }) @@ -618,16 +633,25 @@ impl BlockHashMapping for SubstrateBlockHashMapping { /// A mapping function that converts Ethereum gas to Substrate weight pub trait GasWeightMapping { - fn gas_to_weight(gas: u64) -> Weight; + fn gas_to_weight(gas: u64, without_base_weight: bool) -> Weight; fn weight_to_gas(weight: Weight) -> u64; } -impl GasWeightMapping for () { - fn gas_to_weight(gas: u64) -> Weight { - gas as Weight +pub struct FixedGasWeightMapping(sp_std::marker::PhantomData); +impl GasWeightMapping for FixedGasWeightMapping { + fn gas_to_weight(gas: u64, without_base_weight: bool) -> Weight { + let mut weight = gas.saturating_mul(T::WeightPerGas::get()); + if without_base_weight { + weight = weight.saturating_sub( + T::BlockWeights::get() + .get(frame_support::weights::DispatchClass::Normal) + .base_extrinsic, + ); + } + weight } fn weight_to_gas(weight: Weight) -> u64 { - weight as u64 + weight.wrapping_div(T::WeightPerGas::get()) } } diff --git a/frame/evm/src/mock.rs b/frame/evm/src/mock.rs index f271168dfd..ecd34f4e08 100644 --- a/frame/evm/src/mock.rs +++ b/frame/evm/src/mock.rs @@ -124,10 +124,12 @@ impl FindAuthor for FindAuthorTruncated { } parameter_types! { pub BlockGasLimit: U256 = U256::max_value(); + pub WeightPerGas: u64 = 20_000; } impl crate::Config for Test { type FeeCalculator = FixedGasPrice; - type GasWeightMapping = (); + type GasWeightMapping = crate::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; type CallOrigin = EnsureAddressRoot; type WithdrawOrigin = EnsureAddressNever; diff --git a/template/runtime/src/lib.rs b/template/runtime/src/lib.rs index 6e5b978709..4e39d02574 100644 --- a/template/runtime/src/lib.rs +++ b/template/runtime/src/lib.rs @@ -40,8 +40,7 @@ use pallet_transaction_payment::CurrencyAdapter; use fp_rpc::TransactionStatus; use pallet_ethereum::{Call::transact, Transaction as EthereumTransaction}; use pallet_evm::{ - Account as EVMAccount, EnsureAddressTruncated, FeeCalculator, GasWeightMapping, - HashedAddressMapping, Runner, + Account as EVMAccount, EnsureAddressTruncated, FeeCalculator, HashedAddressMapping, Runner, }; // A few exports that help ease life for downstream crates. @@ -309,24 +308,16 @@ impl> FindAuthor for FindAuthorTruncated { } } -pub struct FixedGasWeightMapping; -impl GasWeightMapping for FixedGasWeightMapping { - fn gas_to_weight(gas: u64) -> Weight { - gas.saturating_mul(WEIGHT_PER_GAS) - } - fn weight_to_gas(weight: Weight) -> u64 { - weight.wrapping_div(WEIGHT_PER_GAS) - } -} - parameter_types! { pub BlockGasLimit: U256 = U256::from(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT / WEIGHT_PER_GAS); pub PrecompilesValue: FrontierPrecompiles = FrontierPrecompiles::<_>::new(); + pub WeightPerGas: u64 = 20_000; } impl pallet_evm::Config for Runtime { type FeeCalculator = BaseFee; - type GasWeightMapping = FixedGasWeightMapping; + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping; type CallOrigin = EnsureAddressTruncated; type WithdrawOrigin = EnsureAddressTruncated; @@ -857,3 +848,16 @@ impl_runtime_apis! { } } } + +#[cfg(test)] +mod tests { + use super::{Runtime, WeightPerGas}; + #[test] + fn configured_base_extrinsic_weight_is_evm_compatible() { + let min_ethereum_transaction_weight = WeightPerGas::get() * 21_000; + let base_extrinsic = ::BlockWeights::get() + .get(frame_support::weights::DispatchClass::Normal) + .base_extrinsic; + assert!(base_extrinsic <= min_ethereum_transaction_weight); + } +} diff --git a/ts-tests/tests/test-gas.ts b/ts-tests/tests/test-gas.ts index 0f4e1191e1..28e55b174f 100644 --- a/ts-tests/tests/test-gas.ts +++ b/ts-tests/tests/test-gas.ts @@ -40,8 +40,8 @@ describeWithFrontier("Frontier RPC (Gas)", (context) => { // Those test are ordered. In general this should be avoided, but due to the time it takes // to spin up a frontier node, it saves a lot of time. - // EXTRINSIC_GAS_LIMIT = [BLOCK_GAS_LIMIT - BLOCK_GAS_LIMIT * (NORMAL_DISPATCH_RATIO - AVERAGE_ON_INITIALIZE_RATIO) - EXTRINSIC_BASE_Weight] / WEIGHT_PER_GAS = (1_000_000_000_000 * 2 * (0.75-0.1) - 125_000_000) / 20000 - const EXTRINSIC_GAS_LIMIT = 64995685; + // EXTRINSIC_GAS_LIMIT = [BLOCK_GAS_LIMIT - BLOCK_GAS_LIMIT * (NORMAL_DISPATCH_RATIO - AVERAGE_ON_INITIALIZE_RATIO)] / WEIGHT_PER_GAS = (1_000_000_000_000 * 2 * (0.75-0.1) - 125_000_000) / 20000 + const EXTRINSIC_GAS_LIMIT = 65000000; it("eth_estimateGas for contract creation", async function () { // The value returned as an estimation by the evm with estimate mode ON.