From f2f05952abb06d41a7f796fecbc9560edf8d9fd4 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Sat, 1 Oct 2022 10:33:05 -0700 Subject: [PATCH] MPT hashing logic, part 2 --- evm/src/cpu/kernel/asm/memory/core.asm | 6 +- evm/src/cpu/kernel/asm/mpt/hash.asm | 82 ++++++++---- evm/src/cpu/kernel/asm/mpt/hex_prefix.asm | 121 +++++++++++++----- evm/src/cpu/kernel/asm/rlp/encode.asm | 67 ++++++++++ evm/src/cpu/kernel/interpreter.rs | 4 - evm/src/cpu/kernel/tests/mpt/hex_prefix.rs | 59 +++++++-- .../kernel/tests/{rlp.rs => rlp/decode.rs} | 78 ----------- evm/src/cpu/kernel/tests/rlp/encode.rs | 110 ++++++++++++++++ evm/src/cpu/kernel/tests/rlp/mod.rs | 2 + 9 files changed, 374 insertions(+), 155 deletions(-) rename evm/src/cpu/kernel/tests/{rlp.rs => rlp/decode.rs} (59%) create mode 100644 evm/src/cpu/kernel/tests/rlp/encode.rs create mode 100644 evm/src/cpu/kernel/tests/rlp/mod.rs diff --git a/evm/src/cpu/kernel/asm/memory/core.asm b/evm/src/cpu/kernel/asm/memory/core.asm index f18a620a..6722b0ca 100644 --- a/evm/src/cpu/kernel/asm/memory/core.asm +++ b/evm/src/cpu/kernel/asm/memory/core.asm @@ -99,9 +99,9 @@ // stack: (empty) %endmacro -// Store a single value to @SEGMENT_KERNEL_GENERAL. -%macro mstore_kernel_general +// Store a single byte to @SEGMENT_RLP_RAW. +%macro mstore_rlp // stack: offset, value - %mstore_kernel(@SEGMENT_KERNEL_GENERAL) + %mstore_kernel(@SEGMENT_RLP_RAW) // stack: (empty) %endmacro diff --git a/evm/src/cpu/kernel/asm/mpt/hash.asm b/evm/src/cpu/kernel/asm/mpt/hash.asm index b41e3dbf..4018a036 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash.asm @@ -1,4 +1,12 @@ -global mpt_hash: +// Computes the Merkle root of the given trie node. +// +// The encode_value function should take as input +// - the position withing @SEGMENT_RLP_RAW to write to, +// - the offset of a value within @SEGMENT_TRIE_DATA, and +// - a return address. +// It should serialize the value, write it to @SEGMENT_RLP_RAW starting at the +// given position, and return an updated position (the next unused offset). +%macro mpt_hash(encode_value) // stack: node_ptr, retdest DUP1 %mload_trie_data @@ -9,11 +17,57 @@ global mpt_hash: DUP1 %eq_const(@MPT_NODE_EMPTY) %jumpi(mpt_hash_empty) DUP1 %eq_const(@MPT_NODE_HASH) %jumpi(mpt_hash_hash) - DUP1 %eq_const(@MPT_NODE_BRANCH) %jumpi(mpt_hash_branch) - DUP1 %eq_const(@MPT_NODE_EXTENSION) %jumpi(mpt_hash_extension) - DUP1 %eq_const(@MPT_NODE_LEAF) %jumpi(mpt_hash_leaf) + DUP1 %eq_const(@MPT_NODE_BRANCH) %jumpi(%%mpt_hash_branch) + DUP1 %eq_const(@MPT_NODE_EXTENSION) %jumpi(%%mpt_hash_extension) + DUP1 %eq_const(@MPT_NODE_LEAF) %jumpi(%%mpt_hash_leaf) PANIC // Invalid node type? Shouldn't get here. +%%mpt_hash_branch: + // stack: node_type, node_payload_ptr, retdest + POP + // stack: node_payload_ptr, retdest + PANIC // TODO + +%%mpt_hash_extension: + // stack: node_type, node_payload_ptr, retdest + POP + // stack: node_payload_ptr, retdest + PANIC // TODO + +%%mpt_hash_leaf: + // stack: node_type, node_payload_ptr, retdest + POP + // stack: node_payload_ptr, retdest + PUSH %%mpt_hash_leaf_after_hex_prefix // retdest + PUSH 1 // terminated + // stack: terminated, %%mpt_hash_leaf_after_hex_prefix, node_payload_ptr, retdest + DUP3 %add_const(1) %mload_trie_data // Load the packed_nibbles field, which is at index 1. + // stack: packed_nibbles, terminated, %%mpt_hash_leaf_after_hex_prefix, node_payload_ptr, retdest + DUP4 %mload_trie_data // Load the num_nibbles field, which is at index 0. + // stack: num_nibbles, packed_nibbles, terminated, %%mpt_hash_leaf_after_hex_prefix, node_payload_ptr, retdest + PUSH 9 // We start at 9 to leave room to prepend the largest possible RLP list header. + // stack: rlp_start, num_nibbles, packed_nibbles, terminated, %%mpt_hash_leaf_after_hex_prefix, node_payload_ptr, retdest + %jump(hex_prefix) +%%mpt_hash_leaf_after_hex_prefix: + // stack: rlp_pos, node_payload_ptr, retdest + SWAP1 + %add_const(2) // The value starts at index 2. + %stack (value_ptr, rlp_pos, retdest) + -> (rlp_pos, value_ptr, %%mpt_hash_leaf_after_encode_value, retdest) + %jump($encode_value) +%%mpt_hash_leaf_after_encode_value: + // stack: rlp_end_pos, retdest + %prepend_rlp_list_prefix + // stack: rlp_start_pos, rlp_len, retdest + PUSH $SEGMENT_RLP + PUSH 0 // kernel context + // stack: rlp_start_addr: 3, rlp_len, retdest + KECCAK_GENERAL + // stack: hash, retdest + SWAP + JUMP +%endmacro + mpt_hash_empty: %stack (node_type, node_payload_ptr, retdest) -> (retdest, @EMPTY_NODE_HASH) JUMP @@ -26,23 +80,3 @@ mpt_hash_hash: // stack: hash, retdest SWAP1 JUMP - -mpt_hash_branch: - // stack: node_type, node_payload_ptr, retdest - POP - // stack: node_payload_ptr, retdest - PANIC // TODO - -mpt_hash_extension: - // stack: node_type, node_payload_ptr, retdest - POP - // stack: node_payload_ptr, retdest - PANIC // TODO - -mpt_hash_leaf: - // stack: node_type, node_payload_ptr, retdest - POP - // stack: node_payload_ptr, retdest - DUP1 %mload_trie_data - // stack: node_nibbles, node_payload_ptr, retdest - PANIC // TODO diff --git a/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm b/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm index a1319748..043974a4 100644 --- a/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm +++ b/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm @@ -1,51 +1,104 @@ -// Computes the hex-prefix encoding of the given nibble list and termination -// flag. Writes the result to the @KERNEL_GENERAL segment of memory, and returns -// its length on the stack. -global hex_prefix: - // stack: num_nibbles, packed_nibbles, terminated, retdest - // We will iterate backwards, from i = num_nibbles/2 to i=0, so that we can - // take nibbles from the least-significant end of packed_nibbles. - PUSH 2 DUP2 DIV // i = num_nibbles / 2 - // stack: i, num_nibbles, packed_nibbles, terminated, retdest +// Computes the RLP encoding of the hex-prefix encoding of the given nibble list +// and termination flag. Writes the result to @SEGMENT_RLP_RAW starting at the +// given position, and returns the updated position, i.e. a pointer to the next +// unused offset. +// +// Pre stack: rlp_start_pos, num_nibbles, packed_nibbles, terminated, retdest +// Post stack: rlp_end_pos + +global hex_prefix_rlp: + // stack: rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + // We will iterate backwards, from i = num_nibbles / 2 to i = 0, so that we + // can take nibbles from the least-significant end of packed_nibbles. + PUSH 2 DUP3 DIV // i = num_nibbles / 2 + // stack: i, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + + // Compute the length of the hex-prefix string, in bytes: + // hp_len = num_nibbles / 2 + 1 = i + 1 + DUP1 %add_const(1) + // stack: hp_len, i, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + + // Write the RLP header. + DUP1 %gt_const(55) %jumpi(rlp_header_large) + DUP1 %gt_const(1) %jumpi(rlp_header_medium) + + // The hex-prefix is a single byte. It must be <= 127, since its first + // nibble only has two bits. So this is the "small" RLP string case, where + // the byte is its own RLP encoding. + // stack: hp_len, i, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + %jump(start_loop) + +rlp_header_medium: + // stack: hp_len, i, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + DUP1 // value = hp_len + DUP4 // offset = rlp_pos + %mstore_rlp + + // rlp_pos += 1 + SWAP2 %add_const(1) SWAP2 + + %jump(start_loop) + +rlp_header_large: + // stack: hp_len, i, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + // In practice hex-prefix length will never exceed 256, so the length of the + // length will always be 1 byte in this case. + + PUSH 1 // value = len_of_len = 1 + DUP4 // offset = rlp_pos + %mstore_rlp + + DUP1 // value = hp_len + DUP4 %add_const(1) // offset = rlp_pos + 1 + %mstore_rlp + + // rlp_pos += 2 + SWAP2 %add_const(2) SWAP2 + +start_loop: + // stack: hp_len, i, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + SWAP1 loop: + // stack: i, hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest // If i == 0, break to first_byte. DUP1 ISZERO %jumpi(first_byte) - // stack: i, num_nibbles, packed_nibbles, terminated, retdest - DUP3 // packed_nibbles + // stack: i, hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + DUP5 // packed_nibbles %and_const(0xFF) - // stack: byte_i, i, num_nibbles, packed_nibbles, terminated, retdest - DUP2 // i - %mstore_kernel_general - // stack: i, num_nibbles, packed_nibbles, terminated, retdest + // stack: byte_i, i, hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + DUP4 // rlp_pos + DUP3 // i + ADD // We'll write to offset rlp_pos + i + %mstore_rlp + + // stack: i, hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest %sub_const(1) - SWAP2 %shr_const(8) SWAP2 // packed_nibbles >>= 8 - // stack: i, num_nibbles, packed_nibbles, terminated, retdest + SWAP4 %shr_const(8) SWAP4 // packed_nibbles >>= 8 %jump(loop) first_byte: - // stack: 0, num_nibbles, first_nibble_or_zero, terminated, retdest + // stack: 0, hp_len, rlp_pos, num_nibbles, first_nibble_or_zero, terminated, retdest POP - DUP1 - // stack: num_nibbles, num_nibbles, first_nibble_or_zero, terminated, retdest - %div_const(2) - %add_const(1) - // stack: result_len, num_nibbles, first_nibble_or_zero, terminated, retdest - SWAP3 - // stack: terminated, num_nibbles, first_nibble_or_zero, result_len, retdest + // stack: hp_len, rlp_pos, num_nibbles, first_nibble_or_zero, terminated, retdest + DUP2 ADD + // stack: rlp_end_pos, rlp_pos, num_nibbles, first_nibble_or_zero, terminated, retdest + SWAP4 + // stack: terminated, rlp_pos, num_nibbles, first_nibble_or_zero, rlp_end_pos, retdest %mul_const(2) - SWAP1 - // stack: num_nibbles, terminated * 2, first_nibble_or_zero, result_len, retdest - %mod_const(2) + // stack: terminated * 2, rlp_pos, num_nibbles, first_nibble_or_zero, rlp_end_pos, retdest + %stack (terminated_x2, rlp_pos, num_nibbles, first_nibble_or_zero) + -> (num_nibbles, terminated_x2, first_nibble_or_zero, rlp_pos) + // stack: num_nibbles, terminated * 2, first_nibble_or_zero, rlp_pos, rlp_end_pos, retdest + %mod_const(2) // parity ADD - // stack: parity + terminated * 2, first_nibble_or_zero, result_len, retdest + // stack: parity + terminated * 2, first_nibble_or_zero, rlp_pos, rlp_end_pos, retdest %mul_const(16) ADD - // stack: 16 * (parity + terminated * 2) + first_nibble_or_zero, result_len, retdest - PUSH 0 - %mstore_kernel_general - - // stack: result_len, retdest + // stack: first_byte, rlp_pos, rlp_end_pos, retdest + SWAP1 + %mstore_rlp + // stack: rlp_end_pos, retdest SWAP1 JUMP diff --git a/evm/src/cpu/kernel/asm/rlp/encode.asm b/evm/src/cpu/kernel/asm/rlp/encode.asm index 7e296f9d..cd931037 100644 --- a/evm/src/cpu/kernel/asm/rlp/encode.asm +++ b/evm/src/cpu/kernel/asm/rlp/encode.asm @@ -90,6 +90,73 @@ encode_rlp_fixed_finish: SWAP1 JUMP +// Given an RLP list payload which starts at position 9 and ends at the given +// position, prepend the appropriate RLP list prefix. Returns the updated start +// position, as well as the length of the RLP data (including the newly-added +// prefix). +// +// (We sometimes start list payloads at position 9 because 9 is the length of +// the longest possible RLP list prefix.) +// +// Pre stack: end_pos, retdest +// Post stack: start_pos, rlp_len +global prepend_rlp_list_prefix: + // stack: end_pos, retdest + // Since the list payload starts at position 9, payload_len = end_pos - 9. + PUSH 9 DUP2 SUB + // stack: payload_len, end_pos, retdest + DUP1 %gt_const(55) + %jumpi(prepend_rlp_list_prefix_big) + + // If we got here, we have a small list, so we prepend 0xc0 + len at position 8. + // stack: payload_len, end_pos, retdest + %add_const(0xc0) + // stack: prefix_byte, end_pos, retdest + PUSH 8 // offset + %mstore_rlp + // stack: end_pos, retdest + %sub_const(8) + // stack: rlp_len, retdest + PUSH 8 // start_pos + %stack (start_pos, rlp_len, retdest) -> (retdest, start_pos, rlp_len) + JUMP + +prepend_rlp_list_prefix_big: + // We have a large list, so we prepend 0xf7 + len_of_len at position + // 8 - len_of_len, followed by the length itself. + // stack: payload_len, end_pos, retdest + DUP1 %num_bytes + // stack: len_of_len, payload_len, end_pos, retdest + DUP1 + PUSH 8 + SUB + // stack: start_pos, len_of_len, payload_len, end_pos, retdest + DUP2 DUP2 %mstore_rlp // rlp[start_pos] = len_of_len + DUP1 %add_const(1) // start_len_pos = start_pos + 1 + %stack (start_len_pos, start_pos, len_of_len, payload_len, end_pos, retdest) + -> (len_of_len, start_len_pos, payload_len, + prepend_rlp_list_prefix_big_done_writing_len, + start_pos, end_pos, retdest) + %jump(encode_rlp_fixed) +prepend_rlp_list_prefix_big_done_writing_len: + // stack: start_payload_pos, start_pos, end_pos, retdest + POP + // stack: start_pos, end_pos, retdest + DUP1 + SWAP2 + // stack: end_pos, start_pos, start_pos, retdest + SUB + // stack: rlp_len, start_pos, retdest + %stack (rlp_len, start_pos, retdest) -> (retdest, start_pos, rlp_len) + JUMP + +// Convenience macro to call prepend_rlp_list_prefix and return where we left off. +%macro prepend_rlp_list_prefix + %stack (start_pos) -> (start_pos, %%after) + %jump(prepend_rlp_list_prefix) +%%after: +%endmacro + // Get the number of bytes required to represent the given scalar. // The scalar is assumed to be non-zero, as small scalars like zero should // have already been handled with the small-scalar encoding. diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 04fc5ced..8a898cb3 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -161,10 +161,6 @@ impl<'a> Interpreter<'a> { &self.memory.context_memory[0].segments[Segment::TrieData as usize].content } - pub(crate) fn get_kernel_general_data(&self) -> &[U256] { - &self.memory.context_memory[0].segments[Segment::KernelGeneral as usize].content - } - pub(crate) fn get_rlp_memory(&self) -> Vec { self.memory.context_memory[self.context].segments[Segment::RlpRaw as usize] .content diff --git a/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs b/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs index 4318b560..76fe8014 100644 --- a/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs +++ b/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs @@ -5,20 +5,25 @@ use crate::cpu::kernel::interpreter::Interpreter; #[test] fn hex_prefix_even_nonterminated() -> Result<()> { - let hex_prefix = KERNEL.global_labels["hex_prefix"]; + let hex_prefix = KERNEL.global_labels["hex_prefix_rlp"]; let retdest = 0xDEADBEEFu32.into(); let terminated = 0.into(); let packed_nibbles = 0xABCDEF.into(); let num_nibbles = 6.into(); - let initial_stack = vec![retdest, terminated, packed_nibbles, num_nibbles]; + let rlp_pos = 0.into(); + let initial_stack = vec![retdest, terminated, packed_nibbles, num_nibbles, rlp_pos]; let mut interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack); interpreter.run()?; - assert_eq!(interpreter.stack(), vec![4.into()]); + assert_eq!(interpreter.stack(), vec![5.into()]); assert_eq!( - interpreter.get_kernel_general_data(), - vec![0.into(), 0xAB.into(), 0xCD.into(), 0xEF.into(),] + interpreter.get_rlp_memory(), + vec![ + 4, // length + 0, // neither flag is set + 0xAB, 0xCD, 0xEF + ] ); Ok(()) @@ -26,23 +31,53 @@ fn hex_prefix_even_nonterminated() -> Result<()> { #[test] fn hex_prefix_odd_terminated() -> Result<()> { - let hex_prefix = KERNEL.global_labels["hex_prefix"]; + let hex_prefix = KERNEL.global_labels["hex_prefix_rlp"]; let retdest = 0xDEADBEEFu32.into(); let terminated = 1.into(); let packed_nibbles = 0xABCDE.into(); let num_nibbles = 5.into(); - let initial_stack = vec![retdest, terminated, packed_nibbles, num_nibbles]; + let rlp_pos = 0.into(); + let initial_stack = vec![retdest, terminated, packed_nibbles, num_nibbles, rlp_pos]; let mut interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack); interpreter.run()?; - assert_eq!(interpreter.stack(), vec![3.into()]); + assert_eq!(interpreter.stack(), vec![4.into()]); assert_eq!( - interpreter.get_kernel_general_data(), + interpreter.get_rlp_memory(), vec![ - (terminated * 2 + 1u32) * 16 + 0xAu32, - 0xBC.into(), - 0xDE.into(), + 3, // length + (2 + 1) * 16 + 0xA, + 0xBC, + 0xDE, + ] + ); + + Ok(()) +} + +#[test] +fn hex_prefix_odd_terminated_tiny() -> Result<()> { + let hex_prefix = KERNEL.global_labels["hex_prefix_rlp"]; + + let retdest = 0xDEADBEEFu32.into(); + let terminated = 1.into(); + let packed_nibbles = 0xA.into(); + let num_nibbles = 1.into(); + let rlp_pos = 2.into(); + let initial_stack = vec![retdest, terminated, packed_nibbles, num_nibbles, rlp_pos]; + let mut interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack); + interpreter.run()?; + assert_eq!(interpreter.stack(), vec![3.into()]); + + assert_eq!( + interpreter.get_rlp_memory(), + vec![ + // Since rlp_pos = 2, we skipped over the first two bytes. + 0, + 0, + // No length prefix; this tiny string is its own RLP encoding. + (2 + 1) * 16 + 0xA, ] ); diff --git a/evm/src/cpu/kernel/tests/rlp.rs b/evm/src/cpu/kernel/tests/rlp/decode.rs similarity index 59% rename from evm/src/cpu/kernel/tests/rlp.rs rename to evm/src/cpu/kernel/tests/rlp/decode.rs index 37949e13..a1ca3609 100644 --- a/evm/src/cpu/kernel/tests/rlp.rs +++ b/evm/src/cpu/kernel/tests/rlp/decode.rs @@ -3,84 +3,6 @@ use anyhow::Result; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; -#[test] -fn test_encode_rlp_scalar_small() -> Result<()> { - let encode_rlp_scalar = KERNEL.global_labels["encode_rlp_scalar"]; - - let retdest = 0xDEADBEEFu32.into(); - let scalar = 42.into(); - let pos = 2.into(); - let initial_stack = vec![retdest, scalar, pos]; - let mut interpreter = Interpreter::new_with_kernel(encode_rlp_scalar, initial_stack); - - interpreter.run()?; - let expected_stack = vec![3.into()]; // pos' = pos + rlp_len = 2 + 1 - let expected_rlp = vec![0, 0, 42]; - assert_eq!(interpreter.stack(), expected_stack); - assert_eq!(interpreter.get_rlp_memory(), expected_rlp); - - Ok(()) -} - -#[test] -fn test_encode_rlp_scalar_medium() -> Result<()> { - let encode_rlp_scalar = KERNEL.global_labels["encode_rlp_scalar"]; - - let retdest = 0xDEADBEEFu32.into(); - let scalar = 0x12345.into(); - let pos = 2.into(); - let initial_stack = vec![retdest, scalar, pos]; - let mut interpreter = Interpreter::new_with_kernel(encode_rlp_scalar, initial_stack); - - interpreter.run()?; - let expected_stack = vec![6.into()]; // pos' = pos + rlp_len = 2 + 4 - let expected_rlp = vec![0, 0, 0x80 + 3, 0x01, 0x23, 0x45]; - assert_eq!(interpreter.stack(), expected_stack); - assert_eq!(interpreter.get_rlp_memory(), expected_rlp); - - Ok(()) -} - -#[test] -fn test_encode_rlp_160() -> Result<()> { - let encode_rlp_160 = KERNEL.global_labels["encode_rlp_160"]; - - let retdest = 0xDEADBEEFu32.into(); - let string = 0x12345.into(); - let pos = 0.into(); - let initial_stack = vec![retdest, string, pos]; - let mut interpreter = Interpreter::new_with_kernel(encode_rlp_160, initial_stack); - - interpreter.run()?; - let expected_stack = vec![(1 + 20).into()]; // pos' - #[rustfmt::skip] - let expected_rlp = vec![0x80 + 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x23, 0x45]; - assert_eq!(interpreter.stack(), expected_stack); - assert_eq!(interpreter.get_rlp_memory(), expected_rlp); - - Ok(()) -} - -#[test] -fn test_encode_rlp_256() -> Result<()> { - let encode_rlp_256 = KERNEL.global_labels["encode_rlp_256"]; - - let retdest = 0xDEADBEEFu32.into(); - let string = 0x12345.into(); - let pos = 0.into(); - let initial_stack = vec![retdest, string, pos]; - let mut interpreter = Interpreter::new_with_kernel(encode_rlp_256, initial_stack); - - interpreter.run()?; - let expected_stack = vec![(1 + 32).into()]; // pos' - #[rustfmt::skip] - let expected_rlp = vec![0x80 + 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x23, 0x45]; - assert_eq!(interpreter.stack(), expected_stack); - assert_eq!(interpreter.get_rlp_memory(), expected_rlp); - - Ok(()) -} - #[test] fn test_decode_rlp_string_len_short() -> Result<()> { let decode_rlp_string_len = KERNEL.global_labels["decode_rlp_string_len"]; diff --git a/evm/src/cpu/kernel/tests/rlp/encode.rs b/evm/src/cpu/kernel/tests/rlp/encode.rs new file mode 100644 index 00000000..bacf32fb --- /dev/null +++ b/evm/src/cpu/kernel/tests/rlp/encode.rs @@ -0,0 +1,110 @@ +use anyhow::Result; + +use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::kernel::interpreter::Interpreter; + +#[test] +fn test_encode_rlp_scalar_small() -> Result<()> { + let encode_rlp_scalar = KERNEL.global_labels["encode_rlp_scalar"]; + + let retdest = 0xDEADBEEFu32.into(); + let scalar = 42.into(); + let pos = 2.into(); + let initial_stack = vec![retdest, scalar, pos]; + let mut interpreter = Interpreter::new_with_kernel(encode_rlp_scalar, initial_stack); + + interpreter.run()?; + let expected_stack = vec![3.into()]; // pos' = pos + rlp_len = 2 + 1 + let expected_rlp = vec![0, 0, 42]; + assert_eq!(interpreter.stack(), expected_stack); + assert_eq!(interpreter.get_rlp_memory(), expected_rlp); + + Ok(()) +} + +#[test] +fn test_encode_rlp_scalar_medium() -> Result<()> { + let encode_rlp_scalar = KERNEL.global_labels["encode_rlp_scalar"]; + + let retdest = 0xDEADBEEFu32.into(); + let scalar = 0x12345.into(); + let pos = 2.into(); + let initial_stack = vec![retdest, scalar, pos]; + let mut interpreter = Interpreter::new_with_kernel(encode_rlp_scalar, initial_stack); + + interpreter.run()?; + let expected_stack = vec![6.into()]; // pos' = pos + rlp_len = 2 + 4 + let expected_rlp = vec![0, 0, 0x80 + 3, 0x01, 0x23, 0x45]; + assert_eq!(interpreter.stack(), expected_stack); + assert_eq!(interpreter.get_rlp_memory(), expected_rlp); + + Ok(()) +} + +#[test] +fn test_encode_rlp_160() -> Result<()> { + let encode_rlp_160 = KERNEL.global_labels["encode_rlp_160"]; + + let retdest = 0xDEADBEEFu32.into(); + let string = 0x12345.into(); + let pos = 0.into(); + let initial_stack = vec![retdest, string, pos]; + let mut interpreter = Interpreter::new_with_kernel(encode_rlp_160, initial_stack); + + interpreter.run()?; + let expected_stack = vec![(1 + 20).into()]; // pos' + #[rustfmt::skip] + let expected_rlp = vec![0x80 + 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x23, 0x45]; + assert_eq!(interpreter.stack(), expected_stack); + assert_eq!(interpreter.get_rlp_memory(), expected_rlp); + + Ok(()) +} + +#[test] +fn test_encode_rlp_256() -> Result<()> { + let encode_rlp_256 = KERNEL.global_labels["encode_rlp_256"]; + + let retdest = 0xDEADBEEFu32.into(); + let string = 0x12345.into(); + let pos = 0.into(); + let initial_stack = vec![retdest, string, pos]; + let mut interpreter = Interpreter::new_with_kernel(encode_rlp_256, initial_stack); + + interpreter.run()?; + let expected_stack = vec![(1 + 32).into()]; // pos' + #[rustfmt::skip] + let expected_rlp = vec![0x80 + 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x23, 0x45]; + assert_eq!(interpreter.stack(), expected_stack); + assert_eq!(interpreter.get_rlp_memory(), expected_rlp); + + Ok(()) +} + +#[test] +fn test_prepend_rlp_list_prefix_small() -> Result<()> { + let prepend_rlp_list_prefix = KERNEL.global_labels["prepend_rlp_list_prefix"]; + + let retdest = 0xDEADBEEFu32.into(); + let end_pos = (9 + 5).into(); + let initial_stack = vec![retdest, end_pos]; + let mut interpreter = Interpreter::new_with_kernel(prepend_rlp_list_prefix, initial_stack); + interpreter.set_rlp_memory(vec![ + // Nine 0s to leave room for the longest possible RLP list prefix. + 0, 0, 0, 0, 0, 0, 0, 0, 0, + // The actual RLP list payload, consisting of 5 tiny strings. + 1, 2, 3, 4, 5, + ]); + + interpreter.run()?; + + let expected_rlp_len = 6.into(); + let expected_start_pos = 8.into(); + let expected_stack = vec![expected_rlp_len, expected_start_pos]; + let expected_rlp = vec![0, 0, 0, 0, 0, 0, 0, 0, 0xc0 + 5, 1, 2, 3, 4, 5]; + + assert_eq!(interpreter.stack(), expected_stack); + assert_eq!(interpreter.get_rlp_memory(), expected_rlp); + + Ok(()) +} diff --git a/evm/src/cpu/kernel/tests/rlp/mod.rs b/evm/src/cpu/kernel/tests/rlp/mod.rs new file mode 100644 index 00000000..bc9bde59 --- /dev/null +++ b/evm/src/cpu/kernel/tests/rlp/mod.rs @@ -0,0 +1,2 @@ +mod decode; +mod encode;