From 539152d76703b038f294b56f737626879543649a Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Sun, 14 Aug 2022 09:12:16 -0700 Subject: [PATCH] RLP encoding functions --- evm/src/cpu/kernel/asm/memory/packing.asm | 2 + evm/src/cpu/kernel/asm/rlp/encode.asm | 126 ++++++++++++++++++++-- evm/src/cpu/kernel/interpreter.rs | 11 +- evm/src/cpu/kernel/tests/rlp.rs | 78 ++++++++++++++ 4 files changed, 207 insertions(+), 10 deletions(-) diff --git a/evm/src/cpu/kernel/asm/memory/packing.asm b/evm/src/cpu/kernel/asm/memory/packing.asm index 35bc673e..cb498464 100644 --- a/evm/src/cpu/kernel/asm/memory/packing.asm +++ b/evm/src/cpu/kernel/asm/memory/packing.asm @@ -6,6 +6,8 @@ global mload_packing: // TODO // stack: value +// Pre stack: context, segment, offset, value, len, retdest +// Post stack: (empty) global mstore_unpacking: // stack: context, segment, offset, value, len, retdest // We will enumerate i in (32 - len)..32. diff --git a/evm/src/cpu/kernel/asm/rlp/encode.asm b/evm/src/cpu/kernel/asm/rlp/encode.asm index 58cb9230..7e296f9d 100644 --- a/evm/src/cpu/kernel/asm/rlp/encode.asm +++ b/evm/src/cpu/kernel/asm/rlp/encode.asm @@ -1,17 +1,127 @@ // RLP-encode a scalar, i.e. a variable-length integer. // Pre stack: pos, scalar, retdest -// Post stack: (empty) +// Post stack: pos global encode_rlp_scalar: - PANIC // TODO: implement + // stack: pos, scalar, retdest + // If scalar > 0x7f, this is the "medium" case. + DUP2 + %gt_const(0x7f) + %jumpi(encode_rlp_scalar_medium) -// RLP-encode a fixed-length 160-bit string. Assumes string < 2^160. + // This is the "small" case, where the value is its own encoding. + // stack: pos, scalar, retdest + %stack (pos, scalar) -> (pos, scalar, pos) + // stack: pos, scalar, pos, retdest + %mstore_current(@SEGMENT_RLP_RAW) + // stack: pos, retdest + %add_const(1) + // stack: pos', retdest + SWAP1 + JUMP + +encode_rlp_scalar_medium: + // This is the "medium" case, where we write 0x80 + len followed by the + // (big-endian) scalar bytes. We first compute the minimal number of bytes + // needed to represent this scalar, then treat it as if it was a fixed- + // length string with that length. + // stack: pos, scalar, retdest + DUP2 + %num_bytes + // stack: scalar_bytes, pos, scalar, retdest + %jump(encode_rlp_fixed) + +// Convenience macro to call encode_rlp_scalar and return where we left off. +%macro encode_rlp_scalar + %stack (pos, scalar) -> (pos, scalar, %%after) + %jump(encode_rlp_scalar) +%%after: +%endmacro + +// RLP-encode a fixed-length 160 bit (20 byte) string. Assumes string < 2^160. // Pre stack: pos, string, retdest -// Post stack: (empty) +// Post stack: pos global encode_rlp_160: - PANIC // TODO: implement + PUSH 20 + %jump(encode_rlp_fixed) -// RLP-encode a fixed-length 256-bit string. +// Convenience macro to call encode_rlp_160 and return where we left off. +%macro encode_rlp_160 + %stack (pos, string) -> (pos, string, %%after) + %jump(encode_rlp_160) +%%after: +%endmacro + +// RLP-encode a fixed-length 256 bit (32 byte) string. // Pre stack: pos, string, retdest -// Post stack: (empty) +// Post stack: pos global encode_rlp_256: - PANIC // TODO: implement + PUSH 32 + %jump(encode_rlp_fixed) + +// Convenience macro to call encode_rlp_256 and return where we left off. +%macro encode_rlp_256 + %stack (pos, string) -> (pos, string, %%after) + %jump(encode_rlp_256) +%%after: +%endmacro + +// RLP-encode a fixed-length string with the given byte length. Assumes string < 2^(8 * len). +encode_rlp_fixed: + // stack: len, pos, string, retdest + DUP1 + %add_const(0x80) + // stack: first_byte, len, pos, string, retdest + DUP3 + // stack: pos, first_byte, len, pos, string, retdest + %mstore_current(@SEGMENT_RLP_RAW) + // stack: len, pos, string, retdest + SWAP1 + %add_const(1) // increment pos + // stack: pos, len, string, retdest + %stack (pos, len, string) -> (@SEGMENT_RLP_RAW, pos, string, len, encode_rlp_fixed_finish, pos, len) + GET_CONTEXT + // stack: context, segment, pos, string, len, encode_rlp_fixed, pos, retdest + %jump(mstore_unpacking) + +encode_rlp_fixed_finish: + // stack: pos, len, retdest + ADD + // stack: pos', retdest + SWAP1 + JUMP + +// 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. +num_bytes: + // stack: x, retdest + PUSH 0 // i + // stack: i, x, retdest + +num_bytes_loop: + // stack: i, x, retdest + // If x[i] != 0, break. + DUP2 DUP2 + // stack: i, x, i, x, retdest + BYTE + // stack: x[i], i, x, retdest + %jumpi(num_bytes_finish) + // stack: i, x, retdest + + %add_const(1) + // stack: i', x, retdest + %jump(num_bytes_loop) + +num_bytes_finish: + // stack: i, x, retdest + PUSH 32 + SUB + %stack (num_bytes, x, retdest) -> (retdest, num_bytes) + JUMP + +// Convenience macro to call num_bytes and return where we left off. +%macro num_bytes + %stack (x) -> (x, %%after) + %jump(num_bytes) +%%after: +%endmacro diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 45208d61..17be0523 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -146,8 +146,16 @@ impl<'a> Interpreter<'a> { &self.memory.context_memory[0].segments[Segment::TxnData as usize].content } + pub(crate) fn get_rlp_memory(&self) -> Vec { + self.memory.context_memory[self.context].segments[Segment::RlpRaw as usize] + .content + .iter() + .map(|x| x.as_u32() as u8) + .collect() + } + pub(crate) fn set_rlp_memory(&mut self, rlp: Vec) { - self.memory.context_memory[0].segments[Segment::RlpRaw as usize].content = + self.memory.context_memory[self.context].segments[Segment::RlpRaw as usize].content = rlp.into_iter().map(U256::from).collect(); } @@ -386,7 +394,6 @@ impl<'a> Interpreter<'a> { } fn run_byte(&mut self) { - dbg!("byte"); let i = self.pop(); let x = self.pop(); let result = if i > 32.into() { diff --git a/evm/src/cpu/kernel/tests/rlp.rs b/evm/src/cpu/kernel/tests/rlp.rs index a1ca3609..37949e13 100644 --- a/evm/src/cpu/kernel/tests/rlp.rs +++ b/evm/src/cpu/kernel/tests/rlp.rs @@ -3,6 +3,84 @@ 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"];