From 36187937060d467eb27d0b37876d21e10c8e1e22 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Mon, 1 Aug 2022 11:00:12 -0700 Subject: [PATCH] Test for parsing type 0 transactions I made a couple related changes to `Interpreter`: - Only enforce the `JUMPDEST` rule if we're in kernel mode. - Convenience methods for dealing with the RLP and txn field segments of memory. --- evm/src/cpu/kernel/interpreter.rs | 43 ++++++++++++------ evm/src/cpu/kernel/tests/mod.rs | 1 + evm/src/cpu/kernel/tests/rlp.rs | 39 ++++++---------- evm/src/cpu/kernel/tests/type_0_txn.rs | 63 ++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 38 deletions(-) create mode 100644 evm/src/cpu/kernel/tests/type_0_txn.rs diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 6a9d31fa..3b753f0a 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -7,11 +7,12 @@ use keccak_hash::keccak; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::assembler::Kernel; use crate::cpu::kernel::prover_input::ProverInputFn; +use crate::cpu::kernel::txn_fields::NormalizedTxnField; use crate::generation::memory::{MemoryContextState, MemorySegmentState}; use crate::memory::segments::Segment; /// Halt interpreter execution whenever a jump to this offset is done. -const HALT_OFFSET: usize = 0xdeadbeef; +const DEFAULT_HALT_OFFSET: usize = 0xdeadbeef; #[derive(Debug)] pub(crate) struct InterpreterMemory { @@ -49,12 +50,14 @@ impl InterpreterMemory { } pub struct Interpreter<'a> { + kernel_mode: bool, jumpdests: Vec, offset: usize, context: usize, pub(crate) memory: InterpreterMemory, prover_inputs_map: &'a HashMap, prover_inputs: Vec, + pub(crate) halt_offsets: Vec, running: bool, } @@ -100,12 +103,14 @@ impl<'a> Interpreter<'a> { prover_inputs: &'a HashMap, ) -> Self { Self { + kernel_mode: true, jumpdests: find_jumpdests(code), offset: initial_offset, memory: InterpreterMemory::with_code_and_stack(code, initial_stack), prover_inputs_map: prover_inputs, prover_inputs: Vec::new(), context: 0, + halt_offsets: vec![DEFAULT_HALT_OFFSET], running: true, } } @@ -128,6 +133,15 @@ impl<'a> Interpreter<'a> { .collect::>() } + pub(crate) fn get_txn_field(&self, field: NormalizedTxnField) -> U256 { + self.memory.context_memory[0].segments[Segment::TxnFields as usize].content[field as usize] + } + + pub(crate) fn set_rlp_memory(&mut self, rlp: Vec) { + self.memory.context_memory[0].segments[Segment::RlpRaw as usize].content = + rlp.into_iter().map(U256::from).collect(); + } + fn incr(&mut self, n: usize) { self.offset += n; } @@ -435,24 +449,27 @@ impl<'a> Interpreter<'a> { fn run_jump(&mut self) { let x = self.pop().as_usize(); - self.offset = x; - if self.offset == HALT_OFFSET { - self.running = false; - } else if self.jumpdests.binary_search(&self.offset).is_err() { - panic!("Destination is not a JUMPDEST."); - } + self.jump_to(x); } fn run_jumpi(&mut self) { let x = self.pop().as_usize(); let b = self.pop(); if !b.is_zero() { - self.offset = x; - if self.offset == HALT_OFFSET { - self.running = false; - } else if self.jumpdests.binary_search(&self.offset).is_err() { - panic!("Destination is not a JUMPDEST."); - } + self.jump_to(x); + } + } + + fn jump_to(&mut self, offset: usize) { + // The JUMPDEST rule is not enforced in kernel mode. + if !self.kernel_mode && self.jumpdests.binary_search(&offset).is_err() { + panic!("Destination is not a JUMPDEST."); + } + + self.offset = offset; + + if self.halt_offsets.contains(&offset) { + self.running = false; } } diff --git a/evm/src/cpu/kernel/tests/mod.rs b/evm/src/cpu/kernel/tests/mod.rs index 73eb3ada..e912388b 100644 --- a/evm/src/cpu/kernel/tests/mod.rs +++ b/evm/src/cpu/kernel/tests/mod.rs @@ -2,6 +2,7 @@ mod curve_ops; mod ecrecover; mod exp; mod rlp; +mod type_0_txn; use std::str::FromStr; diff --git a/evm/src/cpu/kernel/tests/rlp.rs b/evm/src/cpu/kernel/tests/rlp.rs index e74213da..fd790dcb 100644 --- a/evm/src/cpu/kernel/tests/rlp.rs +++ b/evm/src/cpu/kernel/tests/rlp.rs @@ -15,7 +15,7 @@ fn test_decode_rlp_string_len_short() -> Result<()> { let mut interpreter = Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); // A couple dummy bytes, followed by "0x70" which is its own encoding. - set_rlp_memory(&mut interpreter, vec![123, 234, 0x70]); + interpreter.set_rlp_memory(vec![123, 234, 0x70]); interpreter.run()?; let expected_stack = vec![1.into(), 2.into()]; // len, pos @@ -32,7 +32,7 @@ fn test_decode_rlp_string_len_medium() -> Result<()> { let mut interpreter = Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); // A couple dummy bytes, followed by the RLP encoding of "1 2 3 4 5". - set_rlp_memory(&mut interpreter, vec![123, 234, 0x85, 1, 2, 3, 4, 5]); + interpreter.set_rlp_memory(vec![123, 234, 0x85, 1, 2, 3, 4, 5]); interpreter.run()?; let expected_stack = vec![5.into(), 3.into()]; // len, pos @@ -49,14 +49,11 @@ fn test_decode_rlp_string_len_long() -> Result<()> { let mut interpreter = Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); // The RLP encoding of the string "1 2 3 ... 56". - set_rlp_memory( - &mut interpreter, - vec![ - 123, 234, 0xb8, 56, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - ], - ); + interpreter.set_rlp_memory(vec![ + 123, 234, 0xb8, 56, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + ]); interpreter.run()?; let expected_stack = vec![56.into(), 4.into()]; // len, pos @@ -73,7 +70,7 @@ fn test_decode_rlp_list_len_short() -> Result<()> { let mut interpreter = Interpreter::new_with_kernel(decode_rlp_list_len, initial_stack); // The RLP encoding of [1, 2, [3, 4]]. - set_rlp_memory(&mut interpreter, vec![0xc5, 1, 2, 0xc2, 3, 4]); + interpreter.set_rlp_memory(vec![0xc5, 1, 2, 0xc2, 3, 4]); interpreter.run()?; let expected_stack = vec![5.into(), 1.into()]; // len, pos @@ -90,14 +87,11 @@ fn test_decode_rlp_list_len_long() -> Result<()> { let mut interpreter = Interpreter::new_with_kernel(decode_rlp_list_len, initial_stack); // The RLP encoding of [1, ..., 56]. - set_rlp_memory( - &mut interpreter, - vec![ - 0xf8, 56, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, - 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - ], - ); + interpreter.set_rlp_memory(vec![ + 0xf8, 56, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + ]); interpreter.run()?; let expected_stack = vec![56.into(), 2.into()]; // len, pos @@ -114,7 +108,7 @@ fn test_decode_rlp_scalar() -> Result<()> { let mut interpreter = Interpreter::new_with_kernel(decode_rlp_scalar, initial_stack); // The RLP encoding of "12 34 56". - set_rlp_memory(&mut interpreter, vec![0x83, 0x12, 0x34, 0x56]); + interpreter.set_rlp_memory(vec![0x83, 0x12, 0x34, 0x56]); interpreter.run()?; let expected_stack = vec![0x123456.into(), 4.into()]; // scalar, pos @@ -122,8 +116,3 @@ fn test_decode_rlp_scalar() -> Result<()> { Ok(()) } - -fn set_rlp_memory(interpreter: &mut Interpreter, rlp: Vec) { - interpreter.memory.context_memory[0].segments[Segment::RlpRaw as usize].content = - rlp.into_iter().map(U256::from).collect(); -} diff --git a/evm/src/cpu/kernel/tests/type_0_txn.rs b/evm/src/cpu/kernel/tests/type_0_txn.rs new file mode 100644 index 00000000..437795e9 --- /dev/null +++ b/evm/src/cpu/kernel/tests/type_0_txn.rs @@ -0,0 +1,63 @@ +use std::str::FromStr; + +use anyhow::Result; +use ethereum_types::U256; +use hex_literal::hex; +use NormalizedTxnField::*; + +use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::kernel::interpreter::Interpreter; +use crate::cpu::kernel::tests::rlp::set_rlp_memory; +use crate::cpu::kernel::txn_fields::NormalizedTxnField; + +#[test] +fn process_type_0_txn() -> Result<()> { + let process_type_0_txn = KERNEL.global_labels["process_type_0_txn"]; + let process_normalized_txn = KERNEL.global_labels["process_normalized_txn"]; + + let mut interpreter = Interpreter::new_with_kernel(process_type_0_txn, vec![]); + + // When we reach process_normalized_txn, we're done with parsing and normalizing. + // Processing normalized transactions is outside the scope of this test. + interpreter.halt_offsets.push(process_normalized_txn); + + // Generated with py-evm: + // unsigned_txn = chain.create_unsigned_transaction( + // nonce=5, + // gas_price=10, + // gas=22_000, + // to=constants.ZERO_ADDRESS, + // value=100, + // data=b'\x42\x42', + // ) + // my_txn = unsigned_txn.as_signed_transaction(my_sk) + // rlp.encode(my_txn) + interpreter.set_rlp_memory(hex!("f861050a8255f0940000000000000000000000000000000000000000648242421ca07c5c61ed975ebd286f6b027b8c504842e50a47d318e1e801719dd744fe93e6c6a01e7b5119b57dd54e175ff2f055c91f3ab1b53eba0b2c184f347cdff0e745aca2").to_vec()); + + interpreter.run()?; + + assert_eq!(interpreter.get_txn_field(ChainIdPresent), 0.into()); + assert_eq!(interpreter.get_txn_field(ChainId), 0.into()); + assert_eq!(interpreter.get_txn_field(Nonce), 5.into()); + assert_eq!(interpreter.get_txn_field(MaxPriorityFeePerGas), 10.into()); + assert_eq!(interpreter.get_txn_field(MaxPriorityFeePerGas), 10.into()); + assert_eq!(interpreter.get_txn_field(MaxFeePerGas), 10.into()); + assert_eq!(interpreter.get_txn_field(To), 0.into()); + assert_eq!(interpreter.get_txn_field(Value), 100.into()); + assert_eq!(interpreter.get_txn_field(DataLen), 2.into()); + assert_eq!(interpreter.get_txn_field(YParity), 1.into()); + assert_eq!( + interpreter.get_txn_field(R), + U256::from_big_endian(&hex!( + "7c5c61ed975ebd286f6b027b8c504842e50a47d318e1e801719dd744fe93e6c6" + )) + ); + assert_eq!( + interpreter.get_txn_field(S), + U256::from_big_endian(&hex!( + "1e7b5119b57dd54e175ff2f055c91f3ab1b53eba0b2c184f347cdff0e745aca2" + )) + ); + + Ok(()) +}