From 5e90cf078fdb60c3e8203f151313cd1608658caf Mon Sep 17 00:00:00 2001 From: rakita Date: Wed, 8 Jan 2025 03:54:37 +0100 Subject: [PATCH] fix eip7702 tests --- crates/handler/src/pre_execution.rs | 47 ++++++++++++------- crates/precompile/src/secp256k1.rs | 20 ++++---- crates/specification/src/eip7702.rs | 6 +-- .../statetest-types/src/test_authorization.rs | 47 +++++++++++++++++-- 4 files changed, 86 insertions(+), 34 deletions(-) diff --git a/crates/handler/src/pre_execution.rs b/crates/handler/src/pre_execution.rs index a8213028dc..857ddf3dfc 100644 --- a/crates/handler/src/pre_execution.rs +++ b/crates/handler/src/pre_execution.rs @@ -11,7 +11,7 @@ use context_interface::{ PerformantContextAccess, TransactionGetter, }; use handler_interface::PreExecutionHandler; -use primitives::{Address, BLOCKHASH_STORAGE_ADDRESS, U256}; +use primitives::{Address, BLOCKHASH_STORAGE_ADDRESS, KECCAK_EMPTY, U256}; use specification::{eip7702, hardfork::SpecId}; use std::{boxed::Box, vec::Vec}; @@ -145,45 +145,58 @@ pub fn apply_eip7702_auth_list< let mut refunded_accounts = 0; for authorization in authorization_list { - // 1. Recover authority and authorized addresses. - // authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s] - let Some(authority) = authorization.authority else { + // 1. Verify the chain id is either 0 or the chain's current ID. + let auth_chain_id = authorization.chain_id; + if !auth_chain_id.is_zero() && auth_chain_id != U256::from(chain_id) { continue; - }; + } - // 2. Verify the chain id is either 0 or the chain's current ID. - if authorization.chain_id.is_zero() && authorization.chain_id != U256::from(chain_id) { + // 2. Verify the `nonce` is less than `2**64 - 1`. + if authorization.nonce == u64::MAX { continue; } - // Warm authority account and check nonce. - // 3. Add authority to accessed_addresses (as defined in EIP-2929.) + // recover authority and authorized addresses. + // 3. `authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s]` + let Some(authority) = authorization.authority else { + continue; + }; + + // warm authority account and check nonce. + // 4. Add `authority` to `accessed_addresses` (as defined in [EIP-2929](./eip-2929.md).) let mut authority_acc = context.journal().load_account_code(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. if let Some(bytecode) = &authority_acc.info.code { - // If it is not empty and it is not eip7702 + // if it is not empty and it is not eip7702 if !bytecode.is_empty() && !bytecode.is_eip7702() { continue; } } - // 5. Verify the nonce of authority is equal to nonce. + // 6. Verify the nonce of `authority` is equal to `nonce`. In case `authority` does not exist in the trie, verify that `nonce` is equal to `0`. if authorization.nonce != authority_acc.info.nonce { continue; } - // 6. Refund the sender PER_EMPTY_ACCOUNT_COST - PER_AUTH_BASE_COST gas 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 !authority_acc.is_empty() { refunded_accounts += 1; } - // 7. Set the code of authority to be 0xef0100 || address. This is a delegation designation. - let bytecode = Bytecode::new_eip7702(authorization.address); - authority_acc.info.code_hash = bytecode.hash_slow(); + // 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 accounts code and reset the account's code hash to the empty hash `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`. + let (bytecode, hash) = if authorization.address.is_zero() { + (Bytecode::default(), KECCAK_EMPTY) + } else { + let bytecode = Bytecode::new_eip7702(authorization.address); + let hash = bytecode.hash_slow(); + (bytecode, hash) + }; + authority_acc.info.code_hash = hash; authority_acc.info.code = Some(bytecode); - // 8. Increase the nonce of authority by one. + // 9. Increase the nonce of `authority` by one. authority_acc.info.nonce = authority_acc.info.nonce.saturating_add(1); authority_acc.mark_touch(); } diff --git a/crates/precompile/src/secp256k1.rs b/crates/precompile/src/secp256k1.rs index 6911027cf6..86cfa8f9fe 100644 --- a/crates/precompile/src/secp256k1.rs +++ b/crates/precompile/src/secp256k1.rs @@ -31,16 +31,18 @@ pub fn ec_recover_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { let recid = input[63] - 27; let sig = <&B512>::try_from(&input[64..128]).unwrap(); - cfg_if::cfg_if! { - if #[cfg(feature = "secp256k1")] { - let res = bitcoin_secp256k1::ecrecover(sig, recid, msg); - } else if #[cfg(feature = "libsecp256k1")] { - let res = parity_libsecp256k1::ecrecover(sig, recid, msg); - } else { - let res = k256::ecrecover(sig, recid, msg); - } - }; + let res = ecrecover(sig, recid, msg); let out = res.map(|o| o.to_vec().into()).unwrap_or_default(); Ok(PrecompileOutput::new(ECRECOVER_BASE, out)) } + +cfg_if::cfg_if! { + if #[cfg(feature = "secp256k1")] { + pub use bitcoin_secp256k1::ecrecover; + } else if #[cfg(feature = "libsecp256k1")] { + pub use parity_libsecp256k1::ecrecover; + } else { + pub use k256::ecrecover; + } +} diff --git a/crates/specification/src/eip7702.rs b/crates/specification/src/eip7702.rs index a129f76732..248513fb3a 100644 --- a/crates/specification/src/eip7702.rs +++ b/crates/specification/src/eip7702.rs @@ -1,7 +1,7 @@ //! EIP-7702 constants -/// Base cost of updating authorized account -pub const PER_AUTH_BASE_COST: u64 = 2500; +/// Base cost of updating authorized account. +pub const PER_AUTH_BASE_COST: u64 = 12500; -/// Cost of creating authorized account that was previously empty +/// Cost of creating authorized account that was previously empty. pub const PER_EMPTY_ACCOUNT_COST: u64 = 25000; diff --git a/crates/statetest-types/src/test_authorization.rs b/crates/statetest-types/src/test_authorization.rs index 3399969964..327eb9a169 100644 --- a/crates/statetest-types/src/test_authorization.rs +++ b/crates/statetest-types/src/test_authorization.rs @@ -1,16 +1,20 @@ use revm::{ context_interface::transaction::AuthorizationItem, primitives::{Address, U256}, + specification::eip2::SECP256K1N_HALF, }; use serde::{Deserialize, Serialize}; /// Struct for test authorization -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct TestAuthorization { - chain_id: U256, - address: Address, - nonce: U256, + /// The chain ID of the authorization. + pub chain_id: U256, + /// The address of the authorization. + pub address: Address, + /// The nonce for the authorization. + pub nonce: U256, v: U256, r: U256, s: U256, @@ -19,11 +23,44 @@ pub struct TestAuthorization { impl From for AuthorizationItem { fn from(auth: TestAuthorization) -> AuthorizationItem { + let mut signer = auth.signer; + + if auth.s > SECP256K1N_HALF { + signer = None + } + + if auth.v > U256::from(1) { + signer = None + } + ( - auth.signer, + signer, auth.chain_id, auth.nonce.try_into().unwrap_or(u64::MAX), auth.address, ) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn recover_auth() { + // Test named: + // tests/prague/eip7702_set_code_tx/test_gas.py::test_account_warming[fork_Prague-state_test-single_valid_authorization_single_signer-check_delegated_account_first_True] + + let auth = r#"{ + "chainId": "0x00", + "address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "nonce": "0x00", + "v": "0x01", + "r": "0x5a8cac98fd240d8ef83c22db4a061ffa0facb1801245283cc05fc809d8b92837", + "s": "0x1c3162fe11d91bc24d4fa00fb19ca34531e0eacdf8142c804be44058d5b8244f", + "signer": "0x6389e7f33ce3b1e94e4325ef02829cd12297ef71" + }"#; + + let _: TestAuthorization = serde_json::from_str(auth).unwrap(); + } +}