diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0cdd0167..f005dc41 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -86,9 +86,9 @@ jobs: - name: Download Ethereum spec tests fixtures run: | - wget https://github.com/ethereum/execution-spec-tests/releases/download/pectra-devnet-3%40v1.5.0/fixtures_pectra-devnet-3.tar.gz + wget https://github.com/ethereum/execution-spec-tests/releases/download/pectra-devnet-5%40v1.1.0/fixtures_pectra-devnet-5.tar.gz mkdir ethereum-spec-tests - tar -xzf fixtures_pectra-devnet-3.tar.gz -C ethereum-spec-tests + tar -xzf fixtures_pectra-devnet-5.tar.gz -C ethereum-spec-tests tree ethereum-spec-tests/fixtures/state_tests/prague/eip7702_set_code_tx - name: Run Ethereum state tests diff --git a/Cargo.toml b/Cargo.toml index 40ba445f..6453b68b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,10 +9,10 @@ keywords.workspace = true edition.workspace = true [workspace.dependencies] -evm = { version = "0.46.2", path = "." } -evm-core = { version = "0.46.2", path = "core", default-features = false } -evm-gasometer = { version = "0.46.2", path = "gasometer", default-features = false } -evm-runtime = { version = "0.46.2", path = "runtime", default-features = false } +evm = { version = "0.46.3", path = "." } +evm-core = { version = "0.46.3", path = "core", default-features = false } +evm-gasometer = { version = "0.46.3", path = "gasometer", default-features = false } +evm-runtime = { version = "0.46.3", path = "runtime", default-features = false } primitive-types = { version = "0.13", default-features = false } auto_impl = "1.0" sha3 = { version = "0.10", default-features = false } @@ -84,7 +84,7 @@ create-fixed = [] print-debug = ["evm-gasometer/print-debug"] [workspace.package] -version = "0.46.2" +version = "0.46.3" license = "Apache-2.0" authors = ["Aurora Labs ", "Wei Tang ", "Parity Technologies "] description = "Portable Ethereum Virtual Machine implementation written in pure Rust." diff --git a/evm-tests/EIP-152/src/portable.rs b/evm-tests/EIP-152/src/portable.rs index 2b4e8907..589f91b9 100644 --- a/evm-tests/EIP-152/src/portable.rs +++ b/evm-tests/EIP-152/src/portable.rs @@ -18,7 +18,7 @@ use crate::{IV, SIGMA}; -/// The G mixing function. See https://tools.ietf.org/html/rfc7693#section-3.1 +/// The G mixing function. See #[inline(always)] fn g(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) { v[a] = v[a].wrapping_add(v[b]).wrapping_add(x); diff --git a/evm-tests/ethjson/src/bytes.rs b/evm-tests/ethjson/src/bytes.rs index 4e3c5953..dc393554 100644 --- a/evm-tests/ethjson/src/bytes.rs +++ b/evm-tests/ethjson/src/bytes.rs @@ -84,7 +84,7 @@ impl<'a> Deserialize<'a> for Bytes { struct BytesVisitor; -impl<'a> Visitor<'a> for BytesVisitor { +impl Visitor<'_> for BytesVisitor { type Value = Bytes; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { diff --git a/evm-tests/ethjson/src/uint.rs b/evm-tests/ethjson/src/uint.rs index 818ad479..db91d3b8 100644 --- a/evm-tests/ethjson/src/uint.rs +++ b/evm-tests/ethjson/src/uint.rs @@ -64,7 +64,7 @@ impl<'a> Deserialize<'a> for Uint { struct UintVisitor; -impl<'a> Visitor<'a> for UintVisitor { +impl Visitor<'_> for UintVisitor { type Value = Uint; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { diff --git a/evm-tests/jsontests/Cargo.toml b/evm-tests/jsontests/Cargo.toml index 4dad49be..6a1d7237 100644 --- a/evm-tests/jsontests/Cargo.toml +++ b/evm-tests/jsontests/Cargo.toml @@ -27,3 +27,4 @@ hex-literal = "0.4" [features] enable-slow-tests = [] +print-debug = ["evm/print-debug"] diff --git a/evm-tests/jsontests/src/main.rs b/evm-tests/jsontests/src/main.rs index 9aed9c30..8dcacf46 100644 --- a/evm-tests/jsontests/src/main.rs +++ b/evm-tests/jsontests/src/main.rs @@ -289,6 +289,12 @@ const SKIPPED_CASES: &[&str] = &[ // NOTE: this tests related to hard forks: London and before London "stRevertTest/RevertPrecompiledTouch", "stRevertTest/RevertPrecompiledTouch_storage", + // Wrong json fields `s`, `r` for EIP-7702 + "eip7702_set_code_tx/set_code_txs/invalid_tx_invalid_auth_signature", + // Wrong json field `chain_id` for EIP-7702 + "eip7702_set_code_tx/set_code_txs/tx_validity_nonce", + // EIP-7702: for non empty storage fails evm state hash check + "eip7702_set_code_tx/set_code_txs/set_code_to_non_empty_storage", ]; #[cfg(not(feature = "enable-slow-tests"))] @@ -305,6 +311,12 @@ const SKIPPED_CASES: &[&str] = &[ "stTimeConsuming/static_Call50000_sha256", "vmPerformance/loopMul", "stTimeConsuming/CALLBlake2f_MaxRounds", + // Wrong json fields `s`, `r` for EIP-7702 + "eip7702_set_code_tx/set_code_txs/invalid_tx_invalid_auth_signature", + // Wrong json field `chain_id` for EIP-7702 + "eip7702_set_code_tx/set_code_txs/tx_validity_nonce", + // EIP-7702: for non empty storage fails evm state hash check + "eip7702_set_code_tx/set_code_txs/set_code_to_non_empty_storage", ]; /// Check if a path should be skipped. diff --git a/evm-tests/jsontests/src/state.rs b/evm-tests/jsontests/src/state.rs index 9ae8f416..8651f02b 100644 --- a/evm-tests/jsontests/src/state.rs +++ b/evm-tests/jsontests/src/state.rs @@ -942,7 +942,7 @@ fn check_validate_exit_reason( ) -> bool { expect_exception.as_deref().map_or_else( || { - panic!("unexpected validation error reason: {reason:?}"); + panic!("unexpected validation error reason: {reason:?} {name}"); }, |exception| { match reason { @@ -1034,6 +1034,13 @@ fn check_validate_exit_reason( "unexpected exception {exception:?} for BlobVersionedHashesNotSupported for test: [{spec:?}] {name}" ); }, + InvalidTxReason::InvalidAuthorizationChain => { + let check_result = exception == "TransactionException.TYPE_4_INVALID_AUTHORIZATION_FORMAT"; + assert!( + check_result, + "unexpected exception {exception:?} for InvalidAuthorizationChain for test: [{spec:?}] {name}" + ); + } InvalidTxReason::InvalidAuthorizationSignature => { let check_result = exception == "TransactionException.TYPE_4_INVALID_AUTHORITY_SIGNATURE"; assert!( @@ -1042,7 +1049,7 @@ fn check_validate_exit_reason( ); } InvalidTxReason::AuthorizationListNotExist => { - let check_result = exception == "TransactionException.TYPE_4_EMPTY_AUTHORIZATION_LIST"; + let check_result = exception == "TransactionException.TYPE_4_EMPTY_AUTHORIZATION_LIST" || exception == "TransactionException.TYPE_4_TX_CONTRACT_CREATION"; assert!( check_result, "unexpected exception {exception:?} for AuthorizationListNotExist for test: [{spec:?}] {name}" @@ -1175,7 +1182,7 @@ fn test_run( // even if `caller_code` is non-empty transaction should be executed. let is_delegated = original_state .get(&caller) - .map_or(false, |c| Authorization::is_delegated(&c.code)); + .is_some_and(|c| Authorization::is_delegated(&c.code)); for (i, state) in states.iter().enumerate() { let transaction = test_tx.select(&state.indexes); diff --git a/evm-tests/jsontests/src/utils.rs b/evm-tests/jsontests/src/utils.rs index 65f68cef..09cab99d 100644 --- a/evm-tests/jsontests/src/utils.rs +++ b/evm-tests/jsontests/src/utils.rs @@ -447,35 +447,29 @@ pub mod transaction { return Err(InvalidTxReason::AuthorizationListNotExist); } - // The field `to` deviates slightly from the semantics with the exception - // that it MUST NOT be nil and therefore must always represent - // a 20-byte address. This means that blob transactions cannot - // have the form of a create transaction. - let to_address: Option
= test_tx.to.clone().into(); - if to_address.is_none() { - return Err(InvalidTxReason::CreateTransaction); - } - // Check EIP-7702 Spec validation steps: 1 and 2 // Other validation step inside EVM transact logic. for auth in test_tx.authorization_list.iter() { // 1. Verify the chain id is either 0 or the chain’s current ID. - let mut is_valid = - auth.chain_id.0 == U256::from(0) || auth.chain_id.0 == vicinity.chain_id; - // 2. `authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s]` + let mut is_valid = if auth.chain_id.0 > U256::from(u64::MAX) { + false + } else { + auth.chain_id.0 == U256::from(0) || auth.chain_id.0 == vicinity.chain_id + }; + // 3. `authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s]` // Validate the signature, as in tests it is possible to have invalid signatures values. let v = auth.v.0 .0; if !(v[0] < u64::from(u8::MAX) && v[1..4].iter().all(|&elem| elem == 0)) { - return Err(InvalidTxReason::InvalidAuthorizationSignature); + is_valid = false; } // Value `v` shouldn't be greater then 1 if v[0] > 1 { - return Err(InvalidTxReason::InvalidAuthorizationSignature); + is_valid = false; } // EIP-2 validation if auth.s.0 > eip7702::SECP256K1N_HALF { - return Err(InvalidTxReason::InvalidAuthorizationSignature); + is_valid = false; } let auth_address = eip7702::SignedAuthorization::new( @@ -549,6 +543,7 @@ pub mod transaction { GasPriseEip1559, AuthorizationListNotExist, AuthorizationListNotSupported, + InvalidAuthorizationChain, InvalidAuthorizationSignature, CreateTransaction, } diff --git a/fuzzer/src/main.rs b/fuzzer/src/main.rs index fe946cd1..1a769cce 100644 --- a/fuzzer/src/main.rs +++ b/fuzzer/src/main.rs @@ -2,9 +2,9 @@ use evm_core::Machine; use std::rc::Rc; fn find_subsequence(haystack: &[u8], needle: &[u8]) -> Option { - return haystack + haystack .windows(needle.len()) - .position(|window| window == needle); + .position(|window| window == needle) } fn split_at_delim(sequence: &[u8], delim: &[u8]) -> (Vec, Vec) { diff --git a/gasometer/src/lib.rs b/gasometer/src/lib.rs index 222c8938..6a199afb 100644 --- a/gasometer/src/lib.rs +++ b/gasometer/src/lib.rs @@ -617,6 +617,16 @@ fn get_and_set_warm(handler: &mut H, target: H160) -> (bool, Option< (target_is_cold, delegated_designator_is_cold) } +/// Get and set warm address if it's not warmed for non-delegated opcodes like `EXT*`. +/// NOTE: Related to EIP-7702 +fn get_and_set_non_delegated_warm(handler: &mut H, target: H160) -> bool { + let target_is_cold = handler.is_cold(target, None); + if target_is_cold { + handler.warm_target((target, None)); + } + target_is_cold +} + /// Calculate the opcode cost. /// /// # Errors @@ -674,11 +684,8 @@ pub fn dynamic_opcode_cost( Opcode::EXTCODESIZE => { let target = stack.peek_h256(0)?.into(); - let (target_is_cold, delegated_designator_is_cold) = get_and_set_warm(handler, target); - GasCost::ExtCodeSize { - target_is_cold, - delegated_designator_is_cold, - } + let target_is_cold = get_and_set_non_delegated_warm(handler, target); + GasCost::ExtCodeSize { target_is_cold } } Opcode::BALANCE => { let target = stack.peek_h256(0)?.into(); @@ -692,11 +699,8 @@ pub fn dynamic_opcode_cost( Opcode::EXTCODEHASH if config.has_ext_code_hash => { let target = stack.peek_h256(0)?.into(); - let (target_is_cold, delegated_designator_is_cold) = get_and_set_warm(handler, target); - GasCost::ExtCodeHash { - target_is_cold, - delegated_designator_is_cold, - } + let target_is_cold = get_and_set_non_delegated_warm(handler, target); + GasCost::ExtCodeHash { target_is_cold } } Opcode::EXTCODEHASH => GasCost::Invalid(opcode), @@ -732,10 +736,9 @@ pub fn dynamic_opcode_cost( }, Opcode::EXTCODECOPY => { let target = stack.peek_h256(0)?.into(); - let (target_is_cold, delegated_designator_is_cold) = get_and_set_warm(handler, target); + let target_is_cold = get_and_set_non_delegated_warm(handler, target); GasCost::ExtCodeCopy { target_is_cold, - delegated_designator_is_cold, len: stack.peek(3)?, } } @@ -934,7 +937,7 @@ struct Inner<'config> { config: &'config Config, } -impl<'config> Inner<'config> { +impl Inner<'_> { fn memory_gas(&self, memory: MemoryCost) -> Result { let from = memory.offset; let len = memory.len; @@ -1051,25 +1054,16 @@ impl<'config> Inner<'config> { GasCost::Low => u64::from(consts::G_LOW), GasCost::Invalid(opcode) => return Err(ExitError::InvalidCode(opcode)), - GasCost::ExtCodeSize { - target_is_cold, - delegated_designator_is_cold, - } => costs::address_access_cost( + GasCost::ExtCodeSize { target_is_cold } => costs::address_access_cost( target_is_cold, - delegated_designator_is_cold, + None, self.config.gas_ext_code, self.config, ), GasCost::ExtCodeCopy { target_is_cold, - delegated_designator_is_cold, len, - } => costs::extcodecopy_cost( - len, - target_is_cold, - delegated_designator_is_cold, - self.config, - )?, + } => costs::extcodecopy_cost(len, target_is_cold, None, self.config)?, GasCost::Balance { target_is_cold } => costs::address_access_cost( target_is_cold, None, @@ -1077,12 +1071,9 @@ impl<'config> Inner<'config> { self.config, ), GasCost::BlockHash => u64::from(consts::G_BLOCKHASH), - GasCost::ExtCodeHash { - target_is_cold, - delegated_designator_is_cold, - } => costs::address_access_cost( + GasCost::ExtCodeHash { target_is_cold } => costs::address_access_cost( target_is_cold, - delegated_designator_is_cold, + None, self.config.gas_ext_code_hash, self.config, ), @@ -1126,8 +1117,6 @@ pub enum GasCost { ExtCodeSize { /// True if address has not been previously accessed in this transaction target_is_cold: bool, - /// True if delegated designator of authority has not been previously accessed in this transaction (EIP-7702) - delegated_designator_is_cold: Option, }, /// Gas cost for `BALANCE`. Balance { @@ -1140,8 +1129,6 @@ pub enum GasCost { ExtCodeHash { /// True if address has not been previously accessed in this transaction target_is_cold: bool, - /// True if delegated designator of authority has not been previously accessed in this transaction (EIP-7702) - delegated_designator_is_cold: Option, }, /// Gas cost for `CALL`. @@ -1230,8 +1217,6 @@ pub enum GasCost { ExtCodeCopy { /// True if target has not been previously accessed in this transaction target_is_cold: bool, - /// True if delegated designator of authority has not been previously accessed in this transaction (EIP-7702) - delegated_designator_is_cold: Option, /// Length. len: U256, }, diff --git a/runtime/src/eval/system.rs b/runtime/src/eval/system.rs index 2c0c181f..4128435a 100644 --- a/runtime/src/eval/system.rs +++ b/runtime/src/eval/system.rs @@ -126,6 +126,7 @@ pub fn blob_hash(runtime: &mut Runtime, handler: &H) -> Control { Control::Continue } +/// NOTE: For EIP-7702 should return 2 (size of `0xEF01`) pub fn extcodesize(runtime: &mut Runtime, handler: &mut H) -> Control { pop_h256!(runtime, address); push_u256!(runtime, handler.code_size(address.into())); @@ -133,6 +134,7 @@ pub fn extcodesize(runtime: &mut Runtime, handler: &mut H) -> Contro Control::Continue } +/// NOTE: For EIP-7702 should return `keccak(0xEF01)` pub fn extcodehash(runtime: &mut Runtime, handler: &mut H) -> Control { pop_h256!(runtime, address); push_h256!(runtime, handler.code_hash(address.into())); @@ -140,7 +142,8 @@ pub fn extcodehash(runtime: &mut Runtime, handler: &mut H) -> Contro Control::Continue } -pub fn extcodecopy(runtime: &mut Runtime, handler: &mut H) -> Control { +/// NOTE: For EIP-7702 should not copy from designated address +pub fn extcodecopy(runtime: &mut Runtime, handler: &H) -> Control { pop_h256!(runtime, address); pop_u256!(runtime, memory_offset, code_offset, len); @@ -160,7 +163,7 @@ pub fn extcodecopy(runtime: &mut Runtime, handler: &mut H) -> Contro memory_offset, code_offset, len, - &handler.authority_code(address.into()), + &handler.code(address.into()), ) { Ok(()) => (), Err(e) => return Control::Exit(e.into()), diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 7f66c80a..2f4d179a 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -683,7 +683,7 @@ impl DerivedConfigInputs { let mut config = Self::cancun(); config.has_authorization_list = true; config.gas_per_empty_account_cost = 25000; - config.gas_per_auth_base_cost = 2500; + config.gas_per_auth_base_cost = 12500; config } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 812fd681..097e68d1 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.82.0" +channel = "1.84.0" profile = "minimal" components = ["rustfmt", "clippy"] diff --git a/src/backend/memory.rs b/src/backend/memory.rs index 619897d6..c60f962e 100644 --- a/src/backend/memory.rs +++ b/src/backend/memory.rs @@ -96,7 +96,7 @@ impl<'vicinity> MemoryBackend<'vicinity> { } } -impl<'vicinity> Backend for MemoryBackend<'vicinity> { +impl Backend for MemoryBackend<'_> { #[allow(clippy::misnamed_getters)] fn gas_price(&self) -> U256 { self.vicinity.effective_gas_price @@ -186,7 +186,7 @@ impl<'vicinity> Backend for MemoryBackend<'vicinity> { } } -impl<'vicinity> ApplyBackend for MemoryBackend<'vicinity> { +impl ApplyBackend for MemoryBackend<'_> { fn apply(&mut self, values: A, logs: L, delete_empty: bool) where A: IntoIterator>, diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index 4aa89b48..8544faa0 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -47,6 +47,11 @@ macro_rules! try_or_fail { } const DEFAULT_CALL_STACK_CAPACITY: usize = 4; +// For EIP-7702 Hash of `EF01` bytes that is used for `EXTCODEHASH` when called from delegated address. +const EIP7702_MAGIC_HASH: [u8; 32] = [ + 0xEA, 0xDC, 0xDB, 0xA6, 0x6A, 0x79, 0xAB, 0x5D, 0xCE, 0x91, 0x62, 0x2D, 0x1D, 0x75, 0xC8, 0xCF, + 0xF5, 0xCF, 0xF0, 0xB9, 0x69, 0x44, 0xC3, 0xBF, 0x10, 0x72, 0xCD, 0x08, 0xCE, 0x01, 0x83, 0x29, +]; const fn l64(gas: u64) -> u64 { gas - gas / 64 @@ -930,16 +935,18 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> /// Authorized accounts behavior. /// /// According to `EIP-7702` behavior section should be several steps of verifications. - /// Current function includes steps 3-8 from the spec: - /// 3. Add `authority` to `accessed_addresses` - /// 4. Verify the code of `authority` is either empty or already delegated. - /// 5. Verify the `nonce` of `authority` is equal to `nonce` (of address). - /// 7. Set the code of `authority` to be `0xef0100 || address`. This is a delegation designation. - /// 8. Increase the `nonce` of `authority` by one. + /// Current function includes steps 2.4-9 from the spec: + /// 2. Verify the `nonce` is less than `2**64 - 1`. + /// 4. Add `authority` to `accessed_addresses` + /// 5. Verify the code of `authority` is either empty or already delegated. + /// 6. Verify the `nonce` of `authority` is equal to `nonce` (of address). + /// 7. Add `PER_EMPTY_ACCOUNT_COST - PER_AUTH_BASE_COST` gas to the global refund counter if authority exists in the trie. + /// 8. Set the code of `authority` to be `0xef0100 || address`. This is a delegation designation. + /// 9. Increase the `nonce` of `authority` by one. /// - /// It means, that steps 1-2 of spec must be passed before calling this function: + /// It means, that steps 1,3 of spec must be passed before calling this function: /// 1 Verify the chain id is either 0 or the chain’s current ID. - /// 2. `authority = ecrecover(...)` + /// 3. `authority = ecrecover(...)` /// /// See: [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702#behavior) /// @@ -957,38 +964,54 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> let state = self.state_mut(); let mut warm_authority: Vec = Vec::with_capacity(authorization_list.len()); for authority in authorization_list { - // If EIP-7703 Spec validation steps 1 or 2 return false. + // If EIP-7702 Spec validation steps 1, 3 return false. if !authority.is_valid { continue; } - // 3. Add authority to accessed_addresses (as defined in EIP-2929) + + // 2. Verify the `nonce` is less than `2**64 - 1`. + if U256::from(authority.nonce) >= U64_MAX { + continue; + } + + // 4. Add authority to accessed_addresses (as defined in EIP-2929) warm_authority.push(authority.authority); - // 4. Verify the code of authority is either empty or already delegated. + // 5. Verify the code of authority is either empty or already delegated. let authority_code = state.code(authority.authority); if !authority_code.is_empty() && !Authorization::is_delegated(&authority_code) { continue; } - // 5. Verify the nonce of authority is equal to nonce. + // 6. Verify the nonce of authority is equal to nonce. if state.basic(authority.authority).nonce != U256::from(authority.nonce) { continue; } - // 6. Add PER_EMPTY_ACCOUNT_COST - PER_AUTH_BASE_COST gas to the global refund counter if authority exists in the trie. + // 7. Add PER_EMPTY_ACCOUNT_COST - PER_AUTH_BASE_COST gas to the global refund counter if authority exists in the trie. if !state.is_empty(authority.authority) { refunded_accounts += 1; } - // 7. Set the code of authority to be `0xef0100 || address`. This is a delegation designation. - state.set_code(authority.authority, authority.delegation_code()); - // 8. Increase the nonce of authority by one. + // 8. Set the code of authority to be `0xef0100 || address`. This is a delegation designation. + // * As a special case, if address is 0x0000000000000000000000000000000000000000 do not write the designation. + // Clear the account’s code. + let mut delegation_clearing = false; + if authority.address.is_zero() { + delegation_clearing = true; + state.set_code(authority.authority, Vec::new()); + } else { + state.set_code(authority.authority, authority.delegation_code()); + } + // 9. Increase the nonce of authority by one. state.inc_nonce(authority.authority)?; // Add to authority access list cache - state - .metadata_mut() - .add_authority(authority.authority, authority.address); + if !delegation_clearing { + state + .metadata_mut() + .add_authority(authority.authority, authority.address); + } } - // Warm addresses for [Step 3]. + // Warm addresses for [Step 4]. self.state .metadata_mut() .access_addresses(warm_authority.into_iter()); @@ -1342,8 +1365,8 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> } } -impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> InterpreterHandler - for StackExecutor<'config, 'precompiles, S, P> +impl<'config, S: StackState<'config>, P: PrecompileSet> InterpreterHandler + for StackExecutor<'config, '_, S, P> { #[inline] fn before_eval(&mut self) {} @@ -1424,8 +1447,8 @@ pub struct StackExecutorCallInterrupt<'borrow>(TaggedRuntime<'borrow>); pub struct StackExecutorCreateInterrupt<'borrow>(TaggedRuntime<'borrow>); -impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler - for StackExecutor<'config, 'precompiles, S, P> +impl<'config, S: StackState<'config>, P: PrecompileSet> Handler + for StackExecutor<'config, '_, S, P> { type CreateInterrupt = StackExecutorCreateInterrupt<'static>; type CreateFeedback = Infallible; @@ -1442,28 +1465,33 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler /// Provide a default implementation by fetching the code. /// /// According to EIP-7702, the code size of an address is the size of the - /// delegated address code size. + /// `0xef01` - `2`. + /// fn code_size(&mut self, address: H160) -> U256 { - let target_code = self.authority_code(address); - U256::from(target_code.len()) + // If deleted account, return size equal to 2 bytes + if self.get_authority_target(address).is_some() { + U256::from(2) + } else { + let target_code = self.authority_code(address); + U256::from(target_code.len()) + } } /// Fetch the code hash of an address. /// Provide a default implementation by fetching the code. /// /// According to EIP-7702, the code hash of an address is the hash of the - /// delegated address code hash. + /// `keccak256(0xef01): 0xeadcdba66a79ab5dce91622d1d75c8cff5cff0b96944c3bf1072cd08ce018329`. + /// fn code_hash(&mut self, address: H160) -> H256 { if !self.exists(address) { return H256::default(); } - if let Some(target) = self.get_authority_target(address) { - if !self.exists(target) { - return H256::default(); - } + if self.get_authority_target(address).is_some() { + H256::from(EIP7702_MAGIC_HASH) + } else { + H256::from_slice(Keccak256::digest(self.code(address)).as_slice()) } - let code = self.authority_code(address); - H256::from_slice(Keccak256::digest(code).as_slice()) } /// Get account code @@ -1772,8 +1800,8 @@ struct StackExecutorHandle<'inner, 'config, 'precompiles, S, P> { is_static: bool, } -impl<'inner, 'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> PrecompileHandle - for StackExecutorHandle<'inner, 'config, 'precompiles, S, P> +impl<'config, S: StackState<'config>, P: PrecompileSet> PrecompileHandle + for StackExecutorHandle<'_, 'config, '_, S, P> { // Perform subcall in provided context. /// Precompile specifies in which context the subcall is executed. @@ -1918,3 +1946,14 @@ impl<'inner, 'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Pr self.gas_limit } } + +#[cfg(test)] +mod tests { + use super::EIP7702_MAGIC_HASH; + use sha3::{Digest, Keccak256}; + #[test] + fn test_ef01_hash() { + let hash = Keccak256::digest([0xEF, 0x01]); + assert_eq!(hash.as_slice(), EIP7702_MAGIC_HASH); + } +} diff --git a/src/executor/stack/memory.rs b/src/executor/stack/memory.rs index 4606d400..3bc9f91a 100644 --- a/src/executor/stack/memory.rs +++ b/src/executor/stack/memory.rs @@ -368,6 +368,8 @@ impl<'config> MemoryStackSubstate<'config> { } pub fn set_storage(&mut self, address: H160, key: H256, value: H256) { + #[cfg(feature = "print-debug")] + println!(" [SSTORE {address:?}] {key:?}:{value:?}"); self.storages.insert((address, key), value); } @@ -517,7 +519,7 @@ pub struct MemoryStackState<'backend, 'config, B> { substate: MemoryStackSubstate<'config>, } -impl<'backend, 'config, B: Backend> Backend for MemoryStackState<'backend, 'config, B> { +impl Backend for MemoryStackState<'_, '_, B> { fn gas_price(&self) -> U256 { self.backend.gas_price() } @@ -594,7 +596,7 @@ impl<'backend, 'config, B: Backend> Backend for MemoryStackState<'backend, 'conf } } -impl<'backend, 'config, B: Backend> StackState<'config> for MemoryStackState<'backend, 'config, B> { +impl<'config, B: Backend> StackState<'config> for MemoryStackState<'_, 'config, B> { fn metadata(&self) -> &StackSubstateMetadata<'config> { self.substate.metadata() } diff --git a/src/maybe_borrowed.rs b/src/maybe_borrowed.rs index c5dcc686..a10644f5 100644 --- a/src/maybe_borrowed.rs +++ b/src/maybe_borrowed.rs @@ -14,7 +14,7 @@ pub enum MaybeBorrowed<'a, T> { Owned(T), } -impl<'a, T> core::ops::Deref for MaybeBorrowed<'a, T> { +impl core::ops::Deref for MaybeBorrowed<'_, T> { type Target = T; fn deref(&self) -> &Self::Target { @@ -25,7 +25,7 @@ impl<'a, T> core::ops::Deref for MaybeBorrowed<'a, T> { } } -impl<'a, T> core::ops::DerefMut for MaybeBorrowed<'a, T> { +impl core::ops::DerefMut for MaybeBorrowed<'_, T> { fn deref_mut(&mut self) -> &mut Self::Target { match self { Self::Borrowed(x) => x,