diff --git a/gasometer/src/lib.rs b/gasometer/src/lib.rs index 657ea357..0c2ceb78 100644 --- a/gasometer/src/lib.rs +++ b/gasometer/src/lib.rs @@ -335,7 +335,7 @@ impl<'config> Gasometer<'config> { pub fn call_transaction_cost( data: &[u8], access_list: &[(H160, Vec)], - authorization_list: &[(U256, H160, U256, H160)], + authorization_list: &[(U256, H160, U256, Option)], ) -> TransactionCost { let zero_data_len = data.iter().filter(|v| **v == 0).count(); let non_zero_data_len = data.len() - zero_data_len; @@ -358,7 +358,7 @@ pub fn call_transaction_cost( pub fn create_transaction_cost( data: &[u8], access_list: &[(H160, Vec)], - authorization_list: &[(U256, H160, U256, H160)], + authorization_list: &[(U256, H160, U256, Option)], ) -> TransactionCost { let zero_data_len = data.iter().filter(|v| **v == 0).count(); let non_zero_data_len = data.len() - zero_data_len; diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index 406a71d3..8ef62caf 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -430,7 +430,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> &mut self, init_code: &[u8], access_list: &[(H160, Vec)], - authorization_list: &[(U256, H160, U256, H160)], + authorization_list: &[(U256, H160, U256, Option)], ) -> Result<(), ExitError> { let transaction_cost = gasometer::create_transaction_cost(init_code, access_list, authorization_list); @@ -462,7 +462,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> init_code: Vec, gas_limit: u64, access_list: Vec<(H160, Vec)>, // See EIP-2930 - authorization_list: Vec<(U256, H160, U256, H160)>, // See EIP-7702 + authorization_list: Vec<(U256, H160, U256, Option)>, // See EIP-7702 ) -> (ExitReason, Vec) { event!(TransactCreate { caller, @@ -520,7 +520,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> salt: H256, gas_limit: u64, access_list: Vec<(H160, Vec)>, // See EIP-2930 - authorization_list: Vec<(U256, H160, U256, H160)>, // See EIP-7702 + authorization_list: Vec<(U256, H160, U256, Option)>, // See EIP-7702 ) -> (ExitReason, Vec) { if let Some(limit) = self.config.max_initcode_size { if init_code.len() > limit { @@ -588,7 +588,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> init_code: Vec, gas_limit: u64, access_list: Vec<(H160, Vec)>, // See EIP-2930 - authorization_list: Vec<(U256, H160, U256, H160)>, // See EIP-7702 + authorization_list: Vec<(U256, H160, U256, Option)>, // See EIP-7702 contract_address: H160, ) -> (ExitReason, Vec) { event!(TransactCreate { @@ -654,7 +654,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> data: Vec, gas_limit: u64, access_list: Vec<(H160, Vec)>, - authorization_list: Vec<(U256, H160, U256, H160)>, + authorization_list: Vec<(U256, H160, U256, Option)>, ) -> (ExitReason, Vec) { event!(TransactCall { caller, @@ -791,7 +791,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> /// This processes the authorization tuples and applies delegations as specified pub fn initialize_with_authorization_list( &mut self, - authorization_list: Vec<(U256, H160, U256, H160)>, + authorization_list: Vec<(U256, H160, U256, Option)>, ) -> Result<(), ExitError> { for authorization in authorization_list { let (chain_id, delegation_address, nonce, authorizing_address) = authorization; @@ -806,6 +806,11 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> continue; } + // Skip if authorizing_address is None + let Some(authorizing_address) = authorizing_address else { + continue; + }; + // Add authority to accessed_addresses, as defined in EIP-2929 if self.config.increase_state_access_gas { self.state diff --git a/tests/eip7702.rs b/tests/eip7702.rs index 361ef6bf..4f13e760 100644 --- a/tests/eip7702.rs +++ b/tests/eip7702.rs @@ -16,8 +16,13 @@ fn create_authorization( delegation_address: H160, nonce: U256, authorizing_address: H160, -) -> (U256, H160, U256, H160) { - (chain_id, delegation_address, nonce, authorizing_address) +) -> (U256, H160, U256, Option) { + ( + chain_id, + delegation_address, + nonce, + Some(authorizing_address), + ) } /// Create a test vicinity for EIP-7702 tests @@ -4075,3 +4080,116 @@ fn test_12_2_signature_replay_protection() { // Nonce should still be 1 (no increment from failed authorization) assert_eq!(executor.state().basic(authorizing).nonce, U256::from(1)); } + +#[test] +fn test_none_authorizing_address_rejection() { + // Test: Authorization with None authorizing_address should be skipped + // We compare against a valid authorization to show the difference + let caller = H160::from_slice(&[1u8; 20]); + let implementation = H160::from_slice(&[2u8; 20]); + let authorizing = H160::from_slice(&[3u8; 20]); + let target = H160::from_slice(&[4u8; 20]); + + let config = Config::pectra(); + + let mut state = BTreeMap::new(); + state.insert( + caller, + evm::backend::MemoryAccount { + nonce: U256::zero(), + balance: U256::from(10_000_000), + storage: BTreeMap::new(), + code: Vec::new(), + }, + ); + + // Create implementation account with code + state.insert( + implementation, + evm::backend::MemoryAccount { + nonce: U256::zero(), + balance: U256::zero(), + storage: BTreeMap::new(), + code: vec![0x60, 0x42, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xf3], + }, + ); + + // Create empty authorizing account (suitable for delegation) + state.insert( + authorizing, + evm::backend::MemoryAccount { + nonce: U256::zero(), + balance: U256::zero(), + storage: BTreeMap::new(), + code: Vec::new(), + }, + ); + + let vicinity = create_test_vicinity(); + let mut backend = MemoryBackend::new(&vicinity, state); + + // Test 1: Transaction with None authorizing_address + { + let metadata = evm::executor::stack::StackSubstateMetadata::new(1000000, &config); + let state = evm::executor::stack::MemoryStackState::new(metadata, &mut backend); + let precompiles = BTreeMap::new(); + let mut executor = StackExecutor::new_with_precompiles(state, &config, &precompiles); + + let authorization_with_none = ( + U256::from(1), // chain_id matches vicinity + implementation, // delegation_address + U256::zero(), // nonce + None, // authorizing_address is None + ); + + let (exit_reason, _) = executor.transact_call( + caller, + target, + U256::zero(), + Vec::new(), + 100_000, + Vec::new(), + vec![authorization_with_none], + ); + + assert_eq!(exit_reason, ExitReason::Succeed(evm::ExitSucceed::Stopped)); + + // Authorizing account should be unchanged (no delegation applied) + let authorizing_basic = executor.state().basic(authorizing); + assert_eq!(authorizing_basic.nonce, U256::zero()); + assert!(executor.code(authorizing).is_empty()); + } + + // Test 2: Same transaction but with valid authorizing_address for comparison + { + let metadata = evm::executor::stack::StackSubstateMetadata::new(1000000, &config); + let state = evm::executor::stack::MemoryStackState::new(metadata, &mut backend); + let precompiles = BTreeMap::new(); + let mut executor = StackExecutor::new_with_precompiles(state, &config, &precompiles); + + let authorization_with_some = ( + U256::from(1), // chain_id matches vicinity + implementation, // delegation_address + U256::zero(), // nonce + Some(authorizing), // authorizing_address is Some + ); + + let (exit_reason, _) = executor.transact_call( + caller, + target, + U256::zero(), + Vec::new(), + 100_000, + Vec::new(), + vec![authorization_with_some], + ); + + assert_eq!(exit_reason, ExitReason::Succeed(evm::ExitSucceed::Stopped)); + + // Authorizing account should now have delegation designator + let authorizing_basic = executor.state().basic(authorizing); + assert_eq!(authorizing_basic.nonce, U256::from(1)); // Nonce incremented + let expected_delegation = evm_core::create_delegation_designator(implementation); + assert_eq!(executor.code(authorizing), expected_delegation); + } +}