From daf73eea96a71565dc3627359e99eb15f0dc3e29 Mon Sep 17 00:00:00 2001 From: john xu Date: Tue, 17 Oct 2023 13:18:20 +0000 Subject: [PATCH 1/6] feat: support taiko protocol --- crates/interpreter/Cargo.toml | 1 + crates/precompile/Cargo.toml | 1 + crates/primitives/Cargo.toml | 1 + crates/primitives/src/env.rs | 36 ++++++ crates/primitives/src/lib.rs | 4 + crates/primitives/src/result.rs | 3 + crates/primitives/src/taiko.rs | 5 + crates/primitives/src/taiko/anchor.rs | 27 ++++ crates/primitives/src/taiko/env.rs | 15 +++ crates/revm/Cargo.toml | 1 + crates/revm/src/evm_impl.rs | 28 ++++- crates/revm/src/handler.rs | 13 ++ crates/revm/src/handler/taiko.rs | 175 ++++++++++++++++++++++++++ 13 files changed, 308 insertions(+), 2 deletions(-) create mode 100644 crates/primitives/src/taiko.rs create mode 100644 crates/primitives/src/taiko/anchor.rs create mode 100644 crates/primitives/src/taiko/env.rs create mode 100644 crates/revm/src/handler/taiko.rs diff --git a/crates/interpreter/Cargo.toml b/crates/interpreter/Cargo.toml index bc233bec39..ff50020eb7 100644 --- a/crates/interpreter/Cargo.toml +++ b/crates/interpreter/Cargo.toml @@ -22,6 +22,7 @@ serde = ["dep:serde", "revm-primitives/serde"] arbitrary = ["std", "revm-primitives/arbitrary"] optimism = ["revm-primitives/optimism"] +taiko = ["revm-primitives/taiko"] dev = [ "memory_limit", diff --git a/crates/precompile/Cargo.toml b/crates/precompile/Cargo.toml index 99d8183afa..aa3c49c93f 100644 --- a/crates/precompile/Cargo.toml +++ b/crates/precompile/Cargo.toml @@ -43,6 +43,7 @@ std = [ ] optimism = ["revm-primitives/optimism"] +taiko = ["revm-primitives/taiko"] # This library may not work on all no_std platforms as they depend on C libraries. diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 640a5c045a..1b27147d56 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -47,6 +47,7 @@ serde = [ arbitrary = ["std", "alloy-primitives/arbitrary", "bitflags/arbitrary"] optimism = [] +taiko = ["dep:once_cell"] dev = [ "memory_limit", diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs index 1c64b735fb..11634bc4fd 100644 --- a/crates/primitives/src/env.rs +++ b/crates/primitives/src/env.rs @@ -3,6 +3,8 @@ use crate::{ InvalidTransaction, Spec, SpecId, B256, GAS_PER_BLOB, KECCAK_EMPTY, MAX_BLOB_NUMBER_PER_BLOCK, MAX_INITCODE_SIZE, U256, VERSIONED_HASH_VERSION_KZG, }; +#[cfg(feature = "taiko")] +use crate::{EVMError, TaikoEnv, TxType}; use core::cmp::{min, Ordering}; #[derive(Clone, Debug, Default, PartialEq, Eq)] @@ -11,6 +13,23 @@ pub struct Env { pub cfg: CfgEnv, pub block: BlockEnv, pub tx: TxEnv, + #[cfg(feature = "taiko")] + /// Configuration of the taiko + pub taiko: TaikoEnv, +} + +#[cfg(feature = "taiko")] +impl Env { + pub fn pre_check(&self) -> Result<(), EVMError> { + if !crate::anchor::validate(self) { + return Err(InvalidTransaction::InvalidAnchorTransaction.into()); + } + Ok(()) + } + + pub fn is_anchor(&self) -> bool { + self.tx.index == 0 + } } /// The block environment. @@ -129,6 +148,13 @@ impl BlockEnv { #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TxEnv { + /// The index of the transaction in the block. + #[cfg(feature = "taiko")] + pub index: usize, + /// The type of the transaction. + #[cfg(feature = "taiko")] + pub tx_type: TxType, + /// Caller aka Author aka transaction signer. pub caller: Address, /// The gas limit of the transaction. @@ -428,6 +454,11 @@ impl Default for BlockEnv { impl Default for TxEnv { fn default() -> Self { Self { + #[cfg(feature = "taiko")] + index: 0, + #[cfg(feature = "taiko")] + tx_type: TxType::Legacy, + caller: Address::ZERO, gas_limit: u64::MAX, gas_price: U256::ZERO, @@ -648,6 +679,11 @@ impl Env { .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; } + #[cfg(feature = "taiko")] + if self.is_anchor() { + return Ok(()); + } + // Check if account has enough balance for gas_limit*gas_price and value transfer. // Transfer will be done inside `*_inner` functions. if balance_check > account.info.balance { diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 00cda698d4..7590cdda81 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -14,6 +14,8 @@ pub mod precompile; pub mod result; pub mod specification; pub mod state; +#[cfg(feature = "taiko")] +pub mod taiko; pub mod utilities; pub use alloy_primitives::{ @@ -32,4 +34,6 @@ pub use precompile::*; pub use result::*; pub use specification::*; pub use state::*; +#[cfg(feature = "taiko")] +pub use taiko::*; pub use utilities::*; diff --git a/crates/primitives/src/result.rs b/crates/primitives/src/result.rs index e5f230099c..9de92af069 100644 --- a/crates/primitives/src/result.rs +++ b/crates/primitives/src/result.rs @@ -208,6 +208,9 @@ pub enum InvalidTransaction { /// post-regolith hardfork. #[cfg(feature = "optimism")] DepositSystemTxPostRegolith, + /// Anchor check failed + #[cfg(feature = "taiko")] + InvalidAnchorTransaction, } impl From for EVMError { diff --git a/crates/primitives/src/taiko.rs b/crates/primitives/src/taiko.rs new file mode 100644 index 0000000000..d9c7e06738 --- /dev/null +++ b/crates/primitives/src/taiko.rs @@ -0,0 +1,5 @@ +pub mod anchor; +pub mod env; + +pub use anchor::*; +pub use env::*; diff --git a/crates/primitives/src/taiko/anchor.rs b/crates/primitives/src/taiko/anchor.rs new file mode 100644 index 0000000000..c63c1dac70 --- /dev/null +++ b/crates/primitives/src/taiko/anchor.rs @@ -0,0 +1,27 @@ +use super::env::TxType; +use crate::{Address, Env, TransactTo, U256}; +use once_cell::sync::Lazy; +use std::str::FromStr; + +const ANCHOR_SELECTOR: u32 = 0xda69d3db; +const ANCHOR_GAS_LIMIT: u64 = 180_000; +static GOLDEN_TOUCH_ACCOUNT: Lazy
= Lazy::new(|| { + Address::from_str("0x0000777735367b36bC9B61C50022d9D0700dB4Ec") + .expect("invalid golden touch account") +}); + +pub static TREASURY: Lazy
= Lazy::new(|| { + Address::from_str("0xdf09A0afD09a63fb04ab3573922437e1e637dE8b") + .expect("invalid treasury account") +}); + +pub(crate) fn validate(env: &Env) -> bool { + !env.is_anchor() + || (env.tx.tx_type == TxType::Eip1559 + && env.tx.transact_to == TransactTo::Call(env.taiko.l2_address) + && u32::from_be_bytes(env.tx.data[..4].try_into().unwrap()) == ANCHOR_SELECTOR + && env.tx.value == U256::ZERO + && env.tx.gas_limit == ANCHOR_GAS_LIMIT + && env.tx.gas_price == env.block.basefee + && env.tx.caller == *GOLDEN_TOUCH_ACCOUNT) +} diff --git a/crates/primitives/src/taiko/env.rs b/crates/primitives/src/taiko/env.rs new file mode 100644 index 0000000000..b46879fd9d --- /dev/null +++ b/crates/primitives/src/taiko/env.rs @@ -0,0 +1,15 @@ +use crate::Address; + +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TaikoEnv { + pub l2_address: Address, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum TxType { + Legacy, + Eip2930, + Eip1559, +} diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 017787ce3f..b70ed60f00 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -41,6 +41,7 @@ serde = ["dep:serde", "dep:serde_json", "revm-interpreter/serde"] arbitrary = ["revm-interpreter/arbitrary"] optimism = ["revm-interpreter/optimism", "revm-precompile/optimism"] +taiko = ["revm-interpreter/taiko", "revm-precompile/taiko"] ethersdb = ["std", "tokio", "futures", "ethers-providers", "ethers-core"] diff --git a/crates/revm/src/evm_impl.rs b/crates/revm/src/evm_impl.rs index 577b219e3f..69c6e106c8 100644 --- a/crates/revm/src/evm_impl.rs +++ b/crates/revm/src/evm_impl.rs @@ -158,6 +158,9 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> Transact fn preverify_transaction(&mut self) -> Result<(), EVMError> { let env = self.env(); + #[cfg(feature = "taiko")] + env.pre_check()?; + // Important: validate block before tx. env.validate_block_env::()?; env.validate_tx::()?; @@ -193,6 +196,8 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> Transact let tx_value = env.tx.value; let tx_data = env.tx.data.clone(); let tx_gas_limit = env.tx.gas_limit; + #[cfg(feature = "taiko")] + let is_anchor = env.is_anchor(); #[cfg(feature = "optimism")] let tx_l1_cost = { @@ -278,8 +283,15 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> Transact gas_cost = gas_cost.saturating_add(data_fee); } - caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); + #[cfg(feature = "taiko")] + if !is_anchor { + caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); + } + #[cfg(not(feature = "taiko"))] + { + caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); + } // touch account so we know it is changed. caller_account.mark_touch(); @@ -402,6 +414,18 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, precompiles: Precompiles, ) -> Self { let journaled_state = JournaledState::new(precompiles.len(), GSPEC::SPEC_ID); + + #[cfg(feature = "optimism")] + let handler = if env.cfg.optimism { + Handler::optimism::() + } else { + Handler::mainnet::() + }; + #[cfg(feature = "taiko")] + let handler = Handler::taiko::(); + #[cfg(not(any(feature = "optimism", feature = "taiko")))] + let handler = Handler::mainnet::(); + Self { data: EVMData { env, @@ -413,7 +437,7 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, l1_block_info: None, }, inspector, - handler: Handler::mainnet::(), + handler, _phantomdata: PhantomData {}, } } diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index 9a5e2f5b2f..cdff4f69d0 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -1,6 +1,8 @@ pub mod mainnet; #[cfg(feature = "optimism")] pub mod optimism; +#[cfg(feature = "taiko")] +pub mod taiko; use revm_interpreter::primitives::db::Database; use revm_interpreter::primitives::{EVMError, EVMResultGeneric}; @@ -45,6 +47,17 @@ impl Handler { } } + /// Handler for the taiko + #[cfg(feature = "taiko")] + pub fn taiko() -> Self { + Self { + call_return: taiko::handle_call_return::, + calculate_gas_refund: taiko::calculate_gas_refund::, + reimburse_caller: taiko::handle_reimburse_caller::, + reward_beneficiary: taiko::reward_beneficiary::, + } + } + /// Handler for the optimism #[cfg(feature = "optimism")] pub fn optimism() -> Self { diff --git a/crates/revm/src/handler/taiko.rs b/crates/revm/src/handler/taiko.rs new file mode 100644 index 0000000000..1caa7cfa78 --- /dev/null +++ b/crates/revm/src/handler/taiko.rs @@ -0,0 +1,175 @@ +//! Mainnet related handlers. +use revm_interpreter::primitives::EVMError; + +use crate::{ + interpreter::{return_ok, return_revert, Gas, InstructionResult}, + primitives::{db::Database, Env, Spec, SpecId::LONDON, U256}, + EVMData, +}; + +/// Handle output of the transaction +pub fn handle_call_return( + env: &Env, + call_result: InstructionResult, + returned_gas: Gas, +) -> Gas { + let tx_gas_limit = env.tx.gas_limit; + // Spend the gas limit. Gas is reimbursed when the tx returns successfully. + let mut gas = Gas::new(tx_gas_limit); + if env.is_anchor() { + return gas; + } + gas.record_cost(tx_gas_limit); + + match call_result { + return_ok!() => { + gas.erase_cost(returned_gas.remaining()); + gas.record_refund(returned_gas.refunded()); + } + return_revert!() => { + gas.erase_cost(returned_gas.remaining()); + } + _ => {} + } + gas +} + +#[inline] +pub fn handle_reimburse_caller( + data: &mut EVMData<'_, DB>, + gas: &Gas, + gas_refund: u64, +) -> Result<(), EVMError> { + let _ = data; + if data.env.is_anchor() { + return Ok(()); + } + let caller = data.env.tx.caller; + let effective_gas_price = data.env.effective_gas_price(); + + // return balance of not spend gas. + let (caller_account, _) = data + .journaled_state + .load_account(caller, data.db) + .map_err(EVMError::Database)?; + + caller_account.info.balance = caller_account + .info + .balance + .saturating_add(effective_gas_price * U256::from(gas.remaining() + gas_refund)); + + Ok(()) +} + +/// Reward beneficiary with gas fee. +#[inline] +pub fn reward_beneficiary( + data: &mut EVMData<'_, DB>, + gas: &Gas, + gas_refund: u64, +) -> Result<(), EVMError> { + if data.env.is_anchor() { + return Ok(()); + } + let beneficiary = data.env.block.coinbase; + let effective_gas_price = data.env.effective_gas_price(); + + // transfer fee to coinbase/beneficiary. + // EIP-1559 discard basefee for coinbase transfer. Basefee amount of gas is discarded. + let coinbase_gas_price = if SPEC::enabled(LONDON) { + effective_gas_price.saturating_sub(data.env.block.basefee) + } else { + effective_gas_price + }; + + let (coinbase_account, _) = data + .journaled_state + .load_account(beneficiary, data.db) + .map_err(EVMError::Database)?; + + coinbase_account.mark_touch(); + coinbase_account.info.balance = coinbase_account + .info + .balance + .saturating_add(coinbase_gas_price * U256::from(gas.spend() - gas_refund)); + + let treasury = *crate::primitives::anchor::TREASURY; + let basefee = data.env.block.basefee; + + let (treasury_account, _) = data + .journaled_state + .load_account(treasury, data.db) + .map_err(EVMError::Database)?; + + treasury_account.mark_touch(); + treasury_account.info.balance = treasury_account + .info + .balance + .saturating_add(basefee * U256::from(gas.spend() - gas_refund)); + Ok(()) +} + +/// Calculate gas refund for transaction. +/// +/// If config is set to disable gas refund, it will return 0. +/// +/// If spec is set to london, it will decrease the maximum refund amount to 5th part of +/// gas spend. (Before london it was 2th part of gas spend) +#[inline] +pub fn calculate_gas_refund(env: &Env, gas: &Gas) -> u64 { + if env.cfg.is_gas_refund_disabled() { + 0 + } else { + // EIP-3529: Reduction in refunds + let max_refund_quotient = if SPEC::enabled(LONDON) { 5 } else { 2 }; + (gas.refunded() as u64).min(gas.spend() / max_refund_quotient) + } +} + +#[cfg(test)] +mod tests { + use revm_interpreter::primitives::CancunSpec; + + use super::*; + + #[test] + fn test_consume_gas() { + let mut env = Env::default(); + env.tx.gas_limit = 100; + + let gas = handle_call_return::(&env, InstructionResult::Stop, Gas::new(90)); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spend(), 10); + assert_eq!(gas.refunded(), 0); + } + + #[test] + fn test_consume_gas_with_refund() { + let mut env = Env::default(); + env.tx.gas_limit = 100; + + let mut return_gas = Gas::new(90); + return_gas.record_refund(30); + + let gas = handle_call_return::(&env, InstructionResult::Stop, return_gas); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spend(), 10); + assert_eq!(gas.refunded(), 30); + + let gas = handle_call_return::(&env, InstructionResult::Revert, return_gas); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spend(), 10); + assert_eq!(gas.refunded(), 0); + } + + #[test] + fn test_revert_gas() { + let mut env = Env::default(); + env.tx.gas_limit = 100; + + let gas = handle_call_return::(&env, InstructionResult::Revert, Gas::new(90)); + assert_eq!(gas.remaining(), 90); + assert_eq!(gas.spend(), 10); + assert_eq!(gas.refunded(), 0); + } +} From 0e7d7792b85a9ea1312ce1e5e8db41acf83bcf0c Mon Sep 17 00:00:00 2001 From: john xu Date: Thu, 19 Oct 2023 12:28:10 +0000 Subject: [PATCH 2/6] feat: anchor check in zeth --- Cargo.lock | 1 + crates/primitives/Cargo.toml | 2 +- crates/primitives/src/env.rs | 17 ----------------- crates/primitives/src/lib.rs | 4 ---- crates/primitives/src/taiko.rs | 5 ----- crates/primitives/src/taiko/anchor.rs | 27 --------------------------- crates/primitives/src/taiko/env.rs | 15 --------------- crates/revm/Cargo.toml | 3 ++- crates/revm/src/evm_impl.rs | 3 --- crates/revm/src/handler/taiko.rs | 11 +++++++++-- 10 files changed, 13 insertions(+), 75 deletions(-) delete mode 100644 crates/primitives/src/taiko.rs delete mode 100644 crates/primitives/src/taiko/anchor.rs delete mode 100644 crates/primitives/src/taiko/env.rs diff --git a/Cargo.lock b/Cargo.lock index 6d8a6bb64b..0b0138a65d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2362,6 +2362,7 @@ dependencies = [ "ethers-core", "ethers-providers", "futures", + "once_cell", "revm-interpreter", "revm-precompile", "serde", diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 1b27147d56..4e4b700be7 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -47,7 +47,7 @@ serde = [ arbitrary = ["std", "alloy-primitives/arbitrary", "bitflags/arbitrary"] optimism = [] -taiko = ["dep:once_cell"] +taiko = [] dev = [ "memory_limit", diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs index 11634bc4fd..853b289191 100644 --- a/crates/primitives/src/env.rs +++ b/crates/primitives/src/env.rs @@ -3,8 +3,6 @@ use crate::{ InvalidTransaction, Spec, SpecId, B256, GAS_PER_BLOB, KECCAK_EMPTY, MAX_BLOB_NUMBER_PER_BLOCK, MAX_INITCODE_SIZE, U256, VERSIONED_HASH_VERSION_KZG, }; -#[cfg(feature = "taiko")] -use crate::{EVMError, TaikoEnv, TxType}; use core::cmp::{min, Ordering}; #[derive(Clone, Debug, Default, PartialEq, Eq)] @@ -13,20 +11,10 @@ pub struct Env { pub cfg: CfgEnv, pub block: BlockEnv, pub tx: TxEnv, - #[cfg(feature = "taiko")] - /// Configuration of the taiko - pub taiko: TaikoEnv, } #[cfg(feature = "taiko")] impl Env { - pub fn pre_check(&self) -> Result<(), EVMError> { - if !crate::anchor::validate(self) { - return Err(InvalidTransaction::InvalidAnchorTransaction.into()); - } - Ok(()) - } - pub fn is_anchor(&self) -> bool { self.tx.index == 0 } @@ -151,9 +139,6 @@ pub struct TxEnv { /// The index of the transaction in the block. #[cfg(feature = "taiko")] pub index: usize, - /// The type of the transaction. - #[cfg(feature = "taiko")] - pub tx_type: TxType, /// Caller aka Author aka transaction signer. pub caller: Address, @@ -456,8 +441,6 @@ impl Default for TxEnv { Self { #[cfg(feature = "taiko")] index: 0, - #[cfg(feature = "taiko")] - tx_type: TxType::Legacy, caller: Address::ZERO, gas_limit: u64::MAX, diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 7590cdda81..00cda698d4 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -14,8 +14,6 @@ pub mod precompile; pub mod result; pub mod specification; pub mod state; -#[cfg(feature = "taiko")] -pub mod taiko; pub mod utilities; pub use alloy_primitives::{ @@ -34,6 +32,4 @@ pub use precompile::*; pub use result::*; pub use specification::*; pub use state::*; -#[cfg(feature = "taiko")] -pub use taiko::*; pub use utilities::*; diff --git a/crates/primitives/src/taiko.rs b/crates/primitives/src/taiko.rs deleted file mode 100644 index d9c7e06738..0000000000 --- a/crates/primitives/src/taiko.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod anchor; -pub mod env; - -pub use anchor::*; -pub use env::*; diff --git a/crates/primitives/src/taiko/anchor.rs b/crates/primitives/src/taiko/anchor.rs deleted file mode 100644 index c63c1dac70..0000000000 --- a/crates/primitives/src/taiko/anchor.rs +++ /dev/null @@ -1,27 +0,0 @@ -use super::env::TxType; -use crate::{Address, Env, TransactTo, U256}; -use once_cell::sync::Lazy; -use std::str::FromStr; - -const ANCHOR_SELECTOR: u32 = 0xda69d3db; -const ANCHOR_GAS_LIMIT: u64 = 180_000; -static GOLDEN_TOUCH_ACCOUNT: Lazy
= Lazy::new(|| { - Address::from_str("0x0000777735367b36bC9B61C50022d9D0700dB4Ec") - .expect("invalid golden touch account") -}); - -pub static TREASURY: Lazy
= Lazy::new(|| { - Address::from_str("0xdf09A0afD09a63fb04ab3573922437e1e637dE8b") - .expect("invalid treasury account") -}); - -pub(crate) fn validate(env: &Env) -> bool { - !env.is_anchor() - || (env.tx.tx_type == TxType::Eip1559 - && env.tx.transact_to == TransactTo::Call(env.taiko.l2_address) - && u32::from_be_bytes(env.tx.data[..4].try_into().unwrap()) == ANCHOR_SELECTOR - && env.tx.value == U256::ZERO - && env.tx.gas_limit == ANCHOR_GAS_LIMIT - && env.tx.gas_price == env.block.basefee - && env.tx.caller == *GOLDEN_TOUCH_ACCOUNT) -} diff --git a/crates/primitives/src/taiko/env.rs b/crates/primitives/src/taiko/env.rs deleted file mode 100644 index b46879fd9d..0000000000 --- a/crates/primitives/src/taiko/env.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::Address; - -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct TaikoEnv { - pub l2_address: Address, -} - -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum TxType { - Legacy, - Eip2930, - Eip1559, -} diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index b70ed60f00..e8f4c43b3d 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -28,6 +28,7 @@ tokio = { version = "1.32", features = [ ethers-providers = { version = "2.0", optional = true } ethers-core = { version = "2.0", optional = true } futures = { version = "0.3.27", optional = true } +once_cell = { version = "1.18", default-features = false, optional = true } [dev-dependencies] ethers-contract = { version = "2.0.10", default-features = false } @@ -41,7 +42,7 @@ serde = ["dep:serde", "dep:serde_json", "revm-interpreter/serde"] arbitrary = ["revm-interpreter/arbitrary"] optimism = ["revm-interpreter/optimism", "revm-precompile/optimism"] -taiko = ["revm-interpreter/taiko", "revm-precompile/taiko"] +taiko = ["revm-interpreter/taiko", "revm-precompile/taiko", "dep:once_cell"] ethersdb = ["std", "tokio", "futures", "ethers-providers", "ethers-core"] diff --git a/crates/revm/src/evm_impl.rs b/crates/revm/src/evm_impl.rs index 69c6e106c8..6dfdc995d2 100644 --- a/crates/revm/src/evm_impl.rs +++ b/crates/revm/src/evm_impl.rs @@ -158,9 +158,6 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> Transact fn preverify_transaction(&mut self) -> Result<(), EVMError> { let env = self.env(); - #[cfg(feature = "taiko")] - env.pre_check()?; - // Important: validate block before tx. env.validate_block_env::()?; env.validate_tx::()?; diff --git a/crates/revm/src/handler/taiko.rs b/crates/revm/src/handler/taiko.rs index 1caa7cfa78..86ac78f8ea 100644 --- a/crates/revm/src/handler/taiko.rs +++ b/crates/revm/src/handler/taiko.rs @@ -1,11 +1,18 @@ //! Mainnet related handlers. +use core::str::FromStr; use revm_interpreter::primitives::EVMError; use crate::{ interpreter::{return_ok, return_revert, Gas, InstructionResult}, - primitives::{db::Database, Env, Spec, SpecId::LONDON, U256}, + primitives::{db::Database, Address, Env, Spec, SpecId::LONDON, U256}, EVMData, }; +use once_cell::sync::Lazy; + +static TREASURY: Lazy
= Lazy::new(|| { + Address::from_str("0xdf09A0afD09a63fb04ab3573922437e1e637dE8b") + .expect("invalid treasury account") +}); /// Handle output of the transaction pub fn handle_call_return( @@ -93,7 +100,7 @@ pub fn reward_beneficiary( .balance .saturating_add(coinbase_gas_price * U256::from(gas.spend() - gas_refund)); - let treasury = *crate::primitives::anchor::TREASURY; + let treasury = *TREASURY; let basefee = data.env.block.basefee; let (treasury_account, _) = data From 6c6b061ca06460012bbf53af52d113b8d9da4ec1 Mon Sep 17 00:00:00 2001 From: john xu Date: Sat, 9 Dec 2023 07:13:20 +0000 Subject: [PATCH 3/6] Add taiko module and handler --- crates/primitives/src/env.rs | 25 ++++++++----------- crates/primitives/src/lib.rs | 2 ++ crates/primitives/src/taiko/env.rs | 8 ++++++ crates/primitives/src/taiko/mod.rs | 1 + crates/revm/src/evm_impl.rs | 2 +- crates/revm/src/handler.rs | 11 ++++---- crates/revm/src/lib.rs | 3 +++ .../{handler/taiko.rs => taiko/handler.rs} | 17 ++++--------- crates/revm/src/taiko/mod.rs | 1 + 9 files changed, 36 insertions(+), 34 deletions(-) create mode 100644 crates/primitives/src/taiko/env.rs create mode 100644 crates/primitives/src/taiko/mod.rs rename crates/revm/src/{handler/taiko.rs => taiko/handler.rs} (92%) create mode 100644 crates/revm/src/taiko/mod.rs diff --git a/crates/primitives/src/env.rs b/crates/primitives/src/env.rs index 853b289191..862da8a46f 100644 --- a/crates/primitives/src/env.rs +++ b/crates/primitives/src/env.rs @@ -5,6 +5,9 @@ use crate::{ }; use core::cmp::{min, Ordering}; +#[cfg(feature = "taiko")] +use crate::taiko::env::TaikoFields; + #[derive(Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Env { @@ -13,13 +16,6 @@ pub struct Env { pub tx: TxEnv, } -#[cfg(feature = "taiko")] -impl Env { - pub fn is_anchor(&self) -> bool { - self.tx.index == 0 - } -} - /// The block environment. #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -136,10 +132,6 @@ impl BlockEnv { #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct TxEnv { - /// The index of the transaction in the block. - #[cfg(feature = "taiko")] - pub index: usize, - /// Caller aka Author aka transaction signer. pub caller: Address, /// The gas limit of the transaction. @@ -194,6 +186,10 @@ pub struct TxEnv { #[cfg_attr(feature = "serde", serde(flatten))] #[cfg(feature = "optimism")] pub optimism: OptimismFields, + + #[cfg_attr(feature = "serde", serde(flatten))] + #[cfg(feature = "taiko")] + pub taiko: TaikoFields, } impl TxEnv { @@ -439,9 +435,6 @@ impl Default for BlockEnv { impl Default for TxEnv { fn default() -> Self { Self { - #[cfg(feature = "taiko")] - index: 0, - caller: Address::ZERO, gas_limit: u64::MAX, gas_price: U256::ZERO, @@ -456,6 +449,8 @@ impl Default for TxEnv { max_fee_per_blob_gas: None, #[cfg(feature = "optimism")] optimism: OptimismFields::default(), + #[cfg(feature = "taiko")] + taiko: TaikoFields::default(), } } } @@ -663,7 +658,7 @@ impl Env { } #[cfg(feature = "taiko")] - if self.is_anchor() { + if self.tx.taiko.is_anchor { return Ok(()); } diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 00cda698d4..644a19c0ee 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -14,6 +14,8 @@ pub mod precompile; pub mod result; pub mod specification; pub mod state; +#[cfg(feature = "taiko")] +pub mod taiko; pub mod utilities; pub use alloy_primitives::{ diff --git a/crates/primitives/src/taiko/env.rs b/crates/primitives/src/taiko/env.rs new file mode 100644 index 0000000000..df3bb55128 --- /dev/null +++ b/crates/primitives/src/taiko/env.rs @@ -0,0 +1,8 @@ +use crate::Address; + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TaikoFields { + pub treasury: Address, + pub is_anchor: bool, +} diff --git a/crates/primitives/src/taiko/mod.rs b/crates/primitives/src/taiko/mod.rs new file mode 100644 index 0000000000..3d7924f6ab --- /dev/null +++ b/crates/primitives/src/taiko/mod.rs @@ -0,0 +1 @@ +pub mod env; diff --git a/crates/revm/src/evm_impl.rs b/crates/revm/src/evm_impl.rs index 6dfdc995d2..a062613e75 100644 --- a/crates/revm/src/evm_impl.rs +++ b/crates/revm/src/evm_impl.rs @@ -194,7 +194,7 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> Transact let tx_data = env.tx.data.clone(); let tx_gas_limit = env.tx.gas_limit; #[cfg(feature = "taiko")] - let is_anchor = env.is_anchor(); + let is_anchor = env.tx.taiko.is_anchor; #[cfg(feature = "optimism")] let tx_l1_cost = { diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index cdff4f69d0..ba347f852f 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -1,8 +1,6 @@ pub mod mainnet; #[cfg(feature = "optimism")] pub mod optimism; -#[cfg(feature = "taiko")] -pub mod taiko; use revm_interpreter::primitives::db::Database; use revm_interpreter::primitives::{EVMError, EVMResultGeneric}; @@ -50,11 +48,12 @@ impl Handler { /// Handler for the taiko #[cfg(feature = "taiko")] pub fn taiko() -> Self { + use crate::taiko::handler; Self { - call_return: taiko::handle_call_return::, - calculate_gas_refund: taiko::calculate_gas_refund::, - reimburse_caller: taiko::handle_reimburse_caller::, - reward_beneficiary: taiko::reward_beneficiary::, + call_return: handler::handle_call_return::, + calculate_gas_refund: handler::calculate_gas_refund::, + reimburse_caller: handler::handle_reimburse_caller::, + reward_beneficiary: handler::reward_beneficiary::, } } diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index fbaeab6c18..61c7dac1fb 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -15,6 +15,9 @@ mod journaled_state; #[cfg(feature = "optimism")] pub mod optimism; +#[cfg(feature = "taiko")] +pub mod taiko; + #[cfg(all(feature = "with-serde", not(feature = "serde")))] compile_error!("`with-serde` feature has been renamed to `serde`."); diff --git a/crates/revm/src/handler/taiko.rs b/crates/revm/src/taiko/handler.rs similarity index 92% rename from crates/revm/src/handler/taiko.rs rename to crates/revm/src/taiko/handler.rs index 86ac78f8ea..311c8eed45 100644 --- a/crates/revm/src/handler/taiko.rs +++ b/crates/revm/src/taiko/handler.rs @@ -1,18 +1,11 @@ //! Mainnet related handlers. -use core::str::FromStr; use revm_interpreter::primitives::EVMError; use crate::{ interpreter::{return_ok, return_revert, Gas, InstructionResult}, - primitives::{db::Database, Address, Env, Spec, SpecId::LONDON, U256}, + primitives::{db::Database, Env, Spec, SpecId::LONDON, U256}, EVMData, }; -use once_cell::sync::Lazy; - -static TREASURY: Lazy
= Lazy::new(|| { - Address::from_str("0xdf09A0afD09a63fb04ab3573922437e1e637dE8b") - .expect("invalid treasury account") -}); /// Handle output of the transaction pub fn handle_call_return( @@ -23,7 +16,7 @@ pub fn handle_call_return( let tx_gas_limit = env.tx.gas_limit; // Spend the gas limit. Gas is reimbursed when the tx returns successfully. let mut gas = Gas::new(tx_gas_limit); - if env.is_anchor() { + if env.tx.taiko.is_anchor { return gas; } gas.record_cost(tx_gas_limit); @@ -48,7 +41,7 @@ pub fn handle_reimburse_caller( gas_refund: u64, ) -> Result<(), EVMError> { let _ = data; - if data.env.is_anchor() { + if data.env.tx.taiko.is_anchor { return Ok(()); } let caller = data.env.tx.caller; @@ -75,7 +68,7 @@ pub fn reward_beneficiary( gas: &Gas, gas_refund: u64, ) -> Result<(), EVMError> { - if data.env.is_anchor() { + if data.env.tx.taiko.is_anchor { return Ok(()); } let beneficiary = data.env.block.coinbase; @@ -100,7 +93,7 @@ pub fn reward_beneficiary( .balance .saturating_add(coinbase_gas_price * U256::from(gas.spend() - gas_refund)); - let treasury = *TREASURY; + let treasury = data.env.tx.taiko.treasury; let basefee = data.env.block.basefee; let (treasury_account, _) = data diff --git a/crates/revm/src/taiko/mod.rs b/crates/revm/src/taiko/mod.rs new file mode 100644 index 0000000000..062ae9d9be --- /dev/null +++ b/crates/revm/src/taiko/mod.rs @@ -0,0 +1 @@ +pub mod handler; From 13c2272bf4ecce03529fb6545767064d1a7a2754 Mon Sep 17 00:00:00 2001 From: john xu Date: Sat, 9 Dec 2023 07:21:01 +0000 Subject: [PATCH 4/6] Remove once_cell dependency --- Cargo.lock | 1 - crates/revm/Cargo.toml | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0b0138a65d..6d8a6bb64b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2362,7 +2362,6 @@ dependencies = [ "ethers-core", "ethers-providers", "futures", - "once_cell", "revm-interpreter", "revm-precompile", "serde", diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index e8f4c43b3d..b70ed60f00 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -28,7 +28,6 @@ tokio = { version = "1.32", features = [ ethers-providers = { version = "2.0", optional = true } ethers-core = { version = "2.0", optional = true } futures = { version = "0.3.27", optional = true } -once_cell = { version = "1.18", default-features = false, optional = true } [dev-dependencies] ethers-contract = { version = "2.0.10", default-features = false } @@ -42,7 +41,7 @@ serde = ["dep:serde", "dep:serde_json", "revm-interpreter/serde"] arbitrary = ["revm-interpreter/arbitrary"] optimism = ["revm-interpreter/optimism", "revm-precompile/optimism"] -taiko = ["revm-interpreter/taiko", "revm-precompile/taiko", "dep:once_cell"] +taiko = ["revm-interpreter/taiko", "revm-precompile/taiko"] ethersdb = ["std", "tokio", "futures", "ethers-providers", "ethers-core"] From 2e961443c60eae354a078a3c82d22af6d530035d Mon Sep 17 00:00:00 2001 From: john xu Date: Sat, 9 Dec 2023 07:59:10 +0000 Subject: [PATCH 5/6] Add condition to disable gas refund for anchor transactions --- crates/revm/src/taiko/handler.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/revm/src/taiko/handler.rs b/crates/revm/src/taiko/handler.rs index 311c8eed45..9bc0988e74 100644 --- a/crates/revm/src/taiko/handler.rs +++ b/crates/revm/src/taiko/handler.rs @@ -117,7 +117,8 @@ pub fn reward_beneficiary( /// gas spend. (Before london it was 2th part of gas spend) #[inline] pub fn calculate_gas_refund(env: &Env, gas: &Gas) -> u64 { - if env.cfg.is_gas_refund_disabled() { + // no refund for anchor tx and gas refund disabled. + if env.tx.taiko.is_anchor || env.cfg.is_gas_refund_disabled() { 0 } else { // EIP-3529: Reduction in refunds From 327dc05411c59db6520ba3782a4d1cdab992f479 Mon Sep 17 00:00:00 2001 From: john xu Date: Mon, 18 Dec 2023 06:58:08 +0000 Subject: [PATCH 6/6] Refactor handler functions in EVM implementation --- crates/revm/src/evm_impl.rs | 2 +- crates/revm/src/handler.rs | 4 +- crates/revm/src/taiko/handler.rs | 93 -------------------------------- 3 files changed, 3 insertions(+), 96 deletions(-) diff --git a/crates/revm/src/evm_impl.rs b/crates/revm/src/evm_impl.rs index a062613e75..c325ff8b58 100644 --- a/crates/revm/src/evm_impl.rs +++ b/crates/revm/src/evm_impl.rs @@ -413,7 +413,7 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, let journaled_state = JournaledState::new(precompiles.len(), GSPEC::SPEC_ID); #[cfg(feature = "optimism")] - let handler = if env.cfg.optimism { + let handler: Handler = if env.cfg.optimism { Handler::optimism::() } else { Handler::mainnet::() diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs index ba347f852f..10b3c5dc9c 100644 --- a/crates/revm/src/handler.rs +++ b/crates/revm/src/handler.rs @@ -50,8 +50,8 @@ impl Handler { pub fn taiko() -> Self { use crate::taiko::handler; Self { - call_return: handler::handle_call_return::, - calculate_gas_refund: handler::calculate_gas_refund::, + call_return: mainnet::handle_call_return::, + calculate_gas_refund: mainnet::calculate_gas_refund::, reimburse_caller: handler::handle_reimburse_caller::, reward_beneficiary: handler::reward_beneficiary::, } diff --git a/crates/revm/src/taiko/handler.rs b/crates/revm/src/taiko/handler.rs index 9bc0988e74..97218e50f2 100644 --- a/crates/revm/src/taiko/handler.rs +++ b/crates/revm/src/taiko/handler.rs @@ -7,33 +7,6 @@ use crate::{ EVMData, }; -/// Handle output of the transaction -pub fn handle_call_return( - env: &Env, - call_result: InstructionResult, - returned_gas: Gas, -) -> Gas { - let tx_gas_limit = env.tx.gas_limit; - // Spend the gas limit. Gas is reimbursed when the tx returns successfully. - let mut gas = Gas::new(tx_gas_limit); - if env.tx.taiko.is_anchor { - return gas; - } - gas.record_cost(tx_gas_limit); - - match call_result { - return_ok!() => { - gas.erase_cost(returned_gas.remaining()); - gas.record_refund(returned_gas.refunded()); - } - return_revert!() => { - gas.erase_cost(returned_gas.remaining()); - } - _ => {} - } - gas -} - #[inline] pub fn handle_reimburse_caller( data: &mut EVMData<'_, DB>, @@ -108,69 +81,3 @@ pub fn reward_beneficiary( .saturating_add(basefee * U256::from(gas.spend() - gas_refund)); Ok(()) } - -/// Calculate gas refund for transaction. -/// -/// If config is set to disable gas refund, it will return 0. -/// -/// If spec is set to london, it will decrease the maximum refund amount to 5th part of -/// gas spend. (Before london it was 2th part of gas spend) -#[inline] -pub fn calculate_gas_refund(env: &Env, gas: &Gas) -> u64 { - // no refund for anchor tx and gas refund disabled. - if env.tx.taiko.is_anchor || env.cfg.is_gas_refund_disabled() { - 0 - } else { - // EIP-3529: Reduction in refunds - let max_refund_quotient = if SPEC::enabled(LONDON) { 5 } else { 2 }; - (gas.refunded() as u64).min(gas.spend() / max_refund_quotient) - } -} - -#[cfg(test)] -mod tests { - use revm_interpreter::primitives::CancunSpec; - - use super::*; - - #[test] - fn test_consume_gas() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - - let gas = handle_call_return::(&env, InstructionResult::Stop, Gas::new(90)); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_consume_gas_with_refund() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - - let mut return_gas = Gas::new(90); - return_gas.record_refund(30); - - let gas = handle_call_return::(&env, InstructionResult::Stop, return_gas); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 30); - - let gas = handle_call_return::(&env, InstructionResult::Revert, return_gas); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_revert_gas() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - - let gas = handle_call_return::(&env, InstructionResult::Revert, Gas::new(90)); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } -}