diff --git a/src/evm.rs b/src/evm.rs index 2783ad6..5d204ac 100644 --- a/src/evm.rs +++ b/src/evm.rs @@ -24,7 +24,7 @@ impl Self(Evm { ctx, inspector, - instruction: ScrollInstructions::new_mainnet(), + instruction: ScrollInstructions::new_mainnet(spec), precompiles: ScrollPrecompileProvider::new_with_spec(spec), }) } diff --git a/src/instructions.rs b/src/instructions.rs index 59faf42..621c994 100644 --- a/src/instructions.rs +++ b/src/instructions.rs @@ -46,8 +46,8 @@ where WIRE: InterpreterTypes, HOST: ScrollContextTr, { - pub fn new_mainnet() -> Self { - Self::new(make_scroll_instruction_table::()) + pub fn new_mainnet(spec: ScrollSpecId) -> Self { + Self::new(make_scroll_instruction_table::(spec)) } pub fn new(base_table: InstructionTable) -> Self { @@ -65,17 +65,22 @@ where /// - `SELFDESTRUCT` /// - `MCOPY` pub fn make_scroll_instruction_table( + spec: ScrollSpecId, ) -> InstructionTable { let mut table = instruction_table::(); // override the instructions - table[opcode::BLOCKHASH as usize] = blockhash::; table[opcode::BASEFEE as usize] = basefee::; table[opcode::TSTORE as usize] = tstore::; table[opcode::TLOAD as usize] = tload::; table[opcode::SELFDESTRUCT as usize] = selfdestruct::; table[opcode::MCOPY as usize] = mcopy::; + // override blockhash opcode in pre-feynman blocks + if !spec.is_enabled_in(ScrollSpecId::FEYNMAN) { + table[opcode::BLOCKHASH as usize] = blockhash::; + } + table } @@ -204,3 +209,65 @@ fn compute_block_hash(chain_id: u64, block_number: u64) -> U256 { input[8..].copy_from_slice(&block_number.to_be_bytes()); U256::from_be_bytes(keccak256(input).into()) } + +#[cfg(test)] +mod tests { + use revm::{ + bytecode::{opcode::*, Bytecode}, + database::{EmptyDB, InMemoryDB}, + interpreter::Interpreter, + primitives::{Bytes, U256}, + DatabaseRef, + }; + + use crate::{ + builder::{DefaultScrollContext, ScrollContext}, + ScrollSpecId::*, + }; + + use super::{compute_block_hash, make_scroll_instruction_table}; + + #[test] + fn test_blockhash_before_feynman() { + let (chain_id, current_block, target_block, spec) = (123, 1024, 1000, EUCLID); + + let db = EmptyDB::new(); + let mut context = ScrollContext::scroll().with_db(InMemoryDB::new(db)); + context.modify_block(|block| block.number = current_block); + context.modify_cfg(|cfg| cfg.chain_id = chain_id); + context.modify_cfg(|cfg| cfg.spec = spec); + + let instructions = make_scroll_instruction_table(spec); + + let bytecode = Bytecode::new_legacy(Bytes::from(&[BLOCKHASH, STOP])); + let mut interpreter = Interpreter::default().with_bytecode(bytecode); + let _ = interpreter.stack.push(U256::from(target_block)); + interpreter.run_plain(&instructions, &mut context); + + let expected = compute_block_hash(chain_id, target_block); + let actual = interpreter.stack.pop().expect("stack is not empty"); + assert_eq!(actual, expected); + } + + #[test] + fn test_blockhash_after_feynman() { + let (chain_id, current_block, target_block, spec) = (123, 1024, 1000, FEYNMAN); + + let db = EmptyDB::new(); + let mut context = ScrollContext::scroll().with_db(InMemoryDB::new(db)); + context.modify_block(|block| block.number = current_block); + context.modify_cfg(|cfg| cfg.chain_id = chain_id); + context.modify_cfg(|cfg| cfg.spec = spec); + + let instructions = make_scroll_instruction_table(spec); + + let bytecode = Bytecode::new_legacy(Bytes::from(&[BLOCKHASH, STOP])); + let mut interpreter = Interpreter::default().with_bytecode(bytecode); + let _ = interpreter.stack.push(U256::from(target_block)); + interpreter.run_plain(&instructions, &mut context); + + let expected = db.block_hash_ref(target_block).expect("db contains block hash").into(); + let actual = interpreter.stack.pop().expect("stack is not empty"); + assert_eq!(actual, expected); + } +} diff --git a/src/precompile/mod.rs b/src/precompile/mod.rs index 8539ced..9b7bad0 100644 --- a/src/precompile/mod.rs +++ b/src/precompile/mod.rs @@ -29,7 +29,7 @@ impl ScrollPrecompileProvider { let precompiles = match spec { ScrollSpecId::SHANGHAI => pre_bernoulli(), ScrollSpecId::BERNOULLI | ScrollSpecId::CURIE | ScrollSpecId::DARWIN => bernoulli(), - ScrollSpecId::EUCLID => euclid(), + ScrollSpecId::EUCLID | ScrollSpecId::FEYNMAN => euclid(), }; Self { precompile_provider: EthPrecompiles { precompiles, spec: SpecId::default() }, spec } } diff --git a/src/spec.rs b/src/spec.rs index 8f510eb..47e341b 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -11,6 +11,7 @@ pub enum ScrollSpecId { DARWIN = 4, #[default] EUCLID = 5, + FEYNMAN = 6, } impl ScrollSpecId { @@ -35,9 +36,12 @@ impl ScrollSpecId { /// Converts the `ScrollSpecId` to a `SpecId`. const fn into_eth_spec_id(self) -> SpecId { match self { - Self::SHANGHAI | Self::BERNOULLI | Self::CURIE | Self::DARWIN | Self::EUCLID => { - SpecId::SHANGHAI - } + Self::SHANGHAI | + Self::BERNOULLI | + Self::CURIE | + Self::DARWIN | + Self::EUCLID | + Self::FEYNMAN => SpecId::SHANGHAI, } } } @@ -57,6 +61,7 @@ pub mod name { pub const CURIE: &str = "curie"; pub const DARWIN: &str = "darwin"; pub const EUCLID: &str = "euclid"; + pub const FEYNMAN: &str = "feynman"; } impl From<&str> for ScrollSpecId { @@ -67,6 +72,7 @@ impl From<&str> for ScrollSpecId { name::CURIE => Self::CURIE, name::DARWIN => Self::DARWIN, name::EUCLID => Self::EUCLID, + name::FEYNMAN => Self::FEYNMAN, _ => Self::default(), } } @@ -80,6 +86,7 @@ impl From for &'static str { ScrollSpecId::CURIE => name::CURIE, ScrollSpecId::DARWIN => name::DARWIN, ScrollSpecId::EUCLID => name::EUCLID, + ScrollSpecId::FEYNMAN => name::FEYNMAN, } } }