diff --git a/client/rpc/src/cache/lru_cache.rs b/client/rpc/src/cache/lru_cache.rs index 5adc81eb..c09e280f 100644 --- a/client/rpc/src/cache/lru_cache.rs +++ b/client/rpc/src/cache/lru_cache.rs @@ -66,13 +66,19 @@ impl LRUCacheByteLimited { } } pub fn put(&mut self, k: K, v: V) { - // Handle size limit + // If the key is already present, remove it first so its old size is no longer + // counted. Without this, re-inserting an existing key inflates `self.size` and + // causes the eviction loop to throw out unrelated entries unnecessarily, making + // the effective cache capacity much smaller than intended. + if let Some(old_v) = self.cache.remove(&k) { + self.size = self.size.saturating_sub(old_v.encoded_size() as u64); + } + self.size += v.encoded_size() as u64; while self.size > self.max_size { - if let Some((_, v)) = self.cache.pop_oldest() { - let v_size = v.encoded_size() as u64; - self.size -= v_size; + if let Some((_, evicted_v)) = self.cache.pop_oldest() { + self.size = self.size.saturating_sub(evicted_v.encoded_size() as u64); } else { break; } @@ -143,4 +149,41 @@ mod tests { cache.put(3, "lmn"); assert!(cache.get(&3).is_some()); } + + /// Regression test: re-inserting the same key must not inflate `size` and must not + /// evict entries that would still fit within `max_size`. + /// + /// Before the fix, `put(k, v2)` when `k` was already cached with `v1` would add + /// `encoded_size(v2)` to `self.size` without subtracting `encoded_size(v1)`. Over + /// time this caused `self.size` to drift far above the actual data in the cache, + /// leading to excessive LRU evictions and a degraded (nearly empty) cache. + /// + /// We must replace the *newest* key (key 1). Replacing the oldest (key 0) would + /// evict key 0 first, which accidentally "corrects" size; replacing key 1 evicts + /// key 0 incorrectly when the bug exists. + #[test] + fn test_replace_same_key_does_not_inflate_size() { + // max_size = 10 bytes, "abcd" = 4 bytes, "efgh" = 4 bytes. + let mut cache = LRUCacheByteLimited::new("name", 10, None); + + // Fill with two entries that together fit. Order: 0=oldest, 1=newest. + cache.put(0u32, "abcd"); // 4 bytes + cache.put(1u32, "efgh"); // 4 bytes + assert!(cache.get(&0).is_some()); + assert!(cache.get(&1).is_some()); + + // Replace key 1 (newest) with a same-sized value. Before the fix, size grows + // to 12, we evict oldest (key 0), and key 0 is incorrectly gone even though + // both entries still fit. + cache.put(1u32, "wxyz"); // 4 bytes replaces 4 bytes + assert_eq!( + cache.get(&1), + Some(&"wxyz"), + "key 1 should hold the new value" + ); + assert!( + cache.get(&0).is_some(), + "key 0 must not be evicted: replacing a same-size value should not grow the cache" + ); + } } diff --git a/client/rpc/src/eth/format.rs b/client/rpc/src/eth/format.rs index f4c33bae..2461a383 100644 --- a/client/rpc/src/eth/format.rs +++ b/client/rpc/src/eth/format.rs @@ -42,7 +42,10 @@ impl Geth { VError::InvalidChainId => "invalid chain id".into(), VError::InvalidSignature => "invalid sender".into(), VError::GasLimitTooLow => "intrinsic gas too low".into(), - VError::GasLimitTooHigh => "exceeds block gas limit".into(), + VError::GasLimitExceedsBlockLimit => "exceeds block gas limit".into(), + VError::TransactionGasLimitExceedsCap => { + "exceeds transaction gas limit cap".into() + } VError::GasPriceTooLow => "gas price less than block base fee".into(), VError::PriorityFeeTooHigh => { "max priority fee per gas higher than max fee per gas".into() diff --git a/frame/ethereum/src/lib.rs b/frame/ethereum/src/lib.rs index 8a41a24c..188e6a9a 100644 --- a/frame/ethereum/src/lib.rs +++ b/frame/ethereum/src/lib.rs @@ -209,6 +209,8 @@ pub mod pallet { type PostLogContent: Get; /// The maximum length of the extra data in the Executed event. type ExtraDataLength: Get; + /// Whether transactional ethereum calls accept legacy transactions without EIP-155 chain id. + type AllowUnprotectedTxs: Get; } pub mod config_preludes { @@ -225,6 +227,7 @@ pub mod pallet { parameter_types! { pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes; + pub const AllowUnprotectedTxs: bool = false; } #[register_default_impl(TestDefaultConfig)] @@ -232,6 +235,7 @@ pub mod pallet { type StateRoot = IntermediateStateRoot; type PostLogContent = PostBlockAndTxnHashes; type ExtraDataLength = ConstU32<30>; + type AllowUnprotectedTxs = AllowUnprotectedTxs; } } @@ -241,7 +245,10 @@ pub mod pallet { >::store_block( match fp_consensus::find_pre_log(&frame_system::Pallet::::digest()) { Ok(_) => None, - Err(_) => Some(T::PostLogContent::get()), + Err(fp_consensus::FindLogError::NotFound) => Some(T::PostLogContent::get()), + Err(fp_consensus::FindLogError::MultipleLogs) => { + panic!("multiple pre-runtime Frontier logs; block is invalid") + } }, U256::from(UniqueSaturatedInto::::unique_saturated_into( frame_system::Pallet::::block_number(), @@ -263,23 +270,31 @@ pub mod pallet { fn on_initialize(_: BlockNumberFor) -> Weight { let mut weight = T::SystemWeightInfo::kill_storage(1); - // If the digest contain an existing ethereum block(encoded as PreLog), If contains, - // execute the imported block firstly and disable transact dispatch function. - if let Ok(log) = fp_consensus::find_pre_log(&frame_system::Pallet::::digest()) { - let PreLog::Block(block) = log; - - for transaction in block.transactions { - let source = Self::recover_signer(&transaction).expect( - "pre-block transaction signature invalid; the block cannot be built", - ); - - Self::validate_transaction_in_block(source, &transaction).expect( - "pre-block transaction verification failed; the block cannot be built", - ); - let (r, _) = Self::apply_validated_transaction(source, transaction, None) - .expect("pre-block apply transaction failed; the block cannot be built"); + // If the digest contains an existing ethereum block (encoded as PreLog), + // execute the imported block first and disable transact dispatch function. + match fp_consensus::find_pre_log(&frame_system::Pallet::::digest()) { + Ok(log) => { + let PreLog::Block(block) = log; + + for transaction in block.transactions { + let source = Self::recover_signer(&transaction).expect( + "pre-block transaction signature invalid; the block cannot be built", + ); + + Self::validate_transaction_in_block(source, &transaction).expect( + "pre-block transaction verification failed; the block cannot be built", + ); + let (r, _) = Self::apply_validated_transaction(source, transaction, None) + .expect( + "pre-block apply transaction failed; the block cannot be built", + ); - weight = weight.saturating_add(r.actual_weight.unwrap_or_default()); + weight = weight.saturating_add(r.actual_weight.unwrap_or_default()); + } + } + Err(fp_consensus::FindLogError::NotFound) => {} + Err(fp_consensus::FindLogError::MultipleLogs) => { + panic!("multiple pre-runtime Frontier logs; block is invalid") } } // Account for `on_finalize` weight: @@ -547,9 +562,11 @@ impl Pallet { CheckEvmTransactionConfig { evm_config: T::config(), block_gas_limit: T::BlockGasLimit::get(), + transaction_gas_limit: T::TransactionGasLimit::get(), base_fee, chain_id: T::ChainId::get(), is_transactional: true, + allow_unprotected_txs: T::AllowUnprotectedTxs::get(), }, transaction_data.clone().into(), weight_limit, @@ -996,9 +1013,11 @@ impl Pallet { CheckEvmTransactionConfig { evm_config: T::config(), block_gas_limit: T::BlockGasLimit::get(), + transaction_gas_limit: T::TransactionGasLimit::get(), base_fee, chain_id: T::ChainId::get(), is_transactional: true, + allow_unprotected_txs: T::AllowUnprotectedTxs::get(), }, transaction_data.into(), weight_limit, @@ -1121,9 +1140,11 @@ impl From for InvalidTransactionWrapper { TransactionValidationError::GasLimitTooLow => InvalidTransactionWrapper( InvalidTransaction::Custom(TransactionValidationError::GasLimitTooLow as u8), ), - TransactionValidationError::GasLimitTooHigh => InvalidTransactionWrapper( - InvalidTransaction::Custom(TransactionValidationError::GasLimitTooHigh as u8), - ), + TransactionValidationError::GasLimitExceedsBlockLimit => { + InvalidTransactionWrapper(InvalidTransaction::Custom( + TransactionValidationError::GasLimitExceedsBlockLimit as u8, + )) + } TransactionValidationError::PriorityFeeTooHigh => InvalidTransactionWrapper( InvalidTransaction::Custom(TransactionValidationError::PriorityFeeTooHigh as u8), ), @@ -1158,6 +1179,11 @@ impl From for InvalidTransactionWrapper { TransactionValidationError::AuthorizationListTooLarge as u8, )) } + TransactionValidationError::TransactionGasLimitExceedsCap => { + InvalidTransactionWrapper(InvalidTransaction::Custom( + TransactionValidationError::TransactionGasLimitExceedsCap as u8, + )) + } TransactionValidationError::UnknownError => InvalidTransactionWrapper( InvalidTransaction::Custom(TransactionValidationError::UnknownError as u8), ), diff --git a/frame/ethereum/src/mock.rs b/frame/ethereum/src/mock.rs index 28fd9f3a..b899436a 100644 --- a/frame/ethereum/src/mock.rs +++ b/frame/ethereum/src/mock.rs @@ -91,6 +91,8 @@ impl FindAuthor for FindAuthorTruncated { parameter_types! { pub const TransactionByteFee: u64 = 1; pub const GasLimitStorageGrowthRatio: u64 = 366; + pub TransactionGasLimit: Option = Some(fp_evm::MAX_TRANSACTION_GAS_LIMIT); + pub static AllowUnprotectedTxs: bool = false; // Alice is allowed to create contracts via CREATE and CALL(CREATE) pub AllowedAddressesCreate: Vec = vec![H160::from_str("0x1a642f0e3c3af545e7acbd38b07251b3990914f1").expect("alice address")]; pub AllowedAddressesCreateInner: Vec = vec![H160::from_str("0x1a642f0e3c3af545e7acbd38b07251b3990914f1").expect("alice address")]; @@ -107,12 +109,15 @@ impl pallet_evm::Config for Test { type PrecompilesValue = (); type Runner = pallet_evm::runner::stack::Runner; type FindAuthor = FindAuthorTruncated; + type TransactionGasLimit = TransactionGasLimit; type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio; type Timestamp = Timestamp; } #[derive_impl(crate::config_preludes::TestDefaultConfig)] -impl Config for Test {} +impl Config for Test { + type AllowUnprotectedTxs = AllowUnprotectedTxs; +} impl fp_self_contained::SelfContainedCall for RuntimeCall { type SignedInfo = H160; @@ -286,6 +291,17 @@ impl LegacyUnsignedTransaction { H256::from(keccak_256(&stream.out())) } + fn unprotected_signing_hash(&self) -> H256 { + let mut stream = RlpStream::new_list(6); + stream.append(&self.nonce); + stream.append(&self.gas_price); + stream.append(&self.gas_limit); + stream.append(&self.action); + stream.append(&self.value); + stream.append(&self.input); + H256::from(keccak_256(&stream.out())) + } + pub fn sign(&self, key: &H256) -> Transaction { self.sign_with_chain_id(key, ChainId::get()) } @@ -316,6 +332,33 @@ impl LegacyUnsignedTransaction { signature: sig, }) } + + pub fn sign_without_chain_id(&self, key: &H256) -> Transaction { + let hash = self.unprotected_signing_hash(); + let msg = libsecp256k1::Message::parse(hash.as_fixed_bytes()); + let s = libsecp256k1::sign( + &msg, + &libsecp256k1::SecretKey::parse_slice(&key[..]).unwrap(), + ); + let sig = s.0.serialize(); + + let sig = LegacyTransactionSignature::new( + s.1.serialize() as u64 % 2 + 27, + H256::from_slice(&sig[0..32]), + H256::from_slice(&sig[32..64]), + ) + .unwrap(); + + Transaction::Legacy(ethereum::LegacyTransaction { + nonce: self.nonce, + gas_price: self.gas_price, + gas_limit: self.gas_limit, + action: self.action, + value: self.value, + input: self.input.clone(), + signature: sig, + }) + } } pub struct EIP2930UnsignedTransaction { diff --git a/frame/ethereum/src/tests/eip1559.rs b/frame/ethereum/src/tests/eip1559.rs index 418b10d0..cb62f877 100644 --- a/frame/ethereum/src/tests/eip1559.rs +++ b/frame/ethereum/src/tests/eip1559.rs @@ -18,7 +18,7 @@ //! Consensus extension module tests for BABE consensus. use super::*; -use evm::{ExitReason, ExitRevert, ExitSucceed}; +use evm::{ExitError, ExitReason, ExitRevert, ExitSucceed}; use fp_ethereum::{TransactionData, ValidatedTransaction}; use frame_support::{dispatch::DispatchClass, traits::Get, weights::Weight}; use pallet_evm::{AddressMapping, GasWeightMapping}; @@ -51,12 +51,16 @@ fn transaction_with_max_extrinsic_gas_limit_should_success_pre_dispatch() { let max_extrinsic_gas = ::GasWeightMapping::weight_to_gas(max_extrinsic); + // The effective max gas is the minimum of max_extrinsic_gas and the configured cap. + let transaction_gas_limit = TransactionGasLimit::get().unwrap_or_else(U256::max_value); + let effective_max_gas = U256::from(max_extrinsic_gas).min(transaction_gas_limit); + ext.execute_with(|| { let transaction = EIP1559UnsignedTransaction { nonce: U256::zero(), max_priority_fee_per_gas: U256::from(1), max_fee_per_gas: U256::from(1), - gas_limit: U256::from(max_extrinsic_gas), + gas_limit: effective_max_gas, action: ethereum::TransactionAction::Call(bob.address), value: U256::from(1), input: Default::default(), @@ -574,6 +578,8 @@ fn proof_size_weight_limit_validation_works() { let alice = &pairs[0]; ext.execute_with(|| { + System::set_block_number(1); + let mut tx = EIP1559UnsignedTransaction { nonce: U256::from(2), max_priority_fee_per_gas: U256::zero(), @@ -593,11 +599,21 @@ fn proof_size_weight_limit_validation_works() { // Gas limit cannot afford the extra byte and thus is expected to exhaust. tx.input = vec![0u8; (weight_limit.proof_size() + 1) as usize]; let tx = tx.sign(&alice.private_key, None); + let transaction_hash = tx.hash(); - // Execute - assert!( - Ethereum::transact(RawOrigin::EthereumTransaction(alice.address).into(), tx,).is_err() - ); + // Execute - transaction is applied but execution fails with OutOfGas + assert_ok!(Ethereum::apply_validated_transaction( + alice.address, + tx, + None + )); + System::assert_last_event(RuntimeEvent::Ethereum(Event::Executed { + from: alice.address, + to: alice.address, + transaction_hash, + exit_reason: ExitReason::Error(ExitError::OutOfGas), + extra_data: vec![], + })); }); } diff --git a/frame/ethereum/src/tests/eip2930.rs b/frame/ethereum/src/tests/eip2930.rs index 65690535..4019821e 100644 --- a/frame/ethereum/src/tests/eip2930.rs +++ b/frame/ethereum/src/tests/eip2930.rs @@ -18,7 +18,7 @@ //! Consensus extension module tests for BABE consensus. use super::*; -use evm::{ExitReason, ExitRevert, ExitSucceed}; +use evm::{ExitError, ExitReason, ExitRevert, ExitSucceed}; use fp_ethereum::{TransactionData, ValidatedTransaction}; use frame_support::{ dispatch::{DispatchClass, GetDispatchInfo}, @@ -501,6 +501,8 @@ fn proof_size_weight_limit_validation_works() { let alice = &pairs[0]; ext.execute_with(|| { + System::set_block_number(1); + let mut tx = EIP2930UnsignedTransaction { nonce: U256::from(2), gas_price: U256::from(1), @@ -519,11 +521,21 @@ fn proof_size_weight_limit_validation_works() { // Gas limit cannot afford the extra byte and thus is expected to exhaust. tx.input = vec![0u8; (weight_limit.proof_size() + 1) as usize]; let tx = tx.sign(&alice.private_key, None); + let transaction_hash = tx.hash(); - // Execute - assert!( - Ethereum::transact(RawOrigin::EthereumTransaction(alice.address).into(), tx,).is_err() - ); + // Execute - transaction is applied but execution fails with OutOfGas + assert_ok!(Ethereum::apply_validated_transaction( + alice.address, + tx, + None + )); + System::assert_last_event(RuntimeEvent::Ethereum(Event::Executed { + from: alice.address, + to: alice.address, + transaction_hash, + exit_reason: ExitReason::Error(ExitError::OutOfGas), + extra_data: vec![], + })); }); } diff --git a/frame/ethereum/src/tests/eip7825.rs b/frame/ethereum/src/tests/eip7825.rs new file mode 100644 index 00000000..75dd628b --- /dev/null +++ b/frame/ethereum/src/tests/eip7825.rs @@ -0,0 +1,252 @@ +// This file is part of Frontier. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! EIP-7825 Transaction Gas Limit Cap tests + +use super::*; +use fp_evm::{TransactionValidationError, MAX_TRANSACTION_GAS_LIMIT}; + +/// Helper function to create a legacy transaction with a specific gas limit +fn legacy_transaction_with_gas_limit( + nonce: U256, + gas_limit: U256, + to: H160, + value: U256, +) -> LegacyUnsignedTransaction { + LegacyUnsignedTransaction { + nonce, + gas_price: U256::from(1), + gas_limit, + action: TransactionAction::Call(to), + value, + input: vec![], + } +} + +/// Helper function to create an EIP-1559 transaction with a specific gas limit +fn eip1559_transaction_with_gas_limit( + nonce: U256, + gas_limit: U256, + to: H160, + value: U256, +) -> EIP1559UnsignedTransaction { + EIP1559UnsignedTransaction { + nonce, + max_priority_fee_per_gas: U256::from(1), + max_fee_per_gas: U256::from(1), + gas_limit, + action: TransactionAction::Call(to), + value, + input: vec![], + } +} + +#[test] +fn eip7825_transaction_at_exactly_cap_succeeds() { + let (pairs, mut ext) = new_test_ext_with_initial_balance(2, 100_000_000_000_000); + let alice = &pairs[0]; + let bob = &pairs[1]; + + ext.execute_with(|| { + // Transaction at exactly the EIP-7825 cap should pass validation + let transaction = legacy_transaction_with_gas_limit( + U256::zero(), + MAX_TRANSACTION_GAS_LIMIT, // Exactly at cap (16,777,216) + bob.address, + U256::from(1000), + ) + .sign(&alice.private_key); + + let call = crate::Call::::transact { transaction }; + let source = call.check_self_contained().unwrap().unwrap(); + + // Transaction should be valid + assert_ok!(call + .validate_self_contained(&source, &call.get_dispatch_info(), 0) + .unwrap()); + }); +} + +#[test] +fn eip7825_transaction_exceeds_cap_by_one_fails() { + let (pairs, mut ext) = new_test_ext_with_initial_balance(2, 100_000_000_000_000); + let alice = &pairs[0]; + let bob = &pairs[1]; + + ext.execute_with(|| { + // Transaction exceeding cap by 1 gas should fail + let transaction = legacy_transaction_with_gas_limit( + U256::zero(), + MAX_TRANSACTION_GAS_LIMIT + 1, // 1 over cap + bob.address, + U256::from(1000), + ) + .sign(&alice.private_key); + + let call = crate::Call::::transact { transaction }; + let source = call.check_self_contained().unwrap().unwrap(); + + // Transaction should fail validation + let validation_result = call + .validate_self_contained(&source, &call.get_dispatch_info(), 0) + .unwrap(); + + assert!(validation_result.is_err()); + assert_eq!( + validation_result.unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Custom( + TransactionValidationError::TransactionGasLimitExceedsCap as u8 + )) + ); + }); +} + +#[test] +fn eip7825_transaction_standard_transfer_gas_succeeds() { + let (pairs, mut ext) = new_test_ext_with_initial_balance(2, 100_000_000_000_000); + let alice = &pairs[0]; + let bob = &pairs[1]; + + ext.execute_with(|| { + // Transaction with standard transfer gas (21,000) should pass + let transaction = legacy_transaction_with_gas_limit( + U256::zero(), + U256::from(21_000u64), + bob.address, + U256::from(1000), + ) + .sign(&alice.private_key); + + let call = crate::Call::::transact { transaction }; + let source = call.check_self_contained().unwrap().unwrap(); + + // Transaction should be valid + assert_ok!(call + .validate_self_contained(&source, &call.get_dispatch_info(), 0) + .unwrap()); + }); +} + +#[test] +fn eip7825_transaction_well_over_cap_fails() { + let (pairs, mut ext) = new_test_ext_with_initial_balance(2, 100_000_000_000_000); + let alice = &pairs[0]; + let bob = &pairs[1]; + + ext.execute_with(|| { + // Transaction with gas limit nearly double the cap should fail + let transaction = legacy_transaction_with_gas_limit( + U256::zero(), + U256::from(30_000_000u64), // Nearly double the 16,777,216 cap + bob.address, + U256::from(1000), + ) + .sign(&alice.private_key); + + let call = crate::Call::::transact { transaction }; + let source = call.check_self_contained().unwrap().unwrap(); + + // Transaction should fail validation + let validation_result = call + .validate_self_contained(&source, &call.get_dispatch_info(), 0) + .unwrap(); + + assert!(validation_result.is_err()); + assert_eq!( + validation_result.unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Custom( + TransactionValidationError::TransactionGasLimitExceedsCap as u8 + )) + ); + }); +} + +#[test] +fn eip7825_eip1559_transaction_exceeds_cap_fails() { + let (pairs, mut ext) = new_test_ext_with_initial_balance(2, 100_000_000_000_000); + let alice = &pairs[0]; + let bob = &pairs[1]; + + ext.execute_with(|| { + // EIP-1559 transaction exceeding cap should also fail + let transaction = eip1559_transaction_with_gas_limit( + U256::zero(), + MAX_TRANSACTION_GAS_LIMIT + 1, + bob.address, + U256::from(1000), + ) + .sign(&alice.private_key, None); + + let call = crate::Call::::transact { transaction }; + let source = call.check_self_contained().unwrap().unwrap(); + + // Transaction should fail validation + let validation_result = call + .validate_self_contained(&source, &call.get_dispatch_info(), 0) + .unwrap(); + + assert!(validation_result.is_err()); + assert_eq!( + validation_result.unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Custom( + TransactionValidationError::TransactionGasLimitExceedsCap as u8 + )) + ); + }); +} + +#[test] +fn eip7825_block_validation_rejects_exceeding_cap() { + let (pairs, mut ext) = new_test_ext_with_initial_balance(2, 100_000_000_000_000); + let alice = &pairs[0]; + let bob = &pairs[1]; + + ext.execute_with(|| { + // Transaction exceeding cap should fail block validation + let transaction = legacy_transaction_with_gas_limit( + U256::zero(), + MAX_TRANSACTION_GAS_LIMIT + 1, + bob.address, + U256::from(1000), + ) + .sign(&alice.private_key); + + let call = crate::Call::::transact { transaction }; + let source = call.check_self_contained().unwrap().unwrap(); + + // Pre-dispatch (block validation) should fail + let pre_dispatch_result = + call.pre_dispatch_self_contained(&source, &call.get_dispatch_info(), 0); + + assert!(pre_dispatch_result.is_some()); + let result = pre_dispatch_result.unwrap(); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + TransactionValidityError::Invalid(InvalidTransaction::Custom( + TransactionValidationError::TransactionGasLimitExceedsCap as u8 + )) + ); + }); +} + +#[test] +fn eip7825_constant_value_is_correct() { + // Verify the constant is correctly set to 2^24 + assert_eq!(MAX_TRANSACTION_GAS_LIMIT, U256::from(16_777_216u64)); + assert_eq!(MAX_TRANSACTION_GAS_LIMIT, U256::from(1u64 << 24)); +} diff --git a/frame/ethereum/src/tests/legacy.rs b/frame/ethereum/src/tests/legacy.rs index 351a03e1..12cf8a7d 100644 --- a/frame/ethereum/src/tests/legacy.rs +++ b/frame/ethereum/src/tests/legacy.rs @@ -18,7 +18,7 @@ //! Consensus extension module tests for BABE consensus. use super::*; -use evm::{ExitReason, ExitRevert, ExitSucceed}; +use evm::{ExitError, ExitReason, ExitRevert, ExitSucceed}; use fp_ethereum::{TransactionData, ValidatedTransaction}; use frame_support::{ dispatch::{DispatchClass, GetDispatchInfo, Pays, PostDispatchInfo}, @@ -209,6 +209,54 @@ fn transaction_with_invalid_chain_id_should_fail_in_block() { }); } +#[test] +fn unprotected_transaction_should_fail_when_not_allowed() { + let (pairs, mut ext) = new_test_ext(1); + let alice = &pairs[0]; + + ext.execute_with(|| { + AllowUnprotectedTxs::set(false); + let transaction = + legacy_erc20_creation_unsigned_transaction().sign_without_chain_id(&alice.private_key); + + let call = crate::Call::::transact { transaction }; + let source = call.check_self_contained().unwrap().unwrap(); + let extrinsic = CheckedExtrinsic::<_, _, SignedExtra, _> { + signed: fp_self_contained::CheckedSignature::SelfContained(source), + function: RuntimeCall::Ethereum(call), + }; + let dispatch_info = extrinsic.get_dispatch_info(); + assert_err!( + extrinsic.apply::(&dispatch_info, 0), + TransactionValidityError::Invalid(InvalidTransaction::Custom( + fp_evm::TransactionValidationError::InvalidChainId as u8, + )) + ); + }); +} + +#[test] +fn unprotected_transaction_should_succeed_when_allowed() { + let (pairs, mut ext) = new_test_ext(1); + let alice = &pairs[0]; + + ext.execute_with(|| { + AllowUnprotectedTxs::set(true); + let transaction = + legacy_erc20_creation_unsigned_transaction().sign_without_chain_id(&alice.private_key); + + let call = crate::Call::::transact { transaction }; + let source = call.check_self_contained().unwrap().unwrap(); + let extrinsic = CheckedExtrinsic::<_, _, SignedExtra, _> { + signed: fp_self_contained::CheckedSignature::SelfContained(source), + function: RuntimeCall::Ethereum(call), + }; + let dispatch_info = extrinsic.get_dispatch_info(); + assert_ok!(extrinsic.apply::(&dispatch_info, 0)); + AllowUnprotectedTxs::set(false); + }); +} + #[test] fn contract_constructor_should_get_executed() { let (pairs, mut ext) = new_test_ext(1); @@ -645,6 +693,8 @@ fn proof_size_weight_limit_validation_works() { let alice = &pairs[0]; ext.execute_with(|| { + System::set_block_number(1); + let mut tx = LegacyUnsignedTransaction { nonce: U256::from(2), gas_price: U256::from(1), @@ -663,11 +713,21 @@ fn proof_size_weight_limit_validation_works() { // Gas limit cannot afford the extra byte and thus is expected to exhaust. tx.input = vec![0u8; (weight_limit.proof_size() + 1) as usize]; let tx = tx.sign(&alice.private_key); + let transaction_hash = tx.hash(); - // Execute - assert!( - Ethereum::transact(RawOrigin::EthereumTransaction(alice.address).into(), tx,).is_err() - ); + // Execute - transaction is applied but execution fails with OutOfGas + assert_ok!(Ethereum::apply_validated_transaction( + alice.address, + tx, + None + )); + System::assert_last_event(RuntimeEvent::Ethereum(Event::Executed { + from: alice.address, + to: alice.address, + transaction_hash, + exit_reason: ExitReason::Error(ExitError::OutOfGas), + extra_data: vec![], + })); }); } diff --git a/frame/ethereum/src/tests/mod.rs b/frame/ethereum/src/tests/mod.rs index 679bbdc0..876f2ed5 100644 --- a/frame/ethereum/src/tests/mod.rs +++ b/frame/ethereum/src/tests/mod.rs @@ -32,6 +32,7 @@ use fp_self_contained::CheckedExtrinsic; mod eip1559; mod eip2930; mod eip7702; +mod eip7825; mod legacy; // This ERC-20 contract mints the maximum amount of tokens to the contract creator. diff --git a/frame/evm/precompile/balances/src/mock.rs b/frame/evm/precompile/balances/src/mock.rs index 7520ed61..4d8ebb7e 100644 --- a/frame/evm/precompile/balances/src/mock.rs +++ b/frame/evm/precompile/balances/src/mock.rs @@ -115,6 +115,7 @@ impl pallet_evm::Config for Test { type FindAuthor = (); type GasLimitPovSizeRatio = (); type GasLimitStorageGrowthRatio = (); + type TransactionGasLimit = (); type Timestamp = Timestamp; type CreateInnerOriginFilter = (); type CreateOriginFilter = (); diff --git a/frame/evm/precompile/dispatch/src/mock.rs b/frame/evm/precompile/dispatch/src/mock.rs index 25cdfe91..a9478630 100644 --- a/frame/evm/precompile/dispatch/src/mock.rs +++ b/frame/evm/precompile/dispatch/src/mock.rs @@ -133,6 +133,7 @@ impl FindAuthor for FindAuthorTruncated { } parameter_types! { pub BlockGasLimit: U256 = U256::max_value(); + pub TransactionGasLimit: Option = Some(fp_evm::MAX_TRANSACTION_GAS_LIMIT); pub WeightPerGas: Weight = Weight::from_parts(20_000, 0); } impl pallet_evm::Config for Test { @@ -152,6 +153,7 @@ impl pallet_evm::Config for Test { type PrecompilesValue = (); type ChainId = (); type BlockGasLimit = BlockGasLimit; + type TransactionGasLimit = TransactionGasLimit; type Runner = pallet_evm::runner::stack::Runner; type OnChargeTransaction = (); type OnCreate = (); diff --git a/frame/evm/precompile/modexp/src/lib.rs b/frame/evm/precompile/modexp/src/lib.rs index fe08ecb3..cdb4c609 100644 --- a/frame/evm/precompile/modexp/src/lib.rs +++ b/frame/evm/precompile/modexp/src/lib.rs @@ -24,7 +24,7 @@ extern crate alloc; use alloc::{vec, vec::Vec}; use core::cmp::max; -use num::{BigUint, FromPrimitive, Integer, One, ToPrimitive, Zero}; +use num::{BigUint, Integer, One, ToPrimitive, Zero}; use fp_evm::{ ExitError, ExitSucceed, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, @@ -33,10 +33,42 @@ use fp_evm::{ pub struct Modexp; -const MIN_GAS_COST: u64 = 200; +// EIP-7883 Gas Pricing Constants +// https://eips.ethereum.org/EIPS/eip-7883 -// Calculate gas cost according to EIP 2565: -// https://eips.ethereum.org/EIPS/eip-2565 +/// Minimum gas cost for ModExp operations (EIP-7883: increased from 200 to 500). +const MIN_GAS_COST: u64 = 500; + +/// Threshold in bytes for applying the complexity floor vs scaled complexity. +/// Inputs with max(base_length, mod_length) <= this value use the floor. +const COMPLEXITY_THRESHOLD: u64 = 32; + +/// Minimum multiplication complexity for small inputs (<= 32 bytes). +/// EIP-7883 introduces this floor to address underpricing of small operations. +const MIN_COMPLEXITY: u64 = 16; + +/// Multiplier applied to word-squared complexity for large inputs (> 32 bytes). +/// EIP-7883 doubles the complexity calculation for larger operations. +const COMPLEXITY_MULTIPLIER: u64 = 2; + +/// Multiplier for iteration count when exponent length exceeds 32 bytes. +/// EIP-7883 doubles this from 8 to 16 to better reflect computation costs. +const ITERATION_MULTIPLIER: u64 = 16; + +/// Gas multiplier for even moduli to compensate for slower computation. +/// CVE-2023-28431: num-bigint uses Montgomery multiplication for odd moduli (fast) +/// but plain power algorithm for even moduli (~20x slower). +/// See: https://github.com/polkadot-evm/frontier/security/advisories/GHSA-fcmm-54jp-7vf6 +const EVEN_MODULUS_MULTIPLIER: u64 = 20; + +/// EIP-7823: Maximum input size limit for MODEXP (8192 bits = 1024 bytes). +/// Each length field (base, exponent, modulus) MUST not exceed this limit. +/// Inputs exceeding this limit consume all available gas and return an error. +/// https://eips.ethereum.org/EIPS/eip-7823 +const EIP7823_INPUT_SIZE_LIMIT: u32 = 1024; + +// Calculate gas cost according to EIP-7883: +// https://eips.ethereum.org/EIPS/eip-7883 fn calculate_gas_cost( base_length: u64, mod_length: u64, @@ -46,6 +78,11 @@ fn calculate_gas_cost( ) -> u64 { fn calculate_multiplication_complexity(base_length: u64, mod_length: u64) -> u64 { let max_length = max(base_length, mod_length); + + if max_length <= COMPLEXITY_THRESHOLD { + return MIN_COMPLEXITY; + } + let mut words = max_length / 8; if !max_length.is_multiple_of(8) { words += 1; @@ -54,8 +91,8 @@ fn calculate_gas_cost( // Note: can't overflow because we take words to be some u64 value / 8, which is // necessarily less than sqrt(u64::MAX). // Additionally, both base_length and mod_length are bounded to 1024, so this has - // an upper bound of roughly (1024 / 8) squared - words * words + // an upper bound of roughly COMPLEXITY_MULTIPLIER * (1024 / 8) squared + COMPLEXITY_MULTIPLIER * words * words } fn calculate_iteration_count(exponent: &BigUint, exponent_bytes: &[u8]) -> u64 { @@ -68,20 +105,20 @@ fn calculate_gas_cost( iteration_count = exponent.bits() - 1; } else if exp_length > 32 { // from the EIP spec: - // (8 * (exp_length - 32)) + ((exponent & (2**256 - 1)).bit_length() - 1) + // (ITERATION_MULTIPLIER * (exp_length - 32)) + ((exponent & (2**256 - 1)).bit_length() - 1) // // Notes: // * exp_length is bounded to 1024 and is > 32 // * exponent can be zero, so we subtract 1 after adding the other terms (whose sum // must be > 0) // * the addition can't overflow because the terms are both capped at roughly - // 8 * max size of exp_length (1024) + // ITERATION_MULTIPLIER * max size of exp_length (1024) // * the EIP spec is written in python, in which (exponent & (2**256 - 1)) takes the // FIRST 32 bytes. However this `BigUint` `&` operator takes the LAST 32 bytes. // We thus instead take the bytes manually. let exponent_head = BigUint::from_bytes_be(&exponent_bytes[..32]); - iteration_count = (8 * (exp_length - 32)) + exponent_head.bits() - 1; + iteration_count = (ITERATION_MULTIPLIER * (exp_length - 32)) + exponent_head.bits() - 1; } max(iteration_count, 1) @@ -89,11 +126,13 @@ fn calculate_gas_cost( let multiplication_complexity = calculate_multiplication_complexity(base_length, mod_length); let iteration_count = calculate_iteration_count(exponent, exponent_bytes); - max( - MIN_GAS_COST, - multiplication_complexity * iteration_count / 3, - ) - .saturating_mul(if mod_is_even { 20 } else { 1 }) + let gas = max(MIN_GAS_COST, multiplication_complexity * iteration_count); + + gas.saturating_mul(if mod_is_even { + EVEN_MODULUS_MULTIPLIER + } else { + 1 + }) } /// Copy bytes from input to target. @@ -143,27 +182,35 @@ impl Precompile for Modexp { let mut mod_len_buf = [0u8; 32]; read_input(input, &mut mod_len_buf, &mut input_offset); - // reasonable assumption: this must fit within the Ethereum EVM's max stack size - let max_size_big = BigUint::from_u32(1024).expect("can't create BigUint"); + // EIP-7823: Maximum input size limit (8192 bits = 1024 bytes) + let max_size_big = BigUint::from(EIP7823_INPUT_SIZE_LIMIT); let base_len_big = BigUint::from_bytes_be(&base_len_buf); if base_len_big > max_size_big { + // EIP-7823: Consume all remaining gas before returning error + handle.record_cost(handle.remaining_gas())?; return Err(PrecompileFailure::Error { - exit_status: ExitError::Other("unreasonably large base length".into()), + exit_status: ExitError::Other("EIP-7823: base length exceeds 1024 bytes".into()), }); } let exp_len_big = BigUint::from_bytes_be(&exp_len_buf); if exp_len_big > max_size_big { + // EIP-7823: Consume all remaining gas before returning error + handle.record_cost(handle.remaining_gas())?; return Err(PrecompileFailure::Error { - exit_status: ExitError::Other("unreasonably large exponent length".into()), + exit_status: ExitError::Other( + "EIP-7823: exponent length exceeds 1024 bytes".into(), + ), }); } let mod_len_big = BigUint::from_bytes_be(&mod_len_buf); if mod_len_big > max_size_big { + // EIP-7823: Consume all remaining gas before returning error + handle.record_cost(handle.remaining_gas())?; return Err(PrecompileFailure::Error { - exit_status: ExitError::Other("unreasonably large modulus length".into()), + exit_status: ExitError::Other("EIP-7823: modulus length exceeds 1024 bytes".into()), }); } @@ -251,7 +298,7 @@ mod tests { #[test] fn process_consensus_tests() -> Result<(), String> { - test_precompile_test_vectors::("../testdata/modexp_eip2565.json")?; + test_precompile_test_vectors::("../testdata/modexp_eip7883.json")?; Ok(()) } @@ -318,7 +365,7 @@ mod tests { ) .expect("Decode failed"); - let cost: u64 = 1; + let cost: u64 = 100000; let context: Context = Context { address: Default::default(), @@ -336,9 +383,16 @@ mod tests { assert_eq!( e, PrecompileFailure::Error { - exit_status: ExitError::Other("unreasonably large base length".into()) + exit_status: ExitError::Other( + "EIP-7823: base length exceeds 1024 bytes".into() + ) } ); + // EIP-7823: Verify all gas was consumed + assert_eq!( + handle.gas_used, cost, + "EIP-7823 requires all gas to be consumed" + ); Ok(()) } } @@ -519,6 +573,154 @@ mod tests { let _ = Modexp::execute(&mut handle).expect("Modexp::execute() returned error"); - assert_eq!(handle.gas_used, 7104 * 20); // gas used when ran in geth (x20) + assert_eq!(handle.gas_used, 56448 * EVEN_MODULUS_MULTIPLIER); + } + + // EIP-7823 Tests: Verify that exceeding the 1024-byte limit consumes all gas + // https://eips.ethereum.org/EIPS/eip-7823 + + #[test] + fn test_eip7823_base_exceeds_limit_consumes_all_gas() { + // Input with base_length = 1025 (0x401) - exceeds 1024 byte limit + let input = hex::decode( + "0000000000000000000000000000000000000000000000000000000000000401\ + 0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000001", + ) + .expect("Decode failed"); + + let gas_limit: u64 = 100000; + let context = Context { + address: Default::default(), + caller: Default::default(), + apparent_value: From::from(0), + }; + + let mut handle = MockHandle::new(input, Some(gas_limit), context); + + let result = Modexp::execute(&mut handle); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + PrecompileFailure::Error { + exit_status: ExitError::Other("EIP-7823: base length exceeds 1024 bytes".into()) + } + ); + // EIP-7823: All gas should be consumed + assert_eq!( + handle.gas_used, gas_limit, + "EIP-7823 requires all gas to be consumed when base length exceeds limit" + ); + } + + #[test] + fn test_eip7823_exp_exceeds_limit_consumes_all_gas() { + // Input with exp_length = 1025 (0x401) - exceeds 1024 byte limit + let input = hex::decode( + "0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000401\ + 0000000000000000000000000000000000000000000000000000000000000001", + ) + .expect("Decode failed"); + + let gas_limit: u64 = 100000; + let context = Context { + address: Default::default(), + caller: Default::default(), + apparent_value: From::from(0), + }; + + let mut handle = MockHandle::new(input, Some(gas_limit), context); + + let result = Modexp::execute(&mut handle); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + PrecompileFailure::Error { + exit_status: ExitError::Other( + "EIP-7823: exponent length exceeds 1024 bytes".into() + ) + } + ); + // EIP-7823: All gas should be consumed + assert_eq!( + handle.gas_used, gas_limit, + "EIP-7823 requires all gas to be consumed when exponent length exceeds limit" + ); + } + + #[test] + fn test_eip7823_mod_exceeds_limit_consumes_all_gas() { + // Input with mod_length = 1025 (0x401) - exceeds 1024 byte limit + let input = hex::decode( + "0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000401", + ) + .expect("Decode failed"); + + let gas_limit: u64 = 100000; + let context = Context { + address: Default::default(), + caller: Default::default(), + apparent_value: From::from(0), + }; + + let mut handle = MockHandle::new(input, Some(gas_limit), context); + + let result = Modexp::execute(&mut handle); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + PrecompileFailure::Error { + exit_status: ExitError::Other("EIP-7823: modulus length exceeds 1024 bytes".into()) + } + ); + // EIP-7823: All gas should be consumed + assert_eq!( + handle.gas_used, gas_limit, + "EIP-7823 requires all gas to be consumed when modulus length exceeds limit" + ); + } + + #[test] + fn test_eip7823_at_limit_succeeds() { + // Input with all lengths at exactly 1024 bytes (0x400) - at the limit, should succeed + // base_length = 1024, exp_length = 1, mod_length = 1 + // base = 2, exp = 3, mod = 5 + // 2^3 % 5 = 3 + let mut input = hex::decode( + "0000000000000000000000000000000000000000000000000000000000000400\ + 0000000000000000000000000000000000000000000000000000000000000001\ + 0000000000000000000000000000000000000000000000000000000000000001", + ) + .expect("Decode failed"); + + // Append base (1024 bytes with value 2 at the end) + let mut base = vec![0u8; 1023]; + base.push(2); + input.extend(base); + + // Append exponent (1 byte with value 3) + input.push(3); + + // Append modulus (1 byte with value 5) + input.push(5); + + let gas_limit: u64 = 10000000; + let context = Context { + address: Default::default(), + caller: Default::default(), + apparent_value: From::from(0), + }; + + let mut handle = MockHandle::new(input, Some(gas_limit), context); + + let result = Modexp::execute(&mut handle); + assert!(result.is_ok(), "Input at exactly 1024 bytes should succeed"); + + let output = result.unwrap().output; + assert_eq!(output.len(), 1); + assert_eq!(output[0], 3, "2^3 % 5 = 3"); } } diff --git a/frame/evm/precompile/shielded-pool/src/mock.rs b/frame/evm/precompile/shielded-pool/src/mock.rs index fba08c63..f7a701c8 100644 --- a/frame/evm/precompile/shielded-pool/src/mock.rs +++ b/frame/evm/precompile/shielded-pool/src/mock.rs @@ -110,6 +110,7 @@ impl pallet_evm::Config for Test { type FindAuthor = (); type GasLimitPovSizeRatio = (); type GasLimitStorageGrowthRatio = (); + type TransactionGasLimit = (); type Timestamp = Timestamp; type CreateInnerOriginFilter = (); type CreateOriginFilter = (); diff --git a/frame/evm/precompile/testdata/modexp_eip2565.json b/frame/evm/precompile/testdata/modexp_eip7883.json similarity index 83% rename from frame/evm/precompile/testdata/modexp_eip2565.json rename to frame/evm/precompile/testdata/modexp_eip7883.json index 916bb56f..198f1b57 100644 --- a/frame/evm/precompile/testdata/modexp_eip2565.json +++ b/frame/evm/precompile/testdata/modexp_eip7883.json @@ -1,121 +1,170 @@ [ - { - "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002003fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - "Expected": "0000000000000000000000000000000000000000000000000000000000000001", - "Name": "eip_example1", - "Gas": 1360, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - "Expected": "0000000000000000000000000000000000000000000000000000000000000000", - "Name": "eip_example2", - "Gas": 1360, - "NoBenchmark": false - }, { "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb502fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", "Expected": "60008f1614cc01dcfb6bfb09c625cf90b47d4468db81b5f8b7a39d42f332eab9b2da8f2d95311648a8f243f4bb13cfb3d8f7f2a3c014122ebb3ed41b02783adc", "Name": "nagydani-1-square", - "Gas": 200, + "Gas": 500, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb503fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", "Expected": "4834a46ba565db27903b1c720c9d593e84e4cbd6ad2e64b31885d944f68cd801f92225a8961c952ddf2797fa4701b330c85c4b363798100b921a1a22a46a7fec", "Name": "nagydani-1-qube", - "Gas": 200, + "Gas": 500, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5010001fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", "Expected": "c36d804180c35d4426b57b50c5bfcca5c01856d104564cd513b461d3c8b8409128a5573e416d0ebe38f5f736766d9dc27143e4da981dfa4d67f7dc474cbee6d2", "Name": "nagydani-1-pow0x10001", - "Gas": 341, + "Gas": 2048, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5102e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", "Expected": "981dd99c3b113fae3e3eaa9435c0dc96779a23c12a53d1084b4f67b0b053a27560f627b873e3f16ad78f28c94f14b6392def26e4d8896c5e3c984e50fa0b3aa44f1da78b913187c6128baa9340b1e9c9a0fd02cb78885e72576da4a8f7e5a113e173a7a2889fde9d407bd9f06eb05bc8fc7b4229377a32941a02bf4edcc06d70", "Name": "nagydani-2-square", - "Gas": 200, + "Gas": 512, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5103e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", "Expected": "d89ceb68c32da4f6364978d62aaa40d7b09b59ec61eb3c0159c87ec3a91037f7dc6967594e530a69d049b64adfa39c8fa208ea970cfe4b7bcd359d345744405afe1cbf761647e32b3184c7fbe87cee8c6c7ff3b378faba6c68b83b6889cb40f1603ee68c56b4c03d48c595c826c041112dc941878f8c5be828154afd4a16311f", "Name": "nagydani-2-qube", - "Gas": 200, + "Gas": 512, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf51010001e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", "Expected": "ad85e8ef13fd1dd46eae44af8b91ad1ccae5b7a1c92944f92a19f21b0b658139e0cabe9c1f679507c2de354bf2c91ebd965d1e633978a830d517d2f6f8dd5fd58065d58559de7e2334a878f8ec6992d9b9e77430d4764e863d77c0f87beede8f2f7f2ab2e7222f85cc9d98b8467f4bb72e87ef2882423ebdb6daf02dddac6db2", "Name": "nagydani-2-pow0x10001", - "Gas": 1365, + "Gas": 8192, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb02d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", "Expected": "affc7507ea6d84751ec6b3f0d7b99dbcc263f33330e450d1b3ff0bc3d0874320bf4edd57debd587306988157958cb3cfd369cc0c9c198706f635c9e0f15d047df5cb44d03e2727f26b083c4ad8485080e1293f171c1ed52aef5993a5815c35108e848c951cf1e334490b4a539a139e57b68f44fee583306f5b85ffa57206b3ee5660458858534e5386b9584af3c7f67806e84c189d695e5eb96e1272d06ec2df5dc5fabc6e94b793718c60c36be0a4d031fc84cd658aa72294b2e16fc240aef70cb9e591248e38bd49c5a554d1afa01f38dab72733092f7555334bbef6c8c430119840492380aa95fa025dcf699f0a39669d812b0c6946b6091e6e235337b6f8", "Name": "nagydani-3-square", - "Gas": 341, + "Gas": 2048, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb03d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", "Expected": "1b280ecd6a6bf906b806d527c2a831e23b238f89da48449003a88ac3ac7150d6a5e9e6b3be4054c7da11dd1e470ec29a606f5115801b5bf53bc1900271d7c3ff3cd5ed790d1c219a9800437a689f2388ba1a11d68f6a8e5b74e9a3b1fac6ee85fc6afbac599f93c391f5dc82a759e3c6c0ab45ce3f5d25d9b0c1bf94cf701ea6466fc9a478dacc5754e593172b5111eeba88557048bceae401337cd4c1182ad9f700852bc8c99933a193f0b94cf1aedbefc48be3bc93ef5cb276d7c2d5462ac8bb0c8fe8923a1db2afe1c6b90d59c534994a6a633f0ead1d638fdc293486bb634ff2c8ec9e7297c04241a61c37e3ae95b11d53343d4ba2b4cc33d2cfa7eb705e", "Name": "nagydani-3-qube", - "Gas": 341, + "Gas": 2048, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb010001d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", "Expected": "37843d7c67920b5f177372fa56e2a09117df585f81df8b300fba245b1175f488c99476019857198ed459ed8d9799c377330e49f4180c4bf8e8f66240c64f65ede93d601f957b95b83efdee1e1bfde74169ff77002eaf078c71815a9220c80b2e3b3ff22c2f358111d816ebf83c2999026b6de50bfc711ff68705d2f40b753424aefc9f70f08d908b5a20276ad613b4ab4309a3ea72f0c17ea9df6b3367d44fb3acab11c333909e02e81ea2ed404a712d3ea96bba87461720e2d98723e7acd0520ac1a5212dbedcd8dc0c1abf61d4719e319ff4758a774790b8d463cdfe131d1b2dcfee52d002694e98e720cb6ae7ccea353bc503269ba35f0f63bf8d7b672a76", "Name": "nagydani-3-pow0x10001", - "Gas": 5461, + "Gas": 32768, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8102df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", "Expected": "8a5aea5f50dcc03dc7a7a272b5aeebc040554dbc1ffe36753c4fc75f7ed5f6c2cc0de3a922bf96c78bf0643a73025ad21f45a4a5cadd717612c511ab2bff1190fe5f1ae05ba9f8fe3624de1de2a817da6072ddcdb933b50216811dbe6a9ca79d3a3c6b3a476b079fd0d05f04fb154e2dd3e5cb83b148a006f2bcbf0042efb2ae7b916ea81b27aac25c3bf9a8b6d35440062ad8eae34a83f3ffa2cc7b40346b62174a4422584f72f95316f6b2bee9ff232ba9739301c97c99a9ded26c45d72676eb856ad6ecc81d36a6de36d7f9dafafee11baa43a4b0d5e4ecffa7b9b7dcefd58c397dd373e6db4acd2b2c02717712e6289bed7c813b670c4a0c6735aa7f3b0f1ce556eae9fcc94b501b2c8781ba50a8c6220e8246371c3c7359fe4ef9da786ca7d98256754ca4e496be0a9174bedbecb384bdf470779186d6a833f068d2838a88d90ef3ad48ff963b67c39cc5a3ee123baf7bf3125f64e77af7f30e105d72c4b9b5b237ed251e4c122c6d8c1405e736299c3afd6db16a28c6a9cfa68241e53de4cd388271fe534a6a9b0dbea6171d170db1b89858468885d08fecbd54c8e471c3e25d48e97ba450b96d0d87e00ac732aaa0d3ce4309c1064bd8a4c0808a97e0143e43a24cfa847635125cd41c13e0574487963e9d725c01375db99c31da67b4cf65eff555f0c0ac416c727ff8d438ad7c42030551d68c2e7adda0abb1ca7c10", "Name": "nagydani-4-square", - "Gas": 1365, + "Gas": 8192, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8103df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", "Expected": "5a2664252aba2d6e19d9600da582cdd1f09d7a890ac48e6b8da15ae7c6ff1856fc67a841ac2314d283ffa3ca81a0ecf7c27d89ef91a5a893297928f5da0245c99645676b481b7e20a566ee6a4f2481942bee191deec5544600bb2441fd0fb19e2ee7d801ad8911c6b7750affec367a4b29a22942c0f5f4744a4e77a8b654da2a82571037099e9c6d930794efe5cdca73c7b6c0844e386bdca8ea01b3d7807146bb81365e2cdc6475f8c23e0ff84463126189dc9789f72bbce2e3d2d114d728a272f1345122de23df54c922ec7a16e5c2a8f84da8871482bd258c20a7c09bbcd64c7a96a51029bbfe848736a6ba7bf9d931a9b7de0bcaf3635034d4958b20ae9ab3a95a147b0421dd5f7ebff46c971010ebfc4adbbe0ad94d5498c853e7142c450d8c71de4b2f84edbf8acd2e16d00c8115b150b1c30e553dbb82635e781379fe2a56360420ff7e9f70cc64c00aba7e26ed13c7c19622865ae07248daced36416080f35f8cc157a857ed70ea4f347f17d1bee80fa038abd6e39b1ba06b97264388b21364f7c56e192d4b62d9b161405f32ab1e2594e86243e56fcf2cb30d21adef15b9940f91af681da24328c883d892670c6aa47940867a81830a82b82716895db810df1b834640abefb7db2092dd92912cb9a735175bc447be40a503cf22dfe565b4ed7a3293ca0dfd63a507430b323ee248ec82e843b673c97ad730728cebc", "Name": "nagydani-4-qube", - "Gas": 1365, + "Gas": 8192, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b81010001df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", "Expected": "bed8b970c4a34849fc6926b08e40e20b21c15ed68d18f228904878d4370b56322d0da5789da0318768a374758e6375bfe4641fca5285ec7171828922160f48f5ca7efbfee4d5148612c38ad683ae4e3c3a053d2b7c098cf2b34f2cb19146eadd53c86b2d7ccf3d83b2c370bfb840913ee3879b1057a6b4e07e110b6bcd5e958bc71a14798c91d518cc70abee264b0d25a4110962a764b364ac0b0dd1ee8abc8426d775ec0f22b7e47b32576afaf1b5a48f64573ed1c5c29f50ab412188d9685307323d990802b81dacc06c6e05a1e901830ba9fcc67688dc29c5e27bde0a6e845ca925f5454b6fb3747edfaa2a5820838fb759eadf57f7cb5cec57fc213ddd8a4298fa079c3c0f472b07fb15aa6a7f0a3780bd296ff6a62e58ef443870b02260bd4fd2bbc98255674b8e1f1f9f8d33c7170b0ebbea4523b695911abbf26e41885344823bd0587115fdd83b721a4e8457a31c9a84b3d3520a07e0e35df7f48e5a9d534d0ec7feef1ff74de6a11e7f93eab95175b6ce22c68d78a642ad642837897ec11349205d8593ac19300207572c38d29ca5dfa03bc14cdbc32153c80e5cc3e739403d34c75915e49beb43094cc6dcafb3665b305ddec9286934ae66ec6b777ca528728c851318eb0f207b39f1caaf96db6eeead6b55ed08f451939314577d42bcc9f97c0b52d0234f88fd07e4c1d7780fdebc025cfffcb572cb27a8c33963", "Name": "nagydani-4-pow0x10001", - "Gas": 21845, + "Gas": 131072, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf02e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", "Expected": "d61fe4e3f32ac260915b5b03b78a86d11bfc41d973fce5b0cc59035cf8289a8a2e3878ea15fa46565b0d806e2f85b53873ea20ed653869b688adf83f3ef444535bf91598ff7e80f334fb782539b92f39f55310cc4b35349ab7b278346eda9bc37c0d8acd3557fae38197f412f8d9e57ce6a76b7205c23564cab06e5615be7c6f05c3d05ec690cba91da5e89d55b152ff8dd2157dc5458190025cf94b1ad98f7cbe64e9482faba95e6b33844afc640892872b44a9932096508f4a782a4805323808f23e54b6ff9b841dbfa87db3505ae4f687972c18ea0f0d0af89d36c1c2a5b14560c153c3fee406f5cf15cfd1c0bb45d767426d465f2f14c158495069d0c5955a00150707862ecaae30624ebacdd8ac33e4e6aab3ff90b6ba445a84689386b9e945d01823a65874444316e83767290fcff630d2477f49d5d8ffdd200e08ee1274270f86ed14c687895f6caf5ce528bd970c20d2408a9ba66216324c6a011ac4999098362dbd98a038129a2d40c8da6ab88318aa3046cb660327cc44236d9e5d2163bd0959062195c51ed93d0088b6f92051fc99050ece2538749165976233697ab4b610385366e5ce0b02ad6b61c168ecfbedcdf74278a38de340fd7a5fead8e588e294795f9b011e2e60377a89e25c90e145397cdeabc60fd32444a6b7642a611a83c464d8b8976666351b4865c37b02e6dc21dbcdf5f930341707b618cc0f03c3122646b3385c9df9f2ec730eec9d49e7dfc9153b6e6289da8c4f0ebea9ccc1b751948e3bb7171c9e4d57423b0eeeb79095c030cb52677b3f7e0b45c30f645391f3f9c957afa549c4e0b2465b03c67993cd200b1af01035962edbc4c9e89b31c82ac121987d6529dafdeef67a132dc04b6dc68e77f22862040b75e2ceb9ff16da0fca534e6db7bd12fa7b7f51b6c08c1e23dfcdb7acbd2da0b51c87ffbced065a612e9b1c8bba9b7e2d8d7a2f04fcc4aaf355b60d764879a76b5e16762d5f2f55d585d0c8e82df6940960cddfb72c91dfa71f6b4e1c6ca25dfc39a878e998a663c04fe29d5e83b9586d047b4d7ff70a9f0d44f127e7d741685ca75f11629128d916a0ffef4be586a30c4b70389cc746e84ebf177c01ee8a4511cfbb9d1ecf7f7b33c7dd8177896e10bbc82f838dcd6db7ac67de62bf46b6a640fb580c5d1d2708f3862e3d2b645d0d18e49ef088053e3a220adc0e033c2afcfe61c90e32151152eb3caaf746c5e377d541cafc6cbb0cc0fa48b5caf1728f2e1957f5addfc234f1a9d89e40d49356c9172d0561a695fce6dab1d412321bbf407f63766ffd7b6b3d79bcfa07991c5a9709849c1008689e3b47c50d613980bec239fb64185249d055b30375ccb4354d71fe4d05648fbf6c80634dfc3575f2f24abb714c1e4c95e8896763bf4316e954c7ad19e5780ab7a040ca6fb9271f90a8b22ae738daf6cb", "Name": "nagydani-5-square", - "Gas": 5461, + "Gas": 32768, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf03e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", "Expected": "5f9c70ec884926a89461056ad20ac4c30155e817f807e4d3f5bb743d789c83386762435c3627773fa77da5144451f2a8aad8adba88e0b669f5377c5e9bad70e45c86fe952b613f015a9953b8a5de5eaee4566acf98d41e327d93a35bd5cef4607d025e58951167957df4ff9b1627649d3943805472e5e293d3efb687cfd1e503faafeb2840a3e3b3f85d016051a58e1c9498aab72e63b748d834b31eb05d85dcde65e27834e266b85c75cc4ec0135135e0601cb93eeeb6e0010c8ceb65c4c319623c5e573a2c8c9fbbf7df68a930beb412d3f4dfd146175484f45d7afaa0d2e60684af9b34730f7c8438465ad3e1d0c3237336722f2aa51095bd5759f4b8ab4dda111b684aa3dac62a761722e7ae43495b7709933512c81c4e3c9133a51f7ce9f2b51fcec064f65779666960b4e45df3900f54311f5613e8012dd1b8efd359eda31a778264c72aa8bb419d862734d769076bce2810011989a45374e5c5d8729fec21427f0bf397eacbb4220f603cf463a4b0c94efd858ffd9768cd60d6ce68d755e0fbad007ce5c2223d70c7018345a102e4ab3c60a13a9e7794303156d4c2063e919f2153c13961fb324c80b240742f47773a7a8e25b3e3fb19b00ce839346c6eb3c732fbc6b888df0b1fe0a3d07b053a2e9402c267b2d62f794d8a2840526e3ade15ce2264496ccd7519571dfde47f7a4bb16292241c20b2be59f3f8fb4f6383f232d838c5a22d8c95b6834d9d2ca493f5a505ebe8899503b0e8f9b19e6e2dd81c1628b80016d02097e0134de51054c4e7674824d4d758760fc52377d2cad145e259aa2ffaf54139e1a66b1e0c1c191e32ac59474c6b526f5b3ba07d3e5ec286eddf531fcd5292869be58c9f22ef91026159f7cf9d05ef66b4299f4da48cc1635bf2243051d342d378a22c83390553e873713c0454ce5f3234397111ac3fe3207b86f0ed9fc025c81903e1748103692074f83824fda6341be4f95ff00b0a9a208c267e12fa01825054cc0513629bf3dbb56dc5b90d4316f87654a8be18227978ea0a8a522760cad620d0d14fd38920fb7321314062914275a5f99f677145a6979b156bd82ecd36f23f8e1273cc2759ecc0b2c69d94dad5211d1bed939dd87ed9e07b91d49713a6e16ade0a98aea789f04994e318e4ff2c8a188cd8d43aeb52c6daa3bc29b4af50ea82a247c5cd67b573b34cbadcc0a376d3bbd530d50367b42705d870f2e27a8197ef46070528bfe408360faa2ebb8bf76e9f388572842bcb119f4d84ee34ae31f5cc594f23705a49197b181fb78ed1ec99499c690f843a4d0cf2e226d118e9372271054fbabdcc5c92ae9fefaef0589cd0e722eaf30c1703ec4289c7fd81beaa8a455ccee5298e31e2080c10c366a6fcf56f7d13582ad0bcad037c612b710fc595b70fbefaaca23623b60c6c39b11beb8e5843b6b3dac60f", "Name": "nagydani-5-qube", - "Gas": 5461, + "Gas": 32768, "NoBenchmark": false }, { "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf010001e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", "Expected": "5a0eb2bdf0ac1cae8e586689fa16cd4b07dfdedaec8a110ea1fdb059dd5253231b6132987598dfc6e11f86780428982d50cf68f67ae452622c3b336b537ef3298ca645e8f89ee39a26758206a5a3f6409afc709582f95274b57b71fae5c6b74619ae6f089a5393c5b79235d9caf699d23d88fb873f78379690ad8405e34c19f5257d596580c7a6a7206a3712825afe630c76b31cdb4a23e7f0632e10f14f4e282c81a66451a26f8df2a352b5b9f607a7198449d1b926e27036810368e691a74b91c61afa73d9d3b99453e7c8b50fd4f09c039a2f2feb5c419206694c31b92df1d9586140cb3417b38d0c503c7b508cc2ed12e813a1c795e9829eb39ee78eeaf360a169b491a1d4e419574e712402de9d48d54c1ae5e03739b7156615e8267e1fb0a897f067afd11fb33f6e24182d7aaaaa18fe5bc1982f20d6b871e5a398f0f6f718181d31ec225cfa9a0a70124ed9a70031bdf0c1c7829f708b6e17d50419ef361cf77d99c85f44607186c8d683106b8bd38a49b5d0fb503b397a83388c5678dcfcc737499d84512690701ed621a6f0172aecf037184ddf0f2453e4053024018e5ab2e30d6d5363b56e8b41509317c99042f517247474ab3abc848e00a07f69c254f46f2a05cf6ed84e5cc906a518fdcfdf2c61ce731f24c5264f1a25fc04934dc28aec112134dd523f70115074ca34e3807aa4cb925147f3a0ce152d323bd8c675ace446d0fd1ae30c4b57f0eb2c23884bc18f0964c0114796c5b6d080c3d89175665fbf63a6381a6a9da39ad070b645c8bb1779506da14439a9f5b5d481954764ea114fac688930bc68534d403cff4210673b6a6ff7ae416b7cd41404c3d3f282fcd193b86d0f54d0006c2a503b40d5c3930da980565b8f9630e9493a79d1c03e74e5f93ac8e4dc1a901ec5e3b3e57049124c7b72ea345aa359e782285d9e6a5c144a378111dd02c40855ff9c2be9b48425cb0b2fd62dc8678fd151121cf26a65e917d65d8e0dacfae108eb5508b601fb8ffa370be1f9a8b749a2d12eeab81f41079de87e2d777994fa4d28188c579ad327f9957fb7bdecec5c680844dd43cb57cf87aeb763c003e65011f73f8c63442df39a92b946a6bd968a1c1e4d5fa7d88476a68bd8e20e5b70a99259c7d3f85fb1b65cd2e93972e6264e74ebf289b8b6979b9b68a85cd5b360c1987f87235c3c845d62489e33acf85d53fa3561fe3a3aee18924588d9c6eba4edb7a4d106b31173e42929f6f0c48c80ce6a72d54eca7c0fe870068b7a7c89c63cdda593f5b32d3cb4ea8a32c39f00ab449155757172d66763ed9527019d6de6c9f2416aa6203f4d11c9ebee1e1d3845099e55504446448027212616167eb36035726daa7698b075286f5379cd3e93cb3e0cf4f9cb8d017facbb5550ed32d5ec5400ae57e47e2bf78d1eaeff9480cc765ceff39db500", "Name": "nagydani-5-pow0x10001", - "Gas": 87381, + "Gas": 524288, + "NoBenchmark": false + }, + { + "Input": "00000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c51f81bcdfc324a0dff2b5bec9d92e21cbebc4d5e29d3a3d30de3e03fbeab8d7f2ee5f854d076701c8753d72779187e404f9b2fb705c495137d78551250314a463ef5a213fe22de1cea28d60f518364ff95fe0b73660793e3efcfbe31bda68aeccc21cc9477a6aea5df8cae73422b700c47e54d892691e099167e77befc94780a920ae4155769cd69c30626f054134b5f003772473f57f84837402df6d166e66303f01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c954b27ac388a17c8e9a7ba12a968f288f3308d6fe7bcdf28e685e9a2e00d8be1af19726b7662016d6404f9336493ad633777feb88c9d02a1d2428e566ac38f42c0bb66d2962ec349088ce0d03b35bf27f6114414ef558c87ad8e543754a352f7dffcaca429690688595ab1d1b349d9295b480a82f43ac5c9112fe40720545cc78501cd8b42f3605212fe06a835c9cbc0328e07e94aedb2ac11f6d6649e7fcd8c43", + "Expected": "120cf297dbe810911c7d060e109e03699ccefa00a257d296c5a14b4180776c5f7c0d7f1cd789c694807689729af267b53f00373f395dee264a3daba11fcac1fa8875aee0950acd8fa656f1fc58077a7549d794dd160506ecea1acc9c0cda13795749c94f9973b683ce2162866e8d6b5b1165a4c7fa4234964d394d6ec4e0113698b89d173e24a962dd7a41a1819b0fef188ef64e7ee264595dce0d76fbc3ba42d5de833b143c8744366effede8bc8197e8f747ff8cdbc0bf1a93560bec960ca9", + "Name": "marcin-1-base-heavy", + "Gas": 1152, + "NoBenchmark": false + }, + { + "Input": "0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000005100000000000000000000000000000000000000000000000000000000000000080001020304050607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0001020304050607", + "Expected": "0000000000000000", + "Name": "marcin-1-exp-heavy", + "Gas": 16624, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000028e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c000102030405060701fffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c950001020304050607", + "Expected": "1abce71dc2205cce4eb6934397a88136f94641342e283cbcd30e929e85605c6718ed67f475192ffd", + "Name": "marcin-1-balanced", + "Gas": 1200, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000019800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000198e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c51f81bcdfc324a0dff2b5bec9d92e21cbebc4d5e29d3a3d30de3e03fbeab8d7f2ee5f854d076701c8753d72779187e404f9b2fb705c495137d78551250314a463ef5a213fe22de1cea28d60f518364ff95fe0b73660793e3efcfbe31bda68aeccc21cc9477a6aea5df8cae73422b700c47e54d892691e099167e77befc94780a920ae4155769cd69c30626f054134b5f003772473f57f84837402df6d166e663c29021b0e084f7dc16f6ec88cc597f1aea9f8e0b9501e0f7a546805d2a20eeda0bf080aeb3ed7ea6f9174d804bd242f0b31ff1ea24800344abb580cd87f61ca75a013a87733553966400242399dee3760877fead2cd87287747155e47a854acb50fe07922f57ae3b4553201bfd7c11aca85e1541f91db8e62dca9c418dc5feae086c9487350539c884510044efce5e3f2aaffca4215c12b9044506375097fecd9b22e2ef46f01f1af8aff742aebf96bdcaf55a341600971dc62555376b9e98a8000102030405060708090a0b0c0d0e0f101112131415161703f01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c954b27ac388a17c8e9a7ba12a968f288f3308d6fe7bcdf28e685e9a2e00d8be1af19726b7662016d6404f9336493ad633777feb88c9d02a1d2428e566ac38f42c0bb66d2962ec349088ce0d03b35bf27f6114414ef558c87ad8e543754a352f7dffcaca429690688595ab1d1b349d9295b480a82f43ac5c9112fe40720545cc78501cd8b42f3605212fe06a835c9cbc0328e07e94aedb2ac11f6d6649e7fcd8c43ddce2bd0cdc6c22c4dcd345735040d5bfe3f09b7c61362089f728e2222db96cab2f2c2ccf43574f9e119f4860fd0f1b6036a43ad9db8a428ea09a4ee385112f3fe9c6656ea2cec604cbb5a9227526653bfa7035e4ae80010b1ba16a76608d5dde0a62bc019e9047b5ec05b1005fd017366130a4ba555e7be654561ee3f539c93cb2c9988fca71bf0ad9c4a426b924641a28e1e4adb93609bfa5b2bc81714cbba1110208b86d7b87be28bdf63a62e33ae81dbcc43de9192bd192c40e85faab539000102030405060708090a0b0c0d0e0f1011121314151617", + "Expected": "817d05c4d540ace3f250fd082e2deb8c2097410fcfe4ce40862cfa015b7f62a7bb72ec1af2915cad66294447b45d177fe759eb80370b0dbd3c0e1c448d54db81eadc11e40c19e394d066a5c019c443798a98d4afd116fc220593d42fbf191b6af0ae75410badb641187ba24a0b968f742a75e2822853f137151d9ea972fd1f36b7c7cc4e71355ecf50648aec094b864cfae9316c0a7be3ddb8ab2d0050b2a029ee956c2366d49430c8f889f29ab514aea8e5b8dec40ba1b49432e30aee32ec45e96dd548205a79d8f8f918eed46acb2115c59086b1011b1d2b093cea723535c3d95efc8e51a7da43b80586d69eb7f213dfb06f7a8e789a9472392bd224411b50f8ca6f2862bcd63431912d1ff99c8d76408245da9e4bea649f0eb930b32922f2d0a345a206160be44d418f1a6c74bd49c4618392ef9350b264a461dfc684c7343211e27675b027054f1cb3d4a5b1a066d3a3ea2eed9caf13251d8be936818f15274e8e3d7539b7c5f216cef327a270fd2a886fbf679c163fb5806249f2c74da5ee0e3ffe9ad1fde2634b29b35da6da6d184ab6ae70199229", + "Name": "marcin-2-base-heavy", + "Gas": 5202, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000010000102030405060708090a0b0c0d0e0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000102030405060708090a0b0c0d0e0f", + "Expected": "00000000000000000000000000000000", + "Name": "marcin-2-exp-heavy", + "Gas": 16368, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000038e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c000102030405060708090a0b0c0d0e0f10111213141516172bfffffffffffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c95000102030405060708090a0b0c0d0e0f1011121314151617", + "Expected": "86bef3367fc7117c8a6b825cadebe80f3e94c321dda73e9e240b98188a1d5c071c60a195097c8d1fb85ce03a2e1b6964846edee5aa2c3f46", + "Name": "marcin-2-balanced", + "Gas": 5978, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244cfffffffffffffffffffffffffffffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c95", + "Expected": "4004762c491606a5132134da6086284f74cc8e14b08f18b90fc09f31bca3d78f", + "Name": "marcin-3-base-heavy", + "Gas": 2032, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000018000102030405060708090a0b0c0d0e0f1011121314151617ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000102030405060708090a0b0c0d0e0f1011121314151617", + "Expected": "000000000000000000000000000000000000000000000000", + "Name": "marcin-3-exp-heavy", + "Gas": 4080, + "NoBenchmark": false + }, + { + "Input": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244cfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01681d2220bfea4bb888a5543db8c0916274ddb1ea93b144c042c01d8164c95", + "Expected": "2d3feee20d394af68dd6744b86a8aca6a4a0b7f01bbcd3c3eec768245ca6acee", + "Name": "marcin-3-balanced", + "Gas": 4080, "NoBenchmark": false } -] +] \ No newline at end of file diff --git a/frame/evm/src/lib.rs b/frame/evm/src/lib.rs index d073aebb..2bd28bbe 100644 --- a/frame/evm/src/lib.rs +++ b/frame/evm/src/lib.rs @@ -178,6 +178,11 @@ pub mod pallet { /// The block gas limit. Can be a simple constant, or an adjustment algorithm in another pallet. type BlockGasLimit: Get; + /// Optional maximum gas limit for a single transaction. + /// + /// Set to `None` to rely only on the block gas limit and weight/proof-size constraints. + type TransactionGasLimit: Get>; + /// EVM execution runner. #[pallet::no_default] type Runner: Runner; @@ -235,6 +240,7 @@ pub mod pallet { parameter_types! { pub BlockGasLimit: U256 = U256::from(BLOCK_GAS_LIMIT); + pub TransactionGasLimit: Option = Some(fp_evm::MAX_TRANSACTION_GAS_LIMIT); pub const ChainId: u64 = 42; pub const GasLimitPovSizeRatio: u64 = BLOCK_GAS_LIMIT.saturating_div(MAX_POV_SIZE); pub const GasLimitStorageGrowthRatio: u64 = BLOCK_GAS_LIMIT.saturating_div(MAX_STORAGE_GROWTH); @@ -253,6 +259,7 @@ pub mod pallet { type PrecompilesValue = (); type ChainId = ChainId; type BlockGasLimit = BlockGasLimit; + type TransactionGasLimit = TransactionGasLimit; type OnChargeTransaction = (); type OnCreate = (); type FindAuthor = FindAuthorTruncated; @@ -599,8 +606,8 @@ pub mod pallet { InvalidNonce, /// Gas limit is too low. GasLimitTooLow, - /// Gas limit is too high. - GasLimitTooHigh, + /// Gas limit exceeds block gas limit. + GasLimitExceedsBlockLimit, /// The chain id is invalid. InvalidChainId, /// the signature is invalid. @@ -613,13 +620,17 @@ pub mod pallet { Undefined, /// Address not allowed to deploy contracts either via CREATE or CALL(CREATE). CreateOriginNotAllowed, + /// EIP-7825: Transaction gas limit exceeds protocol cap (2^24). + TransactionGasLimitExceedsCap, } impl From for Error { fn from(validation_error: TransactionValidationError) -> Self { match validation_error { TransactionValidationError::GasLimitTooLow => Error::::GasLimitTooLow, - TransactionValidationError::GasLimitTooHigh => Error::::GasLimitTooHigh, + TransactionValidationError::GasLimitExceedsBlockLimit => { + Error::::GasLimitExceedsBlockLimit + } TransactionValidationError::BalanceTooLow => Error::::BalanceLow, TransactionValidationError::TxNonceTooLow => Error::::InvalidNonce, TransactionValidationError::TxNonceTooHigh => Error::::InvalidNonce, @@ -630,6 +641,9 @@ pub mod pallet { TransactionValidationError::InvalidSignature => Error::::InvalidSignature, TransactionValidationError::EmptyAuthorizationList => Error::::Undefined, TransactionValidationError::AuthorizationListTooLarge => Error::::Undefined, + TransactionValidationError::TransactionGasLimitExceedsCap => { + Error::::TransactionGasLimitExceedsCap + } TransactionValidationError::UnknownError => Error::::Undefined, } } diff --git a/frame/evm/src/runner/stack.rs b/frame/evm/src/runner/stack.rs index b947be23..f7f7d87f 100644 --- a/frame/evm/src/runner/stack.rs +++ b/frame/evm/src/runner/stack.rs @@ -189,12 +189,21 @@ where { // Used to record the external costs in the evm through the StackState implementation let maybe_weight_info = - WeightInfo::new_from_weight_limit(weight_limit, proof_size_base_cost).map_err( - |_| RunnerError { - error: Error::::GasLimitTooLow, - weight, - }, - )?; + match WeightInfo::new_from_weight_limit(weight_limit, proof_size_base_cost) { + Ok(weight_info) => weight_info, + Err(_) => { + return Ok(ExecutionInfoV2 { + exit_reason: ExitError::OutOfGas.into(), + value: Default::default(), + used_gas: fp_evm::UsedGas { + standard: gas_limit.into(), + effective: gas_limit.into(), + }, + weight_info: None, + logs: Default::default(), + }) + } + }; // The precompile check is only used for transactional invocations. However, here we always // execute the check, because the check has side effects. match precompiles.is_precompile(source, gas_limit) { @@ -478,9 +487,11 @@ where fp_evm::CheckEvmTransactionConfig { evm_config, block_gas_limit: T::BlockGasLimit::get(), + transaction_gas_limit: T::TransactionGasLimit::get(), base_fee, chain_id: T::ChainId::get(), is_transactional, + allow_unprotected_txs: true, }, fp_evm::CheckEvmTransactionInput { chain_id: Some(T::ChainId::get()), diff --git a/frame/evm/test-vector-support/src/lib.rs b/frame/evm/test-vector-support/src/lib.rs index 5fbf8259..778ab0db 100644 --- a/frame/evm/test-vector-support/src/lib.rs +++ b/frame/evm/test-vector-support/src/lib.rs @@ -96,7 +96,9 @@ impl PrecompileHandle for MockHandle { } fn remaining_gas(&self) -> u64 { - unimplemented!() + self.gas_limit + .unwrap_or(u64::MAX) + .saturating_sub(self.gas_used) } fn code_address(&self) -> H160 { diff --git a/precompiles/tests-external/lib.rs b/precompiles/tests-external/lib.rs index 1f1ccb90..4de01de7 100644 --- a/precompiles/tests-external/lib.rs +++ b/precompiles/tests-external/lib.rs @@ -238,6 +238,7 @@ const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; parameter_types! { pub BlockGasLimit: U256 = U256::from(u64::MAX); + pub TransactionGasLimit: Option = Some(fp_evm::MAX_TRANSACTION_GAS_LIMIT); pub PrecompilesValue: Precompiles = Precompiles::new(); pub const WeightPerGas: Weight = Weight::from_parts(1, 0); pub GasLimitPovSizeRatio: u64 = { @@ -260,6 +261,7 @@ impl pallet_evm::Config for Runtime { type PrecompilesValue = PrecompilesValue; type ChainId = (); type BlockGasLimit = BlockGasLimit; + type TransactionGasLimit = TransactionGasLimit; type Runner = pallet_evm::runner::stack::Runner; type OnChargeTransaction = (); type OnCreate = (); diff --git a/primitives/evm/src/validation.rs b/primitives/evm/src/validation.rs index 136bf5c0..b2d280d0 100644 --- a/primitives/evm/src/validation.rs +++ b/primitives/evm/src/validation.rs @@ -41,9 +41,11 @@ pub struct CheckEvmTransactionInput { pub struct CheckEvmTransactionConfig<'config> { pub evm_config: &'config evm::Config, pub block_gas_limit: U256, + pub transaction_gas_limit: Option, pub base_fee: U256, pub chain_id: u64, pub is_transactional: bool, + pub allow_unprotected_txs: bool, } #[derive(Debug)] @@ -61,8 +63,8 @@ pub struct CheckEvmTransaction<'config, E: From> { pub enum TransactionValidationError { /// The transaction gas limit is too low GasLimitTooLow, - /// The transaction gas limit is too hign - GasLimitTooHigh, + /// The transaction gas limit exceeds the block gas limit + GasLimitExceedsBlockLimit, /// The transaction gas price is too low GasPriceTooLow, /// The transaction priority fee is too high @@ -101,6 +103,11 @@ pub enum TransactionValidationError { /// Unknown error #[num_enum(default)] UnknownError, + /// EIP-7825: Transaction gas limit exceeds per-transaction cap + /// + /// The transaction gas limit exceeds the configured per-transaction cap. + /// This cap is independent of the block gas limit and applies to all transactions. + TransactionGasLimitExceedsCap, } impl<'config, E: From> CheckEvmTransaction<'config, E> { @@ -137,10 +144,16 @@ impl<'config, E: From> CheckEvmTransaction<'config, pub fn with_chain_id(&self) -> Result<&Self, E> { // Chain id matches the one in the signature. - if let Some(chain_id) = self.transaction.chain_id { - if chain_id != self.config.chain_id { - return Err(TransactionValidationError::InvalidChainId.into()); + match self.transaction.chain_id { + Some(chain_id) => { + if chain_id != self.config.chain_id { + return Err(TransactionValidationError::InvalidChainId.into()); + } + } + None if self.config.is_transactional && !self.config.allow_unprotected_txs => { + return Err(TransactionValidationError::InvalidChainId.into()) } + None => {} } Ok(self) } @@ -258,7 +271,14 @@ impl<'config, E: From> CheckEvmTransaction<'config, // Transaction gas limit is within the upper bound block gas limit. if self.transaction.gas_limit > self.config.block_gas_limit { - return Err(TransactionValidationError::GasLimitTooHigh.into()); + return Err(TransactionValidationError::GasLimitExceedsBlockLimit.into()); + } + + // Transaction gas limit is within the configured per-transaction cap. + if let Some(transaction_gas_limit) = self.config.transaction_gas_limit { + if self.transaction.gas_limit > transaction_gas_limit { + return Err(TransactionValidationError::TransactionGasLimitExceedsCap.into()); + } } } @@ -304,11 +324,12 @@ impl<'config, E: From> CheckEvmTransaction<'config, #[cfg(test)] mod tests { use super::*; + use crate::MAX_TRANSACTION_GAS_LIMIT; #[derive(Debug, PartialEq)] pub enum TestError { GasLimitTooLow, - GasLimitTooHigh, + GasLimitExceedsBlockLimit, GasPriceTooLow, PriorityFeeTooHigh, BalanceTooLow, @@ -319,6 +340,7 @@ mod tests { InvalidSignature, EmptyAuthorizationList, AuthorizationListTooLarge, + TransactionGasLimitExceedsCap, UnknownError, } @@ -328,7 +350,9 @@ mod tests { fn from(e: TransactionValidationError) -> Self { match e { TransactionValidationError::GasLimitTooLow => TestError::GasLimitTooLow, - TransactionValidationError::GasLimitTooHigh => TestError::GasLimitTooHigh, + TransactionValidationError::GasLimitExceedsBlockLimit => { + TestError::GasLimitExceedsBlockLimit + } TransactionValidationError::GasPriceTooLow => TestError::GasPriceTooLow, TransactionValidationError::PriorityFeeTooHigh => TestError::PriorityFeeTooHigh, TransactionValidationError::BalanceTooLow => TestError::BalanceTooLow, @@ -343,6 +367,9 @@ mod tests { TransactionValidationError::AuthorizationListTooLarge => { TestError::AuthorizationListTooLarge } + TransactionValidationError::TransactionGasLimitExceedsCap => { + TestError::TransactionGasLimitExceedsCap + } TransactionValidationError::UnknownError => TestError::UnknownError, } } @@ -350,9 +377,11 @@ mod tests { struct TestCase { pub blockchain_gas_limit: U256, + pub transaction_gas_limit: Option, pub blockchain_base_fee: U256, pub blockchain_chain_id: u64, pub is_transactional: bool, + pub allow_unprotected_txs: bool, pub chain_id: Option, pub nonce: U256, pub gas_limit: U256, @@ -368,9 +397,11 @@ mod tests { fn default() -> Self { TestCase { blockchain_gas_limit: U256::max_value(), + transaction_gas_limit: Some(MAX_TRANSACTION_GAS_LIMIT), blockchain_base_fee: U256::from(1_000_000_000u128), blockchain_chain_id: 42u64, is_transactional: true, + allow_unprotected_txs: false, chain_id: Some(42u64), nonce: U256::zero(), gas_limit: U256::from(21_000u64), @@ -387,9 +418,11 @@ mod tests { fn test_env<'config>(input: TestCase) -> CheckEvmTransaction<'config, TestError> { let TestCase { blockchain_gas_limit, + transaction_gas_limit, blockchain_base_fee, blockchain_chain_id, is_transactional, + allow_unprotected_txs, chain_id, nonce, gas_limit, @@ -404,9 +437,11 @@ mod tests { CheckEvmTransactionConfig { evm_config: &PECTRA_CONFIG, block_gas_limit: blockchain_gas_limit, + transaction_gas_limit, base_fee: blockchain_base_fee, chain_id: blockchain_chain_id, is_transactional, + allow_unprotected_txs, }, CheckEvmTransactionInput { chain_id, @@ -478,6 +513,18 @@ mod tests { }) } + fn transaction_without_chain_id<'config>( + is_transactional: bool, + allow_unprotected_txs: bool, + ) -> CheckEvmTransaction<'config, TestError> { + test_env(TestCase { + is_transactional, + allow_unprotected_txs, + chain_id: None, + ..Default::default() + }) + } + fn transaction_none_fee<'config>( is_transactional: bool, ) -> CheckEvmTransaction<'config, TestError> { @@ -681,11 +728,11 @@ mod tests { // Pool let res = test.validate_in_pool_for(&who); assert!(res.is_err()); - assert_eq!(res.unwrap_err(), TestError::GasLimitTooHigh); + assert_eq!(res.unwrap_err(), TestError::GasLimitExceedsBlockLimit); // Block let res = test.validate_in_block_for(&who); assert!(res.is_err()); - assert_eq!(res.unwrap_err(), TestError::GasLimitTooHigh); + assert_eq!(res.unwrap_err(), TestError::GasLimitExceedsBlockLimit); } // Valid chain id succeeds. @@ -705,6 +752,28 @@ mod tests { assert_eq!(res.unwrap_err(), TestError::InvalidChainId); } + #[test] + fn validate_chain_id_none_transactional_fails_by_default() { + let test = transaction_without_chain_id(true, false); + let res = test.with_chain_id(); + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), TestError::InvalidChainId); + } + + #[test] + fn validate_chain_id_none_transactional_succeeds_when_allowed() { + let test = transaction_without_chain_id(true, true); + let res = test.with_chain_id(); + assert!(res.is_ok()); + } + + #[test] + fn validate_chain_id_none_non_transactional_succeeds() { + let test = transaction_without_chain_id(false, false); + let res = test.with_chain_id(); + assert!(res.is_ok()); + } + // Valid max fee per gas succeeds. #[test] fn validate_base_fee_succeeds() { @@ -922,9 +991,11 @@ mod tests { CheckEvmTransactionConfig { evm_config: &PECTRA_CONFIG, block_gas_limit: U256::from(1_000_000u64), + transaction_gas_limit: Some(MAX_TRANSACTION_GAS_LIMIT), base_fee: U256::from(1_000_000_000u128), chain_id: 42u64, is_transactional: true, + allow_unprotected_txs: false, }, CheckEvmTransactionInput { chain_id: Some(42u64), @@ -959,9 +1030,11 @@ mod tests { CheckEvmTransactionConfig { evm_config: &PECTRA_CONFIG, block_gas_limit: U256::from(1_000_000u64), + transaction_gas_limit: Some(MAX_TRANSACTION_GAS_LIMIT), base_fee: U256::from(1_000_000_000u128), chain_id: 42u64, is_transactional: true, + allow_unprotected_txs: false, }, CheckEvmTransactionInput { chain_id: Some(42u64), @@ -996,9 +1069,11 @@ mod tests { CheckEvmTransactionConfig { evm_config: &PECTRA_CONFIG, block_gas_limit: U256::from(1_000_000u64), + transaction_gas_limit: Some(MAX_TRANSACTION_GAS_LIMIT), base_fee: U256::from(1_000_000_000u128), chain_id: 42u64, is_transactional: true, + allow_unprotected_txs: false, }, CheckEvmTransactionInput { chain_id: Some(42u64), @@ -1028,9 +1103,11 @@ mod tests { CheckEvmTransactionConfig { evm_config: &PECTRA_CONFIG, block_gas_limit: U256::from(1_000_000u64), + transaction_gas_limit: Some(MAX_TRANSACTION_GAS_LIMIT), base_fee: U256::from(1_000_000_000u128), chain_id: 42u64, is_transactional: true, + allow_unprotected_txs: false, }, CheckEvmTransactionInput { chain_id: Some(42u64), @@ -1052,4 +1129,191 @@ mod tests { let res = validator.with_eip7702_authorization_list(false); // Not EIP-7702 assert!(res.is_ok()); } + + // EIP-7825 Transaction Gas Limit Cap tests + #[test] + fn validate_eip7825_gas_limit_at_cap_succeeds() { + // Transaction at exactly the cap should pass + let validator = CheckEvmTransaction::::new( + CheckEvmTransactionConfig { + evm_config: &crate::EVM_CONFIG, + block_gas_limit: U256::from(30_000_000u64), + transaction_gas_limit: Some(MAX_TRANSACTION_GAS_LIMIT), + base_fee: U256::from(1_000_000_000u128), + chain_id: 42u64, + is_transactional: true, + allow_unprotected_txs: false, + }, + CheckEvmTransactionInput { + chain_id: Some(42u64), + to: Some(H160::default()), + input: vec![], + nonce: U256::zero(), + gas_limit: MAX_TRANSACTION_GAS_LIMIT, // Exactly at cap + gas_price: None, + max_fee_per_gas: Some(U256::from(1_000_000_000u128)), + max_priority_fee_per_gas: Some(U256::from(1_000_000_000u128)), + value: U256::zero(), + access_list: vec![], + authorization_list: vec![], + }, + None, + None, + ); + + let res = validator.validate_common(); + assert!(res.is_ok()); + } + + #[test] + fn validate_eip7825_gas_limit_exceeds_cap_by_one_fails() { + // Transaction exceeding cap by 1 gas should fail + let validator = CheckEvmTransaction::::new( + CheckEvmTransactionConfig { + evm_config: &crate::EVM_CONFIG, + block_gas_limit: U256::from(30_000_000u64), + transaction_gas_limit: Some(MAX_TRANSACTION_GAS_LIMIT), + base_fee: U256::from(1_000_000_000u128), + chain_id: 42u64, + is_transactional: true, + allow_unprotected_txs: false, + }, + CheckEvmTransactionInput { + chain_id: Some(42u64), + to: Some(H160::default()), + input: vec![], + nonce: U256::zero(), + gas_limit: MAX_TRANSACTION_GAS_LIMIT + 1, // 1 over cap + gas_price: None, + max_fee_per_gas: Some(U256::from(1_000_000_000u128)), + max_priority_fee_per_gas: Some(U256::from(1_000_000_000u128)), + value: U256::zero(), + access_list: vec![], + authorization_list: vec![], + }, + None, + None, + ); + + let res = validator.validate_common(); + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), TestError::TransactionGasLimitExceedsCap); + } + + #[test] + fn validate_transaction_gas_limit_cap_none_allows_above_eip7825_cap() { + let validator = test_env(TestCase { + blockchain_gas_limit: U256::from(30_000_000u64), + transaction_gas_limit: None, + gas_limit: MAX_TRANSACTION_GAS_LIMIT + 1, + ..Default::default() + }); + + let res = validator.validate_common(); + assert!(res.is_ok()); + } + + #[test] + fn validate_eip7825_standard_transfer_gas_limit_succeeds() { + // Transaction well under cap should pass + let validator = CheckEvmTransaction::::new( + CheckEvmTransactionConfig { + evm_config: &crate::EVM_CONFIG, + block_gas_limit: U256::from(30_000_000u64), + transaction_gas_limit: Some(MAX_TRANSACTION_GAS_LIMIT), + base_fee: U256::from(1_000_000_000u128), + chain_id: 42u64, + is_transactional: true, + allow_unprotected_txs: false, + }, + CheckEvmTransactionInput { + chain_id: Some(42u64), + to: Some(H160::default()), + input: vec![], + nonce: U256::zero(), + gas_limit: U256::from(21_000u64), // Standard transfer gas + gas_price: None, + max_fee_per_gas: Some(U256::from(1_000_000_000u128)), + max_priority_fee_per_gas: Some(U256::from(1_000_000_000u128)), + value: U256::zero(), + access_list: vec![], + authorization_list: vec![], + }, + None, + None, + ); + + let res = validator.validate_common(); + assert!(res.is_ok()); + } + + #[test] + fn validate_eip7825_gas_limit_well_over_cap_fails() { + // Transaction well over cap should fail + let validator = CheckEvmTransaction::::new( + CheckEvmTransactionConfig { + evm_config: &crate::EVM_CONFIG, + block_gas_limit: U256::from(50_000_000u64), + transaction_gas_limit: Some(MAX_TRANSACTION_GAS_LIMIT), + base_fee: U256::from(1_000_000_000u128), + chain_id: 42u64, + is_transactional: true, + allow_unprotected_txs: false, + }, + CheckEvmTransactionInput { + chain_id: Some(42u64), + to: Some(H160::default()), + input: vec![], + nonce: U256::zero(), + gas_limit: U256::from(30_000_000u64), // Nearly double the cap + gas_price: None, + max_fee_per_gas: Some(U256::from(1_000_000_000u128)), + max_priority_fee_per_gas: Some(U256::from(1_000_000_000u128)), + value: U256::zero(), + access_list: vec![], + authorization_list: vec![], + }, + None, + None, + ); + + let res = validator.validate_common(); + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), TestError::TransactionGasLimitExceedsCap); + } + + #[test] + fn validate_eip7825_non_transactional_exceeds_cap_succeeds() { + // Non-transactional calls (eth_call) exceeding cap should succeed + // This allows dry-running expensive transactions for gas estimation + let validator = CheckEvmTransaction::::new( + CheckEvmTransactionConfig { + evm_config: &crate::EVM_CONFIG, + block_gas_limit: U256::from(50_000_000u64), + transaction_gas_limit: Some(MAX_TRANSACTION_GAS_LIMIT), + base_fee: U256::from(1_000_000_000u128), + chain_id: 42u64, + is_transactional: false, // Non-transactional (dry-run) + allow_unprotected_txs: false, + }, + CheckEvmTransactionInput { + chain_id: Some(42u64), + to: Some(H160::default()), + input: vec![], + nonce: U256::zero(), + gas_limit: U256::from(30_000_000u64), // Exceeds cap but should pass + gas_price: None, + max_fee_per_gas: Some(U256::from(1_000_000_000u128)), + max_priority_fee_per_gas: Some(U256::from(1_000_000_000u128)), + value: U256::zero(), + access_list: vec![], + authorization_list: vec![], + }, + None, + None, + ); + + let res = validator.validate_common(); + assert!(res.is_ok()); + } } diff --git a/template/runtime/RUNTIME_VERSIONS.md b/template/runtime/RUNTIME_VERSIONS.md index 394d9d11..319502d4 100644 --- a/template/runtime/RUNTIME_VERSIONS.md +++ b/template/runtime/RUNTIME_VERSIONS.md @@ -24,7 +24,9 @@ The genesis reset (`69d1b837`) set `spec_version` back to 1 and EVM precompiles: the Balances precompile only runs as a direct call and meters by weight, and every Orbinum precompile address holds code, so Solidity -high-level calls reach them. `transaction_version` is unchanged. Ships with +high-level calls reach them. EVM transaction validation and the modexp +precompile follow upstream Frontier (polkadot-evm/frontier, changes after +#1790). `transaction_version` is unchanged. Ships with `pallet-evm-precompile-balances` 0.2.0. - **Balances precompile (`0x…0802`)** refuses static calls, delegated calls @@ -42,6 +44,24 @@ high-level calls reach them. `transaction_version` is unchanged. Ships with migrations (`RelayerFeesToPool`, `RemoveRelayerPallet`, `MoveStrandedEvmTips`) ran on testnet with spec 19 and are removed, with their tests, so no later upgrade runs them again. +- **Unprotected transactions.** The runtime refuses legacy transactions + without an EIP-155 chain id (`AllowUnprotectedTxs = false`, Frontier #1815). + Only the RPC refused them, so an extrinsic submitted directly could replay a + transaction signed for another chain. +- **Transaction gas cap.** A transaction may ask for at most 2^24 gas + (`TransactionGasLimit`, EIP-7825); above that it is invalid in the pool. The + block limit stays 75M gas. +- **Modexp (`0x05`).** A base, exponent or modulus longer than 1024 bytes + still fails, and now consumes all the call's gas (EIP-7823). Pricing follows + EIP-7883: the minimum goes from 200 to 500 gas, the division by 3 is dropped, + complexity is 16 up to 32 bytes and twice words² above that, and each + exponent byte past the first 32 counts 16 instead of 8. +- **Failed weight conversion.** When the gas limit cannot be converted to a + weight limit, the EVM transaction ends as `OutOfGas` and charges its full gas + limit, instead of failing the dispatch (Frontier #1813). +- **Digest check.** `pallet_ethereum::on_finalize` panics on a block with + more than one Frontier pre-runtime log, so the block is invalid. Before, it + treated that block as having no log (Frontier #1809). ### spec 19 — tx 7 — 2026-10-13 (`v0.5.0`) diff --git a/template/runtime/src/configs/evm.rs b/template/runtime/src/configs/evm.rs index 500ff498..a877a097 100644 --- a/template/runtime/src/configs/evm.rs +++ b/template/runtime/src/configs/evm.rs @@ -60,6 +60,8 @@ parameter_types! { pub const GasLimitStorageGrowthRatio: u64 = BLOCK_GAS_LIMIT.saturating_div(MAX_STORAGE_GROWTH); pub PrecompilesValue: FrontierPrecompiles = FrontierPrecompiles::<_>::new(); pub WeightPerGas: Weight = Weight::from_parts(weight_per_gas(BLOCK_GAS_LIMIT, NORMAL_DISPATCH_RATIO, WEIGHT_MILLISECS_PER_BLOCK), 0); + // EIP-7825 (Osaka) per-transaction cap of 2^24 gas. + pub TransactionGasLimit: Option = Some(fp_evm::MAX_TRANSACTION_GAS_LIMIT); } impl pallet_evm::Config for Runtime { @@ -82,6 +84,7 @@ impl pallet_evm::Config for Runtime { type FindAuthor = (); type GasLimitPovSizeRatio = GasLimitPovSizeRatio; type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio; + type TransactionGasLimit = TransactionGasLimit; type Timestamp = Timestamp; type CreateOriginFilter = (); type CreateInnerOriginFilter = (); @@ -96,6 +99,8 @@ impl pallet_ethereum::Config for Runtime { type StateRoot = pallet_ethereum::IntermediateStateRoot; type PostLogContent = PostBlockAndTxnHashes; type ExtraDataLength = ConstU32<30>; + // Pre-EIP-155 legacy transactions carry no chain id and replay across chains. + type AllowUnprotectedTxs = frame_support::traits::ConstBool; } parameter_types! { diff --git a/ts-tests/tests/test-precompile-modexp.ts b/ts-tests/tests/test-precompile-modexp.ts new file mode 100644 index 00000000..22468828 --- /dev/null +++ b/ts-tests/tests/test-precompile-modexp.ts @@ -0,0 +1,395 @@ +import { assert, expect } from "chai"; +import { GENESIS_ACCOUNT, GENESIS_ACCOUNT_PRIVATE_KEY } from "./config"; +import { createAndFinalizeBlock, customRequest, describeWithFrontier } from "./util"; + +// MODEXP precompile address (0x05) +const MODEXP_PRECOMPILE_ADDRESS = "0x0000000000000000000000000000000000000005"; + +// EIP-7823: Maximum input size limit (1024 bytes) +const EIP7823_INPUT_SIZE_LIMIT = 1024; + +/** + * Encode MODEXP input according to EIP-198: + * - 32 bytes: length of base (big-endian) + * - 32 bytes: length of exponent (big-endian) + * - 32 bytes: length of modulus (big-endian) + * - base bytes + * - exponent bytes + * - modulus bytes + */ +function encodeModexpInput( + baseLen: number, + expLen: number, + modLen: number, + base: string, + exp: string, + mod: string +): string { + const baseLenHex = baseLen.toString(16).padStart(64, "0"); + const expLenHex = expLen.toString(16).padStart(64, "0"); + const modLenHex = modLen.toString(16).padStart(64, "0"); + + return "0x" + baseLenHex + expLenHex + modLenHex + base + exp + mod; +} + +/** + * Create a hex string of specified byte length filled with zeros + */ +function zeroBytes(length: number): string { + return "00".repeat(length); +} + +describeWithFrontier("Frontier RPC (MODEXP Precompile - EIP-7823)", (context) => { + it("should perform basic modexp: 3^5 mod 7 = 5", async function () { + // 3^5 mod 7 = 243 mod 7 = 5 + const input = encodeModexpInput( + 1, // base length + 1, // exp length + 1, // mod length + "03", // base = 3 + "05", // exp = 5 + "07" // mod = 7 + ); + + const result = await context.web3.eth.call({ + to: MODEXP_PRECOMPILE_ADDRESS, + from: GENESIS_ACCOUNT, + data: input, + gas: "0x100000", + }); + + // Result should be 5 (0x05), padded to modulus length (1 byte) + assert.equal(result, "0x05"); + }); + + it("should perform modexp with larger numbers: 2^10 mod 1000 = 24", async function () { + // 2^10 mod 1000 = 1024 mod 1000 = 24 + const input = encodeModexpInput( + 1, // base length + 1, // exp length + 2, // mod length + "02", // base = 2 + "0a", // exp = 10 + "03e8" // mod = 1000 (0x3e8) + ); + + const result = await context.web3.eth.call({ + to: MODEXP_PRECOMPILE_ADDRESS, + from: GENESIS_ACCOUNT, + data: input, + gas: "0x100000", + }); + + // Result should be 24 (0x18), padded to modulus length (2 bytes) + assert.equal(result, "0x0018"); + }); + + it("should return zero when modulus is 1", async function () { + // Any number mod 1 = 0 + const input = encodeModexpInput( + 1, // base length + 1, // exp length + 1, // mod length + "ff", // base = 255 + "ff", // exp = 255 + "01" // mod = 1 + ); + + const result = await context.web3.eth.call({ + to: MODEXP_PRECOMPILE_ADDRESS, + from: GENESIS_ACCOUNT, + data: input, + gas: "0x100000", + }); + + assert.equal(result, "0x00"); + }); + + it("should return empty output when modulus length is 0", async function () { + const input = encodeModexpInput( + 1, // base length + 1, // exp length + 0, // mod length = 0 + "03", // base + "05", // exp + "" // no modulus + ); + + const result = await context.web3.eth.call({ + to: MODEXP_PRECOMPILE_ADDRESS, + from: GENESIS_ACCOUNT, + data: input, + gas: "0x100000", + }); + + assert.equal(result, "0x"); + }); + + // EIP-7823 Tests + + it("EIP-7823: should succeed with base length at exactly 1024 bytes", async function () { + this.timeout(30000); + + // Create input with base_length = 1024 (at the limit) + // base = 2 (padded to 1024 bytes), exp = 3, mod = 5 + // 2^3 mod 5 = 8 mod 5 = 3 + const baseHex = zeroBytes(1023) + "02"; // 1024 bytes with value 2 at end + const input = encodeModexpInput( + EIP7823_INPUT_SIZE_LIMIT, // base length = 1024 + 1, // exp length + 1, // mod length + baseHex, // base (1024 bytes) + "03", // exp = 3 + "05" // mod = 5 + ); + + const result = await context.web3.eth.call({ + to: MODEXP_PRECOMPILE_ADDRESS, + from: GENESIS_ACCOUNT, + data: input, + gas: "0xF42400", // 16M gas + }); + + // 2^3 mod 5 = 3 + assert.equal(result, "0x03"); + }); + + it("EIP-7823: should succeed with exponent length at exactly 1024 bytes", async function () { + this.timeout(30000); + + // Create input with exp_length = 1024 (at the limit) + // base = 2, exp = 3 (padded to 1024 bytes), mod = 5 + const expHex = zeroBytes(1023) + "03"; // 1024 bytes with value 3 at end + const input = encodeModexpInput( + 1, // base length + EIP7823_INPUT_SIZE_LIMIT, // exp length = 1024 + 1, // mod length + "02", // base = 2 + expHex, // exp (1024 bytes) + "05" // mod = 5 + ); + + const result = await context.web3.eth.call({ + to: MODEXP_PRECOMPILE_ADDRESS, + from: GENESIS_ACCOUNT, + data: input, + gas: "0xF42400", // 16M gas + }); + + // 2^3 mod 5 = 3 + assert.equal(result, "0x03"); + }); + + it("EIP-7823: should succeed with modulus length at exactly 1024 bytes", async function () { + this.timeout(30000); + + // Create input with mod_length = 1024 (at the limit) + // base = 2, exp = 3, mod = 5 (padded to 1024 bytes) + const modHex = zeroBytes(1023) + "05"; // 1024 bytes with value 5 at end + const input = encodeModexpInput( + 1, // base length + 1, // exp length + EIP7823_INPUT_SIZE_LIMIT, // mod length = 1024 + "02", // base = 2 + "03", // exp = 3 + modHex // mod (1024 bytes) + ); + + const result = await context.web3.eth.call({ + to: MODEXP_PRECOMPILE_ADDRESS, + from: GENESIS_ACCOUNT, + data: input, + gas: "0xF42400", // 16M gas + }); + + // 2^3 mod 5 = 3, but result is padded to 1024 bytes + const expected = "0x" + zeroBytes(1023) + "03"; + assert.equal(result, expected); + }); + + it("EIP-7823: should fail when base length exceeds 1024 bytes", async function () { + // Create input with base_length = 1025 (exceeds limit) + const baseLenHex = (EIP7823_INPUT_SIZE_LIMIT + 1).toString(16).padStart(64, "0"); + const expLenHex = "1".padStart(64, "0"); + const modLenHex = "1".padStart(64, "0"); + + const input = "0x" + baseLenHex + expLenHex + modLenHex; + + try { + await context.web3.eth.call({ + to: MODEXP_PRECOMPILE_ADDRESS, + from: GENESIS_ACCOUNT, + data: input, + gas: "0x100000", + }); + assert.fail("Expected call to revert"); + } catch (error: any) { + // The call should fail with EIP-7823 error + expect(error.message).to.include("EIP-7823"); + } + }); + + it("EIP-7823: should fail when exponent length exceeds 1024 bytes", async function () { + // Create input with exp_length = 1025 (exceeds limit) + const baseLenHex = "1".padStart(64, "0"); + const expLenHex = (EIP7823_INPUT_SIZE_LIMIT + 1).toString(16).padStart(64, "0"); + const modLenHex = "1".padStart(64, "0"); + + const input = "0x" + baseLenHex + expLenHex + modLenHex; + + try { + await context.web3.eth.call({ + to: MODEXP_PRECOMPILE_ADDRESS, + from: GENESIS_ACCOUNT, + data: input, + gas: "0x100000", + }); + assert.fail("Expected call to revert"); + } catch (error: any) { + // The call should fail with EIP-7823 error + expect(error.message).to.include("EIP-7823"); + } + }); + + it("EIP-7823: should fail when modulus length exceeds 1024 bytes", async function () { + // Create input with mod_length = 1025 (exceeds limit) + const baseLenHex = "1".padStart(64, "0"); + const expLenHex = "1".padStart(64, "0"); + const modLenHex = (EIP7823_INPUT_SIZE_LIMIT + 1).toString(16).padStart(64, "0"); + + const input = "0x" + baseLenHex + expLenHex + modLenHex; + + try { + await context.web3.eth.call({ + to: MODEXP_PRECOMPILE_ADDRESS, + from: GENESIS_ACCOUNT, + data: input, + gas: "0x100000", + }); + assert.fail("Expected call to revert"); + } catch (error: any) { + // The call should fail with EIP-7823 error + expect(error.message).to.include("EIP-7823"); + } + }); + + it("EIP-7823: should consume all gas when base length exceeds limit (via transaction)", async function () { + this.timeout(30000); + + // Create input with base_length = 1025 (exceeds limit) + const baseLenHex = (EIP7823_INPUT_SIZE_LIMIT + 1).toString(16).padStart(64, "0"); + const expLenHex = "1".padStart(64, "0"); + const modLenHex = "1".padStart(64, "0"); + + const input = "0x" + baseLenHex + expLenHex + modLenHex; + const gasLimit = 100000; + + const tx = await context.web3.eth.accounts.signTransaction( + { + from: GENESIS_ACCOUNT, + to: MODEXP_PRECOMPILE_ADDRESS, + data: input, + value: "0x00", + gasPrice: "0x3B9ACA00", + gas: gasLimit, + }, + GENESIS_ACCOUNT_PRIVATE_KEY + ); + + await customRequest(context.web3, "eth_sendRawTransaction", [tx.rawTransaction]); + await createAndFinalizeBlock(context.web3); + + // Get the transaction receipt + const receipt = await context.web3.eth.getTransactionReceipt(tx.transactionHash); + + // EIP-7823: Transaction should fail (status = false) + assert.equal(receipt.status, false, "Transaction should fail"); + + // EIP-7823: All gas should be consumed + assert.equal( + receipt.gasUsed.toString(), + gasLimit.toString(), + "EIP-7823 requires all gas to be consumed when base length exceeds limit" + ); + }); + + it("EIP-7823: should consume all gas when exponent length exceeds limit (via transaction)", async function () { + this.timeout(30000); + + // Create input with exp_length = 1025 (exceeds limit) + const baseLenHex = "1".padStart(64, "0"); + const expLenHex = (EIP7823_INPUT_SIZE_LIMIT + 1).toString(16).padStart(64, "0"); + const modLenHex = "1".padStart(64, "0"); + + const input = "0x" + baseLenHex + expLenHex + modLenHex; + const gasLimit = 100000; + + const tx = await context.web3.eth.accounts.signTransaction( + { + from: GENESIS_ACCOUNT, + to: MODEXP_PRECOMPILE_ADDRESS, + data: input, + value: "0x00", + gasPrice: "0x3B9ACA00", + gas: gasLimit, + }, + GENESIS_ACCOUNT_PRIVATE_KEY + ); + + await customRequest(context.web3, "eth_sendRawTransaction", [tx.rawTransaction]); + await createAndFinalizeBlock(context.web3); + + // Get the transaction receipt + const receipt = await context.web3.eth.getTransactionReceipt(tx.transactionHash); + + // EIP-7823: Transaction should fail (status = false) + assert.equal(receipt.status, false, "Transaction should fail"); + + // EIP-7823: All gas should be consumed + assert.equal( + receipt.gasUsed.toString(), + gasLimit.toString(), + "EIP-7823 requires all gas to be consumed when exponent length exceeds limit" + ); + }); + + it("EIP-7823: should consume all gas when modulus length exceeds limit (via transaction)", async function () { + this.timeout(30000); + + // Create input with mod_length = 1025 (exceeds limit) + const baseLenHex = "1".padStart(64, "0"); + const expLenHex = "1".padStart(64, "0"); + const modLenHex = (EIP7823_INPUT_SIZE_LIMIT + 1).toString(16).padStart(64, "0"); + + const input = "0x" + baseLenHex + expLenHex + modLenHex; + const gasLimit = 100000; + + const tx = await context.web3.eth.accounts.signTransaction( + { + from: GENESIS_ACCOUNT, + to: MODEXP_PRECOMPILE_ADDRESS, + data: input, + value: "0x00", + gasPrice: "0x3B9ACA00", + gas: gasLimit, + }, + GENESIS_ACCOUNT_PRIVATE_KEY + ); + + await customRequest(context.web3, "eth_sendRawTransaction", [tx.rawTransaction]); + await createAndFinalizeBlock(context.web3); + + // Get the transaction receipt + const receipt = await context.web3.eth.getTransactionReceipt(tx.transactionHash); + + // EIP-7823: Transaction should fail (status = false) + assert.equal(receipt.status, false, "Transaction should fail"); + + // EIP-7823: All gas should be consumed + assert.equal( + receipt.gasUsed.toString(), + gasLimit.toString(), + "EIP-7823 requires all gas to be consumed when modulus length exceeds limit" + ); + }); +}); diff --git a/ts-tests/tests/test-precompiles-shielded-pool.ts b/ts-tests/tests/test-precompiles-shielded-pool.ts index 08358dec..2806bb2b 100644 --- a/ts-tests/tests/test-precompiles-shielded-pool.ts +++ b/ts-tests/tests/test-precompiles-shielded-pool.ts @@ -202,13 +202,11 @@ describeWithFrontier("Frontier RPC (Precompile: ShieldedPool – routing)", (con describeWithFrontier("Frontier RPC (Precompile: ShieldedPool – used_addresses)", (context) => { // ── 7. Precompile address appears in the set of known precompiles ───────── it("should include 0x0801 in the precompile address list via eth_getCode", async () => { - // Precompile addresses return empty bytecode (0x) when queried with eth_getCode. - // This is a standard Frontier property: EOAs also return 0x, but what matters - // is that calls succeed (tested above). Here we just confirm the address is - // reachable and not confused with a contract deployment. + // Orbinum precompiles hold the stub `PUSH1 0 PUSH1 0 REVERT`, which never runs. + // Without code at the address, a Solidity high-level call to a function that + // returns nothing reverts at its code-size check before reaching the precompile. const code = await context.web3.eth.getCode(SHIELDED_POOL_PRECOMPILE); - // Frontier precompiles typically return "0x" for code. - assert.equal(code, "0x", "precompile address should report empty bytecode"); + assert.equal(code, "0x60006000fd", "precompile address should hold the revert stub"); }); // ── 8. Smoke-test: ABI-encoded data roundtrips correctly ─────────────────