Skip to content

Commit

Permalink
fix eip7702 tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rakita committed Jan 8, 2025
1 parent db9edbc commit 5e90cf0
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 34 deletions.
47 changes: 30 additions & 17 deletions crates/handler/src/pre_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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();
}
Expand Down
20 changes: 11 additions & 9 deletions crates/precompile/src/secp256k1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
6 changes: 3 additions & 3 deletions crates/specification/src/eip7702.rs
Original file line number Diff line number Diff line change
@@ -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;
47 changes: 42 additions & 5 deletions crates/statetest-types/src/test_authorization.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -19,11 +23,44 @@ pub struct TestAuthorization {

impl From<TestAuthorization> 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();
}
}

0 comments on commit 5e90cf0

Please sign in to comment.