Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions gasometer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ impl<'config> Gasometer<'config> {
pub fn call_transaction_cost(
data: &[u8],
access_list: &[(H160, Vec<H256>)],
authorization_list: &[(U256, H160, U256, H160)],
authorization_list: &[(U256, H160, U256, Option<H160>)],
) -> TransactionCost {
let zero_data_len = data.iter().filter(|v| **v == 0).count();
let non_zero_data_len = data.len() - zero_data_len;
Expand All @@ -358,7 +358,7 @@ pub fn call_transaction_cost(
pub fn create_transaction_cost(
data: &[u8],
access_list: &[(H160, Vec<H256>)],
authorization_list: &[(U256, H160, U256, H160)],
authorization_list: &[(U256, H160, U256, Option<H160>)],
) -> TransactionCost {
let zero_data_len = data.iter().filter(|v| **v == 0).count();
let non_zero_data_len = data.len() - zero_data_len;
Expand Down
17 changes: 11 additions & 6 deletions src/executor/stack/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet>
&mut self,
init_code: &[u8],
access_list: &[(H160, Vec<H256>)],
authorization_list: &[(U256, H160, U256, H160)],
authorization_list: &[(U256, H160, U256, Option<H160>)],
) -> Result<(), ExitError> {
let transaction_cost =
gasometer::create_transaction_cost(init_code, access_list, authorization_list);
Expand Down Expand Up @@ -462,7 +462,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet>
init_code: Vec<u8>,
gas_limit: u64,
access_list: Vec<(H160, Vec<H256>)>, // See EIP-2930
authorization_list: Vec<(U256, H160, U256, H160)>, // See EIP-7702
authorization_list: Vec<(U256, H160, U256, Option<H160>)>, // See EIP-7702
) -> (ExitReason, Vec<u8>) {
event!(TransactCreate {
caller,
Expand Down Expand Up @@ -520,7 +520,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet>
salt: H256,
gas_limit: u64,
access_list: Vec<(H160, Vec<H256>)>, // See EIP-2930
authorization_list: Vec<(U256, H160, U256, H160)>, // See EIP-7702
authorization_list: Vec<(U256, H160, U256, Option<H160>)>, // See EIP-7702
) -> (ExitReason, Vec<u8>) {
if let Some(limit) = self.config.max_initcode_size {
if init_code.len() > limit {
Expand Down Expand Up @@ -588,7 +588,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet>
init_code: Vec<u8>,
gas_limit: u64,
access_list: Vec<(H160, Vec<H256>)>, // See EIP-2930
authorization_list: Vec<(U256, H160, U256, H160)>, // See EIP-7702
authorization_list: Vec<(U256, H160, U256, Option<H160>)>, // See EIP-7702
contract_address: H160,
) -> (ExitReason, Vec<u8>) {
event!(TransactCreate {
Expand Down Expand Up @@ -654,7 +654,7 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet>
data: Vec<u8>,
gas_limit: u64,
access_list: Vec<(H160, Vec<H256>)>,
authorization_list: Vec<(U256, H160, U256, H160)>,
authorization_list: Vec<(U256, H160, U256, Option<H160>)>,
) -> (ExitReason, Vec<u8>) {
event!(TransactCall {
caller,
Expand Down Expand Up @@ -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<H160>)>,
) -> Result<(), ExitError> {
for authorization in authorization_list {
let (chain_id, delegation_address, nonce, authorizing_address) = authorization;
Expand All @@ -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
Expand Down
122 changes: 120 additions & 2 deletions tests/eip7702.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<H160>) {
(
chain_id,
delegation_address,
nonce,
Some(authorizing_address),
)
}

/// Create a test vicinity for EIP-7702 tests
Expand Down Expand Up @@ -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);
}
}