diff --git a/Cargo.lock b/Cargo.lock index 1ef2f79..97d7f8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1793,7 +1793,7 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "revm" version = "24.0.0" -source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#28f83c2759ac9d2d23969a931cf308b375bb6bd4" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "revm-bytecode", "revm-context", @@ -1811,7 +1811,7 @@ dependencies = [ [[package]] name = "revm-bytecode" version = "4.0.1" -source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#28f83c2759ac9d2d23969a931cf308b375bb6bd4" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "bitvec", "once_cell", @@ -1823,7 +1823,7 @@ dependencies = [ [[package]] name = "revm-context" version = "5.0.0" -source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#28f83c2759ac9d2d23969a931cf308b375bb6bd4" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "cfg-if", "derive-where", @@ -1838,7 +1838,7 @@ dependencies = [ [[package]] name = "revm-context-interface" version = "5.0.0" -source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#28f83c2759ac9d2d23969a931cf308b375bb6bd4" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -1853,7 +1853,7 @@ dependencies = [ [[package]] name = "revm-database" version = "4.0.1" -source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#28f83c2759ac9d2d23969a931cf308b375bb6bd4" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "alloy-eips", "revm-bytecode", @@ -1866,7 +1866,7 @@ dependencies = [ [[package]] name = "revm-database-interface" version = "4.0.1" -source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#28f83c2759ac9d2d23969a931cf308b375bb6bd4" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "auto_impl", "revm-primitives", @@ -1877,7 +1877,7 @@ dependencies = [ [[package]] name = "revm-handler" version = "5.0.0" -source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#28f83c2759ac9d2d23969a931cf308b375bb6bd4" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "auto_impl", "revm-bytecode", @@ -1894,7 +1894,7 @@ dependencies = [ [[package]] name = "revm-inspector" version = "5.0.0" -source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#28f83c2759ac9d2d23969a931cf308b375bb6bd4" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "auto_impl", "revm-context", @@ -1910,7 +1910,7 @@ dependencies = [ [[package]] name = "revm-interpreter" version = "20.0.0" -source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#28f83c2759ac9d2d23969a931cf308b375bb6bd4" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "revm-bytecode", "revm-context-interface", @@ -1921,7 +1921,7 @@ dependencies = [ [[package]] name = "revm-precompile" version = "21.0.0" -source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#28f83c2759ac9d2d23969a931cf308b375bb6bd4" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -1946,7 +1946,7 @@ dependencies = [ [[package]] name = "revm-primitives" version = "19.1.0" -source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#28f83c2759ac9d2d23969a931cf308b375bb6bd4" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "alloy-primitives", "num_enum", @@ -1969,7 +1969,7 @@ dependencies = [ [[package]] name = "revm-state" version = "4.0.1" -source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#28f83c2759ac9d2d23969a931cf308b375bb6bd4" +source = "git+https://github.com/scroll-tech/revm?branch=feat%2Freth-v74#774616019e9562b12cbe1c3f1cdd110793f8084c" dependencies = [ "bitflags", "revm-bytecode", diff --git a/Cargo.toml b/Cargo.toml index d8da513..7685e86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # revm -revm = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74", default-features = false, features = ["secp256r1", "enable_eip7702"] } +revm = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74", default-features = false, features = ["secp256r1", "enable_eip7702", "enable_eip7623"] } revm-primitives = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74", default-features = false } revm-inspector = { git = "https://github.com/scroll-tech/revm", branch = "feat/reth-v74", default-features = false } @@ -23,6 +23,8 @@ hashbrown = ["revm/hashbrown"] serde = ["dep:serde", "revm/serde"] portable = ["revm/portable"] +test-utils = [] + # See comments in `revm-precompile` secp256k1 = ["revm/secp256k1"] c-kzg = ["revm/c-kzg"] diff --git a/src/builder.rs b/src/builder.rs index 5c619a5..f1d26fd 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -59,6 +59,7 @@ impl DefaultScrollContext for ScrollContext { let spec = ScrollSpecId::default(); let mut cfg = CfgEnv::new_with_spec(spec); cfg.enable_eip7702 = spec >= ScrollSpecId::EUCLID; + cfg.enable_eip7623 = spec >= ScrollSpecId::FEYNMAN; Context::mainnet() .with_tx(ScrollTransaction::default()) @@ -67,18 +68,31 @@ impl DefaultScrollContext for ScrollContext { } } -/// Activates EIP-7702 if necessary for the context. -pub trait MaybeWithEip7702 { - /// Activates EIP-7702 if necessary. +/// Activates specific EIP's for Euclid. +pub trait EuclidEipActivations { + /// Activates EIP-7702 if the spec is at least at Euclid. fn maybe_with_eip_7702(self) -> Self; } -impl MaybeWithEip7702 for ScrollContext { +/// Activates specific EIP's for Feynman. +pub trait FeynmanEipActivations: EuclidEipActivations { + /// Activates EIP-7623 if the spec is at least at Feynman. + fn maybe_with_eip_7623(self) -> Self; +} + +impl EuclidEipActivations for ScrollContext { fn maybe_with_eip_7702(mut self) -> Self { self.cfg.enable_eip7702 = self.cfg.spec >= ScrollSpecId::EUCLID; self } } +impl FeynmanEipActivations for ScrollContext { + fn maybe_with_eip_7623(mut self) -> Self { + self.cfg.enable_eip7623 = self.cfg.spec >= ScrollSpecId::FEYNMAN; + self + } +} + pub type ScrollContext = Context, CfgEnv, DB, Journal, L1BlockInfo>; diff --git a/src/handler.rs b/src/handler.rs index f7dbe55..92233fc 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -5,7 +5,7 @@ use std::{boxed::Box, string::ToString}; use revm::{ context::{ - result::{EVMError, HaltReason, InvalidTransaction}, + result::{HaltReason, InvalidTransaction}, Block, Cfg, ContextTr, JournalTr, Transaction, }, handler::{ @@ -34,19 +34,6 @@ impl Default for ScrollHandler { } } -pub trait IsLackOfFundForMaxFeeError { - fn is_lack_of_funds_for_max_fee_error(&self) -> bool; -} - -impl IsLackOfFundForMaxFeeError for EVMError { - fn is_lack_of_funds_for_max_fee_error(&self) -> bool { - if let EVMError::Transaction(InvalidTransaction::LackOfFundForMaxFee { .. }) = self { - return true; - } - false - } -} - /// Configure the handler for the Scroll chain. /// /// The trait modifies the following handlers: @@ -62,7 +49,7 @@ impl IsLackOfFundForMaxFeeError for EVMError { impl Handler for ScrollHandler where EVM: EvmTr, - ERROR: EvmTrError + IsLackOfFundForMaxFeeError + From, + ERROR: EvmTrError + From, FRAME: Frame, { type Evm = EVM; @@ -249,7 +236,7 @@ where Context: ScrollContextTr, Inspector: Inspector<<::Evm as EvmTr>::Context, EthInterpreter>, >, - ERROR: EvmTrError + IsLackOfFundForMaxFeeError, + ERROR: EvmTrError, FRAME: InspectorFrame< Evm = EVM, Error = ERROR, @@ -265,68 +252,18 @@ where mod tests { use super::*; use crate::{ - builder::{DefaultScrollContext, ScrollBuilder, ScrollContext}, - l1block::L1_GAS_PRICE_ORACLE_ADDRESS, - transaction::L1_MESSAGE_TYPE, - ScrollSpecId, + builder::ScrollBuilder, + test_utils::{ + context, context_with_funds, BENEFICIARY, CALLER, L1_DATA_COST, MIN_TRANSACTION_COST, + }, }; - use std::{boxed::Box, vec}; + use std::boxed::Box; use revm::{ - context::{ - either::Either, - transaction::{Authorization, SignedAuthorization}, - TransactionType, - }, - database::{DbAccount, InMemoryDB}, + context::result::EVMError, handler::EthFrame, interpreter::{CallOutcome, InstructionResult, InterpreterResult}, - state::AccountInfo, - Context, }; - use revm_primitives::{address, bytes, eip7702, Address}; - - const TX_L1_FEE_PRECISION: U256 = U256::from_limbs([1_000_000_000u64, 0, 0, 0]); - const CALLER: Address = address!("0x000000000000000000000000000000000000dead"); - const TO: Address = address!("0x0000000000000000000000000000000000000001"); - const BENEFICIARY: Address = address!("0x0000000000000000000000000000000000000002"); - const MIN_TRANSACTION_COST: U256 = U256::from_limbs([21_000u64, 0, 0, 0]); - const L1_DATA_COST: U256 = U256::from_limbs([4u64, 0, 0, 0]); - - fn context() -> ScrollContext { - Context::scroll() - .modify_tx_chained(|tx| { - tx.base.caller = CALLER; - tx.base.kind = Some(TO).into(); - tx.base.gas_price = 1; - tx.base.gas_limit = 21000; - tx.base.gas_priority_fee = None; - tx.rlp_bytes = Some(bytes!("01010101")); - }) - .modify_block_chained(|block| block.beneficiary = BENEFICIARY) - .with_db(InMemoryDB::default()) - .modify_db_chained(|db| { - let _ = db.replace_account_storage( - L1_GAS_PRICE_ORACLE_ADDRESS, - (0..7) - .map(|n| (U256::from(n), U256::from(1))) - .chain(core::iter::once((U256::from(7), TX_L1_FEE_PRECISION))) - .collect(), - ); - }) - } - - fn context_with_funds(funds: U256) -> ScrollContext { - context().modify_db_chained(|db| { - db.cache.accounts.insert( - CALLER, - DbAccount { - info: AccountInfo { balance: funds, ..Default::default() }, - ..Default::default() - }, - ); - }) - } #[test] fn test_validate_lacking_funds() -> Result<(), Box> { @@ -345,75 +282,6 @@ mod tests { Ok(()) } - #[test] - fn test_validate_lacking_funds_l1_message() -> Result<(), Box> { - let ctx = context() - .modify_tx_chained(|tx| tx.base.tx_type = L1_MESSAGE_TYPE) - .modify_cfg_chained(|cfg| cfg.spec = ScrollSpecId::EUCLID); - let mut evm = ctx.build_scroll(); - let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); - handler.validate(&mut evm)?; - - Ok(()) - } - - #[test] - fn test_validate_initial_gas_eip7702() -> Result<(), Box> { - let ctx = context(); - let evm = ctx.clone().build_scroll(); - let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); - let gas_empty_authorization_list = handler.validate_initial_tx_gas(&evm)?; - - let evm = ctx - .modify_tx_chained(|tx| { - tx.base.gas_limit += eip7702::PER_EMPTY_ACCOUNT_COST; - tx.base.authorization_list = vec![Either::Left(SignedAuthorization::new_unchecked( - Authorization { - chain_id: Default::default(), - address: Default::default(), - nonce: 0, - }, - 0, - U256::ZERO, - U256::ZERO, - ))] - }) - .build_scroll(); - let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); - let gas_with_authorization_list = handler.validate_initial_tx_gas(&evm)?; - - assert_eq!( - gas_empty_authorization_list.initial_gas + eip7702::PER_EMPTY_ACCOUNT_COST, - gas_with_authorization_list.initial_gas - ); - - Ok(()) - } - - #[test] - fn test_validate_env_eip7702() -> Result<(), Box> { - let ctx = context() - .modify_tx_chained(|tx| { - tx.base.tx_type = TransactionType::Eip7702 as u8; - tx.base.authorization_list = vec![Either::Left(SignedAuthorization::new_unchecked( - Authorization { - chain_id: Default::default(), - address: Default::default(), - nonce: 0, - }, - 0, - U256::ZERO, - U256::ZERO, - ))] - }) - .modify_cfg_chained(|cfg| cfg.spec = ScrollSpecId::EUCLID); - let mut evm = ctx.build_scroll(); - let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); - handler.validate_env(&mut evm)?; - - Ok(()) - } - #[test] fn test_load_account() -> Result<(), Box> { let ctx = context_with_funds(MIN_TRANSACTION_COST + L1_DATA_COST); @@ -427,33 +295,6 @@ mod tests { Ok(()) } - #[test] - fn test_load_account_l1_message() -> Result<(), Box> { - let ctx = context().modify_tx_chained(|tx| tx.base.tx_type = L1_MESSAGE_TYPE); - let mut evm = ctx.build_scroll(); - let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); - handler.load_accounts(&mut evm)?; - - let l1_block_info = evm.ctx().chain.clone(); - assert_eq!(l1_block_info, L1BlockInfo::default()); - - Ok(()) - } - - #[test] - fn test_apply_eip7702_auth_list() -> Result<(), Box> { - let ctx = context() - .modify_tx_chained(|tx| { - tx.base.tx_type = TransactionType::Eip7702 as u8; - }) - .modify_cfg_chained(|cfg| cfg.spec = ScrollSpecId::EUCLID); - let mut evm = ctx.build_scroll(); - let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); - handler.apply_eip7702_auth_list(&mut evm)?; - - Ok(()) - } - #[test] fn test_deduct_caller() -> Result<(), Box> { let ctx = context_with_funds(MIN_TRANSACTION_COST + L1_DATA_COST); @@ -469,22 +310,6 @@ mod tests { Ok(()) } - #[test] - fn test_deduct_caller_l1_message() -> Result<(), Box> { - let ctx = context().modify_tx_chained(|tx| tx.base.tx_type = L1_MESSAGE_TYPE); - - let mut evm = ctx.build_scroll(); - let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); - handler.load_accounts(&mut evm)?; - handler.validate_against_state_and_deduct_caller(&mut evm)?; - - let caller_account = evm.ctx().journal().load_account(CALLER)?; - assert_eq!(caller_account.info.balance, U256::ZERO); - assert_eq!(caller_account.info.nonce, 1); - - Ok(()) - } - #[test] fn test_last_frame_result() -> Result<(), Box> { let ctx = context(); @@ -509,30 +334,6 @@ mod tests { Ok(()) } - #[test] - fn test_last_frame_result_l1_message() -> Result<(), Box> { - let ctx = context().modify_tx_chained(|tx| tx.base.tx_type = L1_MESSAGE_TYPE); - - let mut evm = ctx.build_scroll(); - let mut handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); - let mut gas = Gas::new(21000); - gas.set_refund(10); - gas.set_spent(10); - let mut result = FrameResult::Call(CallOutcome::new( - InterpreterResult { - result: InstructionResult::Return, - output: Default::default(), - gas, - }, - 0..0, - )); - handler.last_frame_result(&mut evm, &mut result)?; - - gas.set_refund(0); - assert_eq!(result.gas(), &gas); - - Ok(()) - } #[test] fn test_refund() -> Result<(), Box> { let ctx = context(); @@ -558,31 +359,6 @@ mod tests { Ok(()) } - #[test] - fn test_refund_l1_message() -> Result<(), Box> { - let ctx = context().modify_tx_chained(|tx| tx.base.tx_type = L1_MESSAGE_TYPE); - - let mut evm = ctx.build_scroll(); - let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); - let mut gas = Gas::new(21000); - gas.set_refund(10); - gas.set_spent(10); - let mut result = FrameResult::Call(CallOutcome::new( - InterpreterResult { - result: InstructionResult::Return, - output: Default::default(), - gas, - }, - 0..0, - )); - handler.refund(&mut evm, &mut result, 0); - - // gas should not have been updated - assert_eq!(result.gas(), &gas); - - Ok(()) - } - #[test] fn test_reward_beneficiary() -> Result<(), Box> { let ctx = context_with_funds(MIN_TRANSACTION_COST + L1_DATA_COST); @@ -607,30 +383,6 @@ mod tests { Ok(()) } - #[test] - fn test_reward_beneficiary_l1_message() -> Result<(), Box> { - let ctx = context().modify_tx_chained(|tx| tx.base.tx_type = L1_MESSAGE_TYPE); - - let mut evm = ctx.build_scroll(); - let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); - let gas = Gas::new_spent(21000); - let mut result = FrameResult::Call(CallOutcome::new( - InterpreterResult { - result: InstructionResult::Return, - output: Default::default(), - gas, - }, - 0..0, - )); - handler.load_accounts(&mut evm)?; - handler.reward_beneficiary(&mut evm, &mut result)?; - - let beneficiary = evm.ctx().journal().load_account(BENEFICIARY)?; - assert_eq!(beneficiary.info.balance, U256::ZERO); - - Ok(()) - } - #[test] fn test_transaction_pre_execution() -> Result<(), Box> { let ctx = context_with_funds(MIN_TRANSACTION_COST + L1_DATA_COST); diff --git a/src/lib.rs b/src/lib.rs index 068c990..6351939 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,5 +22,11 @@ pub mod precompile; pub use spec::*; mod spec; +#[cfg(test)] +mod tests; + +#[cfg(any(test, feature = "test-utils"))] +pub mod test_utils; + pub use transaction::ScrollTransaction; mod transaction; diff --git a/src/test_utils.rs b/src/test_utils.rs new file mode 100644 index 0000000..6854633 --- /dev/null +++ b/src/test_utils.rs @@ -0,0 +1,56 @@ +use crate::{ + builder::{DefaultScrollContext, ScrollContext}, + l1block::L1_GAS_PRICE_ORACLE_ADDRESS, +}; +use revm::{ + database::{DbAccount, InMemoryDB}, + state::AccountInfo, + Context, +}; +use revm_primitives::{address, bytes, Address, U256}; + +pub const TX_L1_FEE_PRECISION: U256 = U256::from_limbs([1_000_000_000u64, 0, 0, 0]); +pub const CALLER: Address = address!("0x000000000000000000000000000000000000dead"); +pub const TO: Address = address!("0x0000000000000000000000000000000000000001"); +pub const BENEFICIARY: Address = address!("0x0000000000000000000000000000000000000002"); +pub const MIN_TRANSACTION_COST: U256 = U256::from_limbs([21_000u64, 0, 0, 0]); +pub const L1_DATA_COST: U256 = U256::from_limbs([40_000u64, 0, 0, 0]); + +/// Returns a test [`ScrollContext`] which contains a basic transaction, a default block beneficiary +/// and a state with L1 gas oracle slots set. +pub fn context() -> ScrollContext { + Context::scroll() + .modify_tx_chained(|tx| { + tx.base.caller = CALLER; + tx.base.kind = Some(TO).into(); + tx.base.gas_price = 1; + tx.base.gas_limit = 21000; + tx.base.gas_priority_fee = None; + tx.rlp_bytes = Some(bytes!("01010101")); + }) + .modify_block_chained(|block| block.beneficiary = BENEFICIARY) + .with_db(InMemoryDB::default()) + .modify_db_chained(|db| { + let _ = db.replace_account_storage( + L1_GAS_PRICE_ORACLE_ADDRESS, + (0..7) + .map(|n| (U256::from(n), U256::from(10_000))) + .chain(core::iter::once((U256::from(7), TX_L1_FEE_PRECISION))) + .collect(), + ); + }) +} + +/// Returns a test [`ScrollContext`] which contains a basic transaction, a default block beneficiary +/// and a state with L1 gas oracle slots set and the provided funds for the caller. +pub fn context_with_funds(funds: U256) -> ScrollContext { + context().modify_db_chained(|db| { + db.cache.accounts.insert( + CALLER, + DbAccount { + info: AccountInfo { balance: funds, ..Default::default() }, + ..Default::default() + }, + ); + }) +} diff --git a/src/tests/eip7623.rs b/src/tests/eip7623.rs new file mode 100644 index 0000000..d8a1679 --- /dev/null +++ b/src/tests/eip7623.rs @@ -0,0 +1,55 @@ +use crate::{ + builder::{FeynmanEipActivations, ScrollBuilder}, + handler::ScrollHandler, + test_utils::context, + ScrollSpecId, +}; + +use revm::{ + context::result::{EVMError, InvalidTransaction}, + handler::{EthFrame, Handler}, +}; +use revm_primitives::bytes; + +#[test] +fn test_should_not_apply_eip7623_calldata_gas_for_euclid() { + const GAS_LIMIT: u64 = 21_032; + + // initiate handler. + let ctx = context() + .modify_cfg_chained(|cfg| cfg.spec = ScrollSpecId::EUCLID) + .modify_tx_chained(|tx| tx.base.gas_limit = GAS_LIMIT) + .maybe_with_eip_7623(); + let evm = ctx.build_scroll(); + let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); + + // check call passes. + let _ = handler.validate_initial_tx_gas(&evm).unwrap(); +} + +#[test] +fn test_should_apply_eip7623_calldata_gas_for_feynman() { + const GAS_LIMIT: u64 = 21_032; + const GAS_FLOOR: u64 = 21_080; + + // initiate handler. + let ctx = context() + .modify_cfg_chained(|cfg| cfg.spec = ScrollSpecId::FEYNMAN) + .modify_tx_chained(|tx| { + tx.base.gas_limit = GAS_LIMIT; + tx.base.data = bytes!("0xdead"); + }) + .maybe_with_eip_7623(); + let evm = ctx.build_scroll(); + let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); + + // check call errors on gas floor more than gas limit. + let err = handler.validate_initial_tx_gas(&evm).unwrap_err(); + assert_eq!( + err, + EVMError::Transaction(InvalidTransaction::GasFloorMoreThanGasLimit { + gas_limit: GAS_LIMIT, + gas_floor: GAS_FLOOR + }) + ) +} diff --git a/src/tests/eip7702.rs b/src/tests/eip7702.rs new file mode 100644 index 0000000..c84a5af --- /dev/null +++ b/src/tests/eip7702.rs @@ -0,0 +1,67 @@ +use crate::{builder::ScrollBuilder, handler::ScrollHandler, test_utils::context}; +use std::{boxed::Box, vec}; + +use revm::{ + context::{ + either::Either, + result::EVMError, + transaction::{Authorization, SignedAuthorization}, + TransactionType, + }, + handler::{EthFrame, Handler}, +}; +use revm_primitives::{eip7702, U256}; + +#[test] +fn test_validate_initial_gas_eip7702() -> Result<(), Box> { + let ctx = context(); + let evm = ctx.clone().build_scroll(); + let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); + let gas_empty_authorization_list = handler.validate_initial_tx_gas(&evm)?; + + let evm = ctx + .modify_tx_chained(|tx| { + tx.base.gas_limit += eip7702::PER_EMPTY_ACCOUNT_COST; + tx.base.authorization_list = vec![Either::Left(SignedAuthorization::new_unchecked( + Authorization { + chain_id: Default::default(), + address: Default::default(), + nonce: 0, + }, + 0, + U256::ZERO, + U256::ZERO, + ))] + }) + .build_scroll(); + let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); + let gas_with_authorization_list = handler.validate_initial_tx_gas(&evm)?; + + // initial gas should include eip7702 cost of authorized accounts. + assert_eq!( + gas_empty_authorization_list.initial_gas + eip7702::PER_EMPTY_ACCOUNT_COST, + gas_with_authorization_list.initial_gas + ); + + Ok(()) +} + +#[test] +fn test_validate_env_eip7702() -> Result<(), Box> { + let ctx = context().modify_tx_chained(|tx| { + tx.base.tx_type = TransactionType::Eip7702 as u8; + tx.base.authorization_list = vec![Either::Left(SignedAuthorization::new_unchecked( + Authorization { chain_id: Default::default(), address: Default::default(), nonce: 0 }, + 0, + U256::ZERO, + U256::ZERO, + ))] + }); + let mut evm = ctx.build_scroll(); + let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); + + // eip 7702 env checks should pass. + handler.validate_env(&mut evm)?; + + Ok(()) +} diff --git a/src/tests/fees.rs b/src/tests/fees.rs new file mode 100644 index 0000000..64acd78 --- /dev/null +++ b/src/tests/fees.rs @@ -0,0 +1,47 @@ +use crate::{ + builder::ScrollBuilder, + handler::ScrollHandler, + test_utils::{context_with_funds, CALLER}, + ScrollSpecId, +}; +use std::boxed::Box; + +use revm::{ + context::{result::EVMError, ContextTr, JournalTr}, + handler::{EthFrame, EvmTr, Handler}, +}; +use revm_primitives::U256; + +#[test] +fn test_should_deduct_correct_fees_bernoulli() -> Result<(), Box> { + let ctx = context_with_funds(U256::from(30_000)) + .modify_cfg_chained(|cfg| cfg.spec = ScrollSpecId::BERNOULLI); + let mut evm = ctx.clone().build_scroll(); + let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); + + handler.pre_execution(&mut evm).unwrap(); + + let caller_account = evm.ctx().journal().load_account(CALLER)?; + + // cost is 21k + 1012 (shanghai l1 cost). + assert_eq!(caller_account.data.info.balance, U256::from(7988)); + + Ok(()) +} + +#[test] +fn test_should_deduct_correct_fees_curie() -> Result<(), Box> { + let ctx = context_with_funds(U256::from(70_000)) + .modify_cfg_chained(|cfg| cfg.spec = ScrollSpecId::CURIE); + let mut evm = ctx.clone().build_scroll(); + let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); + + handler.pre_execution(&mut evm).unwrap(); + + let caller_account = evm.ctx().journal().load_account(CALLER)?; + + // cost is 21k + 40k (curie l1 cost). + assert_eq!(caller_account.data.info.balance, U256::from(9000)); + + Ok(()) +} diff --git a/src/tests/l1_message.rs b/src/tests/l1_message.rs new file mode 100644 index 0000000..15aced6 --- /dev/null +++ b/src/tests/l1_message.rs @@ -0,0 +1,122 @@ +use crate::{ + builder::ScrollBuilder, + handler::ScrollHandler, + l1block::L1BlockInfo, + test_utils::{context, BENEFICIARY, CALLER}, + transaction::L1_MESSAGE_TYPE, +}; +use std::boxed::Box; + +use revm::{ + context::{result::EVMError, ContextTr, JournalTr}, + handler::{EthFrame, EvmTr, FrameResult, Handler}, + interpreter::{CallOutcome, Gas, InstructionResult, InterpreterResult}, +}; +use revm_primitives::U256; + +#[test] +fn test_validate_lacking_funds_l1_message() -> Result<(), Box> { + let ctx = context().modify_tx_chained(|tx| tx.base.tx_type = L1_MESSAGE_TYPE); + let mut evm = ctx.build_scroll(); + let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); + + // pre execution includes fees deduction, which should be skipped for l1 messages. + handler.pre_execution(&mut evm)?; + + Ok(()) +} + +#[test] +fn test_load_account_l1_message() -> Result<(), Box> { + let ctx = context().modify_tx_chained(|tx| tx.base.tx_type = L1_MESSAGE_TYPE); + let mut evm = ctx.build_scroll(); + let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); + handler.load_accounts(&mut evm)?; + + // l1 block info should not be loaded for l1 messages. + let l1_block_info = evm.ctx().chain.clone(); + assert_eq!(l1_block_info, L1BlockInfo::default()); + + Ok(()) +} + +#[test] +fn test_deduct_caller_l1_message() -> Result<(), Box> { + let ctx = context().modify_tx_chained(|tx| tx.base.tx_type = L1_MESSAGE_TYPE); + + let mut evm = ctx.build_scroll(); + let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); + handler.load_accounts(&mut evm)?; + handler.validate_against_state_and_deduct_caller(&mut evm)?; + + // nonce should be increase and caller should have same balance as the start (0). + let caller_account = evm.ctx().journal().load_account(CALLER)?; + assert_eq!(caller_account.info.balance, U256::ZERO); + assert_eq!(caller_account.info.nonce, 1); + + Ok(()) +} + +#[test] +fn test_last_frame_result_l1_message() -> Result<(), Box> { + let ctx = context().modify_tx_chained(|tx| tx.base.tx_type = L1_MESSAGE_TYPE); + + let mut evm = ctx.build_scroll(); + let mut handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); + let mut gas = Gas::new(21000); + gas.set_refund(10); + gas.set_spent(10); + let mut result = FrameResult::Call(CallOutcome::new( + InterpreterResult { result: InstructionResult::Return, output: Default::default(), gas }, + 0..0, + )); + handler.last_frame_result(&mut evm, &mut result)?; + + // refund should be 0 for l1 messages. + gas.set_refund(0); + assert_eq!(result.gas(), &gas); + + Ok(()) +} + +#[test] +fn test_refund_l1_message() -> Result<(), Box> { + let ctx = context().modify_tx_chained(|tx| tx.base.tx_type = L1_MESSAGE_TYPE); + + let mut evm = ctx.build_scroll(); + let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); + let mut gas = Gas::new(21000); + gas.set_refund(10); + gas.set_spent(10); + let mut result = FrameResult::Call(CallOutcome::new( + InterpreterResult { result: InstructionResult::Return, output: Default::default(), gas }, + 0..0, + )); + handler.refund(&mut evm, &mut result, 0); + + // gas should not have been updated + assert_eq!(result.gas(), &gas); + + Ok(()) +} + +#[test] +fn test_reward_beneficiary_l1_message() -> Result<(), Box> { + let ctx = context().modify_tx_chained(|tx| tx.base.tx_type = L1_MESSAGE_TYPE); + + let mut evm = ctx.build_scroll(); + let handler = ScrollHandler::<_, EVMError<_>, EthFrame<_, _, _>>::new(); + let gas = Gas::new_spent(21000); + let mut result = FrameResult::Call(CallOutcome::new( + InterpreterResult { result: InstructionResult::Return, output: Default::default(), gas }, + 0..0, + )); + handler.load_accounts(&mut evm)?; + handler.reward_beneficiary(&mut evm, &mut result)?; + + // beneficiary should not see his balance increased for l1 message execution. + let beneficiary = evm.ctx().journal().load_account(BENEFICIARY)?; + assert_eq!(beneficiary.info.balance, U256::ZERO); + + Ok(()) +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs new file mode 100644 index 0000000..401763a --- /dev/null +++ b/src/tests/mod.rs @@ -0,0 +1,4 @@ +mod eip7623; +mod eip7702; +mod fees; +mod l1_message;