diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 6a9d31fa..6a5b794f 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,19 @@ 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 get_txn_data(&self) -> &[U256] { + &self.memory.context_memory[0].segments[Segment::TxnData as usize].content + } + + 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 +453,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..ab92c5a0 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 transaction_parsing; use std::str::FromStr; diff --git a/evm/src/cpu/kernel/tests/rlp.rs b/evm/src/cpu/kernel/tests/rlp.rs index 79407f1b..cc311b4f 100644 --- a/evm/src/cpu/kernel/tests/rlp.rs +++ b/evm/src/cpu/kernel/tests/rlp.rs @@ -3,7 +3,6 @@ use ethereum_types::U256; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; -use crate::memory::segments::Segment; #[test] fn test_decode_rlp_string_len_short() -> Result<()> { @@ -13,7 +12,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 @@ -30,7 +29,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 @@ -47,14 +46,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 @@ -71,7 +67,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 @@ -88,14 +84,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 @@ -112,7 +105,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 @@ -120,8 +113,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/transaction_parsing/mod.rs b/evm/src/cpu/kernel/tests/transaction_parsing/mod.rs new file mode 100644 index 00000000..fb50625f --- /dev/null +++ b/evm/src/cpu/kernel/tests/transaction_parsing/mod.rs @@ -0,0 +1 @@ +mod parse_type_0_txn; diff --git a/evm/src/cpu/kernel/tests/transaction_parsing/parse_type_0_txn.rs b/evm/src/cpu/kernel/tests/transaction_parsing/parse_type_0_txn.rs new file mode 100644 index 00000000..c01474ce --- /dev/null +++ b/evm/src/cpu/kernel/tests/transaction_parsing/parse_type_0_txn.rs @@ -0,0 +1,65 @@ +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::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: + // import eth, eth_keys, eth_utils, rlp + // genesis_params = { 'difficulty': eth.constants.GENESIS_DIFFICULTY } + // chain = eth.chains.mainnet.MainnetChain.from_genesis(eth.db.atomic.AtomicDB(), genesis_params, {}) + // unsigned_txn = chain.create_unsigned_transaction( + // nonce=5, + // gas_price=10, + // gas=22_000, + // to=eth.constants.ZERO_ADDRESS, + // value=100, + // data=b'\x42\x42', + // ) + // sk = eth_keys.keys.PrivateKey(eth_utils.decode_hex('4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318')) + // signed_txn = unsigned_txn.as_signed_transaction(sk) + // rlp.encode(signed_txn).hex() + 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_data(), &[0x42.into(), 0x42.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(()) +}