diff --git a/evm/src/cpu/kernel/asm/account_code.asm b/evm/src/cpu/kernel/asm/account_code.asm index 0353b481..7d560ec1 100644 --- a/evm/src/cpu/kernel/asm/account_code.asm +++ b/evm/src/cpu/kernel/asm/account_code.asm @@ -8,6 +8,12 @@ // stack: codehash %endmacro + +%macro codesize + ADDRESS + %extcodesize +%endmacro + %macro extcodesize %stack (address) -> (address, %%after) %jump(load_code) @@ -20,10 +26,6 @@ global extcodesize: // stack: extcodesize(address), retdest SWAP1 JUMP -%macro codesize - ADDRESS - %extcodesize -%endmacro %macro codecopy // stack: dest_offset, offset, size, retdest @@ -73,14 +75,15 @@ extcodecopy_loop: %stack (i, code_length, offset, dest_offset, size, retdest) -> (i, size, code_length, offset, dest_offset, retdest) %jump(extcodecopy_loop) - - - extcodecopy_end: - %stack (i, size, code_length, offset, dest_offset, size, retdest) -> (retdest) + %stack (i, size, code_length, offset, dest_offset, retdest) -> (retdest) JUMP +// Loads the code at `address` in the `SEGMENT_KERNEL_ACCOUNT_CODE` at the current context and starting at offset 0. +// Checks that the hash of the loaded code corresponds to the `codehash` in the state trie. +// Pre stack: address, retdest +// Post stack: extcodesize(address) load_code: // stack: address, retdest %extcodehash diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 8057792b..478d5413 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -71,7 +71,7 @@ pub struct Interpreter<'a> { kernel_mode: bool, jumpdests: Vec, pub(crate) offset: usize, - context: usize, + pub(crate) context: usize, pub(crate) memory: InterpreterMemory, pub(crate) generation_state: GenerationState, prover_inputs_map: &'a HashMap, diff --git a/evm/src/cpu/kernel/tests/account_code.rs b/evm/src/cpu/kernel/tests/account_code.rs index 0efd96eb..0576e0e4 100644 --- a/evm/src/cpu/kernel/tests/account_code.rs +++ b/evm/src/cpu/kernel/tests/account_code.rs @@ -11,6 +11,7 @@ use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; use crate::cpu::kernel::interpreter::Interpreter; use crate::cpu::kernel::tests::mpt::nibbles_64; use crate::generation::mpt::{all_mpt_prover_inputs_reversed, AccountRlp}; +use crate::memory::segments::Segment; fn test_account(code: &[u8]) -> AccountRlp { AccountRlp { @@ -23,11 +24,12 @@ fn test_account(code: &[u8]) -> AccountRlp { fn random_code() -> Vec { let mut rng = thread_rng(); - let num_bytes = rng.gen_range(0..10000); + let num_bytes = rng.gen_range(0..1000); (0..num_bytes).map(|_| rng.gen()).collect() } // Stolen from `tests/mpt/insert.rs` +// Prepare the interpreter by inserting the account in the state trie. fn prepare_interpreter( interpreter: &mut Interpreter, address: Address, @@ -99,6 +101,33 @@ fn prepare_interpreter( Ok(()) } +#[test] +fn test_extcodesize() -> Result<()> { + let code = random_code(); + let account = test_account(&code); + + let mut interpreter = Interpreter::new_with_kernel(0, vec![]); + let address: Address = thread_rng().gen(); + // Prepare the interpreter by inserting the account in the state trie. + prepare_interpreter(&mut interpreter, address, &account)?; + + let extcodesize = KERNEL.global_labels["extcodesize"]; + + // Test `extcodesize` + interpreter.offset = extcodesize; + interpreter.pop(); + assert!(interpreter.stack().is_empty()); + interpreter.push(0xDEADBEEFu32.into()); + interpreter.push(U256::from_big_endian(address.as_bytes())); + interpreter.generation_state.inputs.contract_code = + HashMap::from([(keccak(&code), code.clone())]); + interpreter.run()?; + + assert_eq!(interpreter.stack(), vec![code.len().into()]); + + Ok(()) +} + #[test] fn test_extcodecopy() -> Result<()> { let code = random_code(); @@ -106,19 +135,47 @@ fn test_extcodecopy() -> Result<()> { let mut interpreter = Interpreter::new_with_kernel(0, vec![]); let address: Address = thread_rng().gen(); + // Prepare the interpreter by inserting the account in the state trie. prepare_interpreter(&mut interpreter, address, &account)?; let extcodecopy = KERNEL.global_labels["extcodecopy"]; - let extcodesize = KERNEL.global_labels["extcodesize"]; + // Put random data in main memory. + let mut rng = thread_rng(); + for i in 0..2000 { + interpreter.memory.context_memory[interpreter.context].segments + [Segment::MainMemory as usize] + .set(i, U256::from(rng.gen::())); + } + + let dest_offset = rng.gen_range(0..3000); + let offset = rng.gen_range(0..1500); + let size = rng.gen_range(0..1500); + + // Test `extcodecopy` + interpreter.offset = extcodecopy; interpreter.pop(); + assert!(interpreter.stack().is_empty()); interpreter.push(0xDEADBEEFu32.into()); + interpreter.push(size.into()); + interpreter.push(offset.into()); + interpreter.push(dest_offset.into()); interpreter.push(U256::from_big_endian(address.as_bytes())); - interpreter.offset = extcodesize; interpreter.generation_state.inputs.contract_code = HashMap::from([(keccak(&code), code.clone())]); interpreter.run()?; - assert_eq!(interpreter.stack(), vec![code.len().into()]); + + assert!(interpreter.stack().is_empty()); + // Check that the code was correctly copied to memory. + for i in 0..size { + let memory = interpreter.memory.context_memory[interpreter.context].segments + [Segment::MainMemory as usize] + .get(dest_offset + i); + assert_eq!( + memory, + code.get(offset + i).copied().unwrap_or_default().into() + ); + } Ok(()) }