From d6b5193c9b706f64f8670dac5799de9e72da1ed8 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Fri, 29 Jul 2022 13:50:16 -0700 Subject: [PATCH] RLP decoding tests --- evm/src/cpu/kernel/asm/rlp/decode.asm | 2 + evm/src/cpu/kernel/interpreter.rs | 63 +++++++++---- evm/src/cpu/kernel/tests/mod.rs | 1 + evm/src/cpu/kernel/tests/rlp.rs | 129 ++++++++++++++++++++++++++ 4 files changed, 178 insertions(+), 17 deletions(-) create mode 100644 evm/src/cpu/kernel/tests/rlp.rs diff --git a/evm/src/cpu/kernel/asm/rlp/decode.asm b/evm/src/cpu/kernel/asm/rlp/decode.asm index 76daec1a..24d8d5a7 100644 --- a/evm/src/cpu/kernel/asm/rlp/decode.asm +++ b/evm/src/cpu/kernel/asm/rlp/decode.asm @@ -32,6 +32,7 @@ global decode_rlp_string_len: JUMP decode_rlp_string_len_medium: + JUMPDEST // String is 0-55 bytes long. First byte contains the len. // stack: first_byte, pos, retdest %sub_const(0x80) @@ -43,6 +44,7 @@ decode_rlp_string_len_medium: JUMP decode_rlp_string_len_large: + JUMPDEST // String is >55 bytes long. First byte contains the len of the len. // stack: first_byte, pos, retdest %sub_const(0xb7) diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 5c2f6514..6a9d31fa 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -4,6 +4,7 @@ use anyhow::{anyhow, bail}; use ethereum_types::{BigEndianHash, U256, U512}; 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::generation::memory::{MemoryContextState, MemorySegmentState}; @@ -14,7 +15,7 @@ const HALT_OFFSET: usize = 0xdeadbeef; #[derive(Debug)] pub(crate) struct InterpreterMemory { - context_memory: Vec, + pub(crate) context_memory: Vec, } impl Default for InterpreterMemory { @@ -51,13 +52,14 @@ pub struct Interpreter<'a> { jumpdests: Vec, offset: usize, context: usize, - memory: InterpreterMemory, + pub(crate) memory: InterpreterMemory, prover_inputs_map: &'a HashMap, prover_inputs: Vec, running: bool, } pub fn run_with_kernel( + // TODO: Remove param and just use KERNEL. kernel: &Kernel, initial_offset: usize, initial_stack: Vec, @@ -76,24 +78,45 @@ pub fn run<'a>( initial_stack: Vec, prover_inputs: &'a HashMap, ) -> anyhow::Result> { - let mut interpreter = Interpreter { - 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, - running: true, - }; - - while interpreter.running { - interpreter.run_opcode()?; - } - + let mut interpreter = Interpreter::new(code, initial_offset, initial_stack, prover_inputs); + interpreter.run()?; Ok(interpreter) } impl<'a> Interpreter<'a> { + pub(crate) fn new_with_kernel(initial_offset: usize, initial_stack: Vec) -> Self { + Self::new( + &KERNEL.code, + initial_offset, + initial_stack, + &KERNEL.prover_inputs, + ) + } + + pub(crate) fn new( + code: &'a [u8], + initial_offset: usize, + initial_stack: Vec, + prover_inputs: &'a HashMap, + ) -> Self { + Self { + 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, + running: true, + } + } + + pub(crate) fn run(&mut self) -> anyhow::Result<()> { + while self.running { + self.run_opcode()?; + } + Ok(()) + } + fn code(&self) -> &MemorySegmentState { &self.memory.context_memory[self.context].segments[Segment::Code as usize] } @@ -156,7 +179,7 @@ impl<'a> Interpreter<'a> { 0x18 => self.run_xor(), // "XOR", 0x19 => self.run_not(), // "NOT", 0x1a => todo!(), // "BYTE", - 0x1b => todo!(), // "SHL", + 0x1b => self.run_shl(), // "SHL", 0x1c => todo!(), // "SHR", 0x1d => todo!(), // "SAR", 0x20 => self.run_keccak256(), // "KECCAK256", @@ -339,6 +362,12 @@ impl<'a> Interpreter<'a> { self.push(!x); } + fn run_shl(&mut self) { + let shift = self.pop(); + let x = self.pop(); + self.push(x << shift); + } + fn run_keccak256(&mut self) { let offset = self.pop().as_usize(); let size = self.pop().as_usize(); diff --git a/evm/src/cpu/kernel/tests/mod.rs b/evm/src/cpu/kernel/tests/mod.rs index 100ef377..73eb3ada 100644 --- a/evm/src/cpu/kernel/tests/mod.rs +++ b/evm/src/cpu/kernel/tests/mod.rs @@ -1,6 +1,7 @@ mod curve_ops; mod ecrecover; mod exp; +mod rlp; use std::str::FromStr; diff --git a/evm/src/cpu/kernel/tests/rlp.rs b/evm/src/cpu/kernel/tests/rlp.rs new file mode 100644 index 00000000..e74213da --- /dev/null +++ b/evm/src/cpu/kernel/tests/rlp.rs @@ -0,0 +1,129 @@ +use std::str::FromStr; + +use anyhow::Result; +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<()> { + let decode_rlp_string_len = KERNEL.global_labels["decode_rlp_string_len"]; + + let initial_stack = vec![U256::from_str("0xdeadbeef")?, 2.into()]; + 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.run()?; + let expected_stack = vec![1.into(), 2.into()]; // len, pos + assert_eq!(interpreter.stack(), expected_stack); + + Ok(()) +} + +#[test] +fn test_decode_rlp_string_len_medium() -> Result<()> { + let decode_rlp_string_len = KERNEL.global_labels["decode_rlp_string_len"]; + + let initial_stack = vec![U256::from_str("0xdeadbeef")?, 2.into()]; + 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.run()?; + let expected_stack = vec![5.into(), 3.into()]; // len, pos + assert_eq!(interpreter.stack(), expected_stack); + + Ok(()) +} + +#[test] +fn test_decode_rlp_string_len_long() -> Result<()> { + let decode_rlp_string_len = KERNEL.global_labels["decode_rlp_string_len"]; + + let initial_stack = vec![U256::from_str("0xdeadbeef")?, 2.into()]; + 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.run()?; + let expected_stack = vec![56.into(), 4.into()]; // len, pos + assert_eq!(interpreter.stack(), expected_stack); + + Ok(()) +} + +#[test] +fn test_decode_rlp_list_len_short() -> Result<()> { + let decode_rlp_list_len = KERNEL.global_labels["decode_rlp_list_len"]; + + let initial_stack = vec![U256::from_str("0xdeadbeef")?, 0.into()]; + 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.run()?; + let expected_stack = vec![5.into(), 1.into()]; // len, pos + assert_eq!(interpreter.stack(), expected_stack); + + Ok(()) +} + +#[test] +fn test_decode_rlp_list_len_long() -> Result<()> { + let decode_rlp_list_len = KERNEL.global_labels["decode_rlp_list_len"]; + + let initial_stack = vec![U256::from_str("0xdeadbeef")?, 0.into()]; + 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.run()?; + let expected_stack = vec![56.into(), 2.into()]; // len, pos + assert_eq!(interpreter.stack(), expected_stack); + + Ok(()) +} + +#[test] +fn test_decode_rlp_scalar() -> Result<()> { + let decode_rlp_scalar = KERNEL.global_labels["decode_rlp_scalar"]; + + let initial_stack = vec![U256::from_str("0xdeadbeef")?, 0.into()]; + 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.run()?; + let expected_stack = vec![0x123456.into(), 4.into()]; // scalar, pos + assert_eq!(interpreter.stack(), expected_stack); + + 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(); +}