-
Notifications
You must be signed in to change notification settings - Fork 2
test: add Rex4 system contract deployment and idempotence tests #187
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,192 @@ | ||
| //! Tests for Rex4 system contract deployment wiring. | ||
|
|
||
| use alloy_evm::{block::BlockExecutor, Database, Evm, EvmEnv, EvmFactory}; | ||
| use alloy_hardforks::ForkCondition; | ||
| use alloy_op_evm::block::receipt_builder::OpAlloyReceiptBuilder; | ||
| use alloy_primitives::{Address, B256, Bytes}; | ||
| use mega_evm::{ | ||
| test_utils::MemoryDatabase, BlockLimits, MegaBlockExecutionCtx, MegaBlockExecutor, | ||
| MegaEvmFactory, MegaHardfork, MegaHardforkConfig, MegaSpecId, ACCESS_CONTROL_ADDRESS, | ||
| ACCESS_CONTROL_CODE, ACCESS_CONTROL_CODE_HASH, LIMIT_CONTROL_ADDRESS, LIMIT_CONTROL_CODE, | ||
| LIMIT_CONTROL_CODE_HASH, | ||
| }; | ||
| use revm::{context::BlockEnv, database::State, primitives::U256}; | ||
|
|
||
| fn rex4_chain_spec() -> MegaHardforkConfig { | ||
| MegaHardforkConfig::default().with(MegaHardfork::Rex4, ForkCondition::Timestamp(0)) | ||
| } | ||
|
|
||
| fn rex3_chain_spec() -> MegaHardforkConfig { | ||
| MegaHardforkConfig::default().with(MegaHardfork::Rex3, ForkCondition::Timestamp(0)) | ||
| } | ||
|
|
||
| fn make_block_env(timestamp: u64) -> BlockEnv { | ||
| BlockEnv { | ||
| number: U256::from(1_u64), | ||
| timestamp: U256::from(timestamp), | ||
| gas_limit: 30_000_000, | ||
| ..Default::default() | ||
| } | ||
| } | ||
|
|
||
| fn assert_contract_deployed<DB: Database>( | ||
| db: &mut State<DB>, | ||
| address: Address, | ||
| expected_code_hash: B256, | ||
| expected_code: Bytes, | ||
| name: &str, | ||
| ) { | ||
| let cache_acc = db.load_cache_account(address).expect("should load cache account"); | ||
| let acc_info = cache_acc.account_info().expect("system contract account should exist"); | ||
|
|
||
| assert_eq!( | ||
| acc_info.code_hash, expected_code_hash, | ||
| "{name} code hash should match expected bytecode" | ||
| ); | ||
| assert!(acc_info.code.is_some(), "{name} code should be set"); | ||
| let deployed_code = acc_info.code.as_ref().expect("code should be present"); | ||
| assert_eq!( | ||
| deployed_code.original_bytes(), | ||
| expected_code, | ||
| "{name} bytecode should match the embedded system contract code" | ||
| ); | ||
| } | ||
|
|
||
| fn assert_contract_not_deployed<DB: Database>( | ||
| db: &mut State<DB>, | ||
| address: Address, | ||
| name: &str, | ||
| ) { | ||
| let cache_acc = db.load_cache_account(address).expect("should load cache account"); | ||
| assert!( | ||
| cache_acc.account_info().is_none(), | ||
| "{name} should not be deployed for this spec boundary" | ||
| ); | ||
| } | ||
|
Comment on lines
+60
to
+65
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| #[test] | ||
| fn test_rex4_system_contracts_deployed_on_activation() { | ||
| let mut db = MemoryDatabase::default(); | ||
| let mut state = State::builder().with_database(&mut db).build(); | ||
|
|
||
| let mut cfg_env = revm::context::CfgEnv::default(); | ||
| cfg_env.spec = MegaSpecId::REX4; | ||
| let evm_env = EvmEnv::new(cfg_env, make_block_env(0)); | ||
|
|
||
| let evm_factory = MegaEvmFactory::new(); | ||
| let evm = evm_factory.create_evm(&mut state, evm_env); | ||
| let block_ctx = MegaBlockExecutionCtx::new( | ||
| B256::ZERO, | ||
| Some(B256::ZERO), | ||
| Default::default(), | ||
| BlockLimits::no_limits(), | ||
| ); | ||
| let receipt_builder = OpAlloyReceiptBuilder::default(); | ||
| let mut executor = MegaBlockExecutor::new(evm, block_ctx, rex4_chain_spec(), receipt_builder); | ||
|
|
||
| executor.apply_pre_execution_changes().expect("pre-execution changes should succeed"); | ||
|
|
||
| let db_ref = executor.evm_mut().db_mut(); | ||
| assert_contract_deployed( | ||
| db_ref, | ||
| ACCESS_CONTROL_ADDRESS, | ||
| ACCESS_CONTROL_CODE_HASH, | ||
| ACCESS_CONTROL_CODE, | ||
| "MegaAccessControl", | ||
| ); | ||
| assert_contract_deployed( | ||
| db_ref, | ||
| LIMIT_CONTROL_ADDRESS, | ||
| LIMIT_CONTROL_CODE_HASH, | ||
| LIMIT_CONTROL_CODE, | ||
| "MegaLimitControl", | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_rex4_system_contract_deployment_is_idempotent() { | ||
| let mut db = MemoryDatabase::default(); | ||
| let mut state = State::builder().with_database(&mut db).build(); | ||
|
|
||
| let mut cfg_env = revm::context::CfgEnv::default(); | ||
| cfg_env.spec = MegaSpecId::REX4; | ||
| let evm_env = EvmEnv::new(cfg_env, make_block_env(0)); | ||
|
|
||
| let evm_factory = MegaEvmFactory::new(); | ||
| let evm = evm_factory.create_evm(&mut state, evm_env); | ||
| let block_ctx = MegaBlockExecutionCtx::new( | ||
| B256::ZERO, | ||
| Some(B256::ZERO), | ||
| Default::default(), | ||
| BlockLimits::no_limits(), | ||
| ); | ||
| let receipt_builder = OpAlloyReceiptBuilder::default(); | ||
| let mut executor = MegaBlockExecutor::new(evm, block_ctx, rex4_chain_spec(), receipt_builder); | ||
|
|
||
| executor.apply_pre_execution_changes().expect("first pre-execution changes should succeed"); | ||
|
|
||
| { | ||
| let db_ref = executor.evm_mut().db_mut(); | ||
| assert_contract_deployed( | ||
| db_ref, | ||
| ACCESS_CONTROL_ADDRESS, | ||
| ACCESS_CONTROL_CODE_HASH, | ||
| ACCESS_CONTROL_CODE, | ||
| "MegaAccessControl", | ||
| ); | ||
| assert_contract_deployed( | ||
| db_ref, | ||
| LIMIT_CONTROL_ADDRESS, | ||
| LIMIT_CONTROL_CODE_HASH, | ||
| LIMIT_CONTROL_CODE, | ||
| "MegaLimitControl", | ||
| ); | ||
| } | ||
|
|
||
| executor | ||
| .apply_pre_execution_changes() | ||
| .expect("second pre-execution changes should also succeed"); | ||
|
|
||
| let db_ref = executor.evm_mut().db_mut(); | ||
| assert_contract_deployed( | ||
| db_ref, | ||
| ACCESS_CONTROL_ADDRESS, | ||
| ACCESS_CONTROL_CODE_HASH, | ||
| ACCESS_CONTROL_CODE, | ||
| "MegaAccessControl after second apply", | ||
| ); | ||
| assert_contract_deployed( | ||
| db_ref, | ||
| LIMIT_CONTROL_ADDRESS, | ||
| LIMIT_CONTROL_CODE_HASH, | ||
| LIMIT_CONTROL_CODE, | ||
| "MegaLimitControl after second apply", | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_rex3_boundary_does_not_deploy_rex4_system_contracts() { | ||
| let mut db = MemoryDatabase::default(); | ||
| let mut state = State::builder().with_database(&mut db).build(); | ||
|
|
||
| let mut cfg_env = revm::context::CfgEnv::default(); | ||
| cfg_env.spec = MegaSpecId::REX3; | ||
| let evm_env = EvmEnv::new(cfg_env, make_block_env(0)); | ||
|
|
||
| let evm_factory = MegaEvmFactory::new(); | ||
| let evm = evm_factory.create_evm(&mut state, evm_env); | ||
| let block_ctx = MegaBlockExecutionCtx::new( | ||
| B256::ZERO, | ||
| Some(B256::ZERO), | ||
| Default::default(), | ||
| BlockLimits::no_limits(), | ||
| ); | ||
| let receipt_builder = OpAlloyReceiptBuilder::default(); | ||
| let mut executor = MegaBlockExecutor::new(evm, block_ctx, rex3_chain_spec(), receipt_builder); | ||
|
|
||
| executor.apply_pre_execution_changes().expect("pre-execution changes should succeed"); | ||
|
|
||
| let db_ref = executor.evm_mut().db_mut(); | ||
| assert_contract_not_deployed(db_ref, ACCESS_CONTROL_ADDRESS, "MegaAccessControl"); | ||
| assert_contract_not_deployed(db_ref, LIMIT_CONTROL_ADDRESS, "MegaLimitControl"); | ||
| } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Significant setup duplication across all three test functions (~20 lines of identical boilerplate each). The existing test suite typically extracts setup into helper functions. Consider a helper like |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rex4_chain_spec()only activatesMegaHardfork::Rex4without enabling prerequisite hardforks (Rex, Rex1, Rex2, Rex3). In production, all prior hardforks would be active. Ifapply_pre_execution_changes()deploys earlier system contracts (Oracle, HP Timestamp, Keyless Deploy) conditionally on their hardfork activation, this test won't exercise those paths, potentially masking ordering dependencies.Consider using
MegaHardforkConfig::default().with_all_activated()or chaining all prerequisite hardforks to better reflect production conditions.