From e70e4fca06bffc2c7a210aae00a773c9f6a9a543 Mon Sep 17 00:00:00 2001 From: Daniel Lubarov Date: Mon, 3 Apr 2023 21:35:08 -0700 Subject: [PATCH] Doubly RLP-encode storage values --- evm/src/cpu/kernel/aggregator.rs | 1 + .../asm/mpt/hash/hash_trie_specific.asm | 13 +-- evm/src/cpu/kernel/asm/rlp/encode.asm | 79 +++++---------- .../cpu/kernel/asm/rlp/encode_rlp_scalar.asm | 99 +++++++++++++++++++ 4 files changed, 131 insertions(+), 61 deletions(-) create mode 100644 evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm diff --git a/evm/src/cpu/kernel/aggregator.rs b/evm/src/cpu/kernel/aggregator.rs index 023a0c88..b45a7e1a 100644 --- a/evm/src/cpu/kernel/aggregator.rs +++ b/evm/src/cpu/kernel/aggregator.rs @@ -109,6 +109,7 @@ pub(crate) fn combined_kernel() -> Kernel { include_str!("asm/mpt/util.asm"), include_str!("asm/rlp/decode.asm"), include_str!("asm/rlp/encode.asm"), + include_str!("asm/rlp/encode_rlp_scalar.asm"), include_str!("asm/rlp/encode_rlp_string.asm"), include_str!("asm/rlp/num_bytes.asm"), include_str!("asm/rlp/read_to_memory.asm"), diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm index 3662ee04..07b3e674 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash_trie_specific.asm @@ -19,7 +19,7 @@ global mpt_hash_storage_trie: %jump(mpt_hash) %macro mpt_hash_storage_trie - PUSH %%after + %stack (node_ptr) -> (node_ptr, %%after) %jump(mpt_hash_storage_trie) %%after: %endmacro @@ -83,12 +83,9 @@ global encode_account: // stack: balance, rlp_pos_4, value_ptr, retdest SWAP1 %encode_rlp_scalar // stack: rlp_pos_5, value_ptr, retdest - PUSH encode_account_after_hash_storage_trie - PUSH encode_storage_value - DUP4 %add_const(2) %mload_trie_data // storage_root_ptr = value[2] - // stack: storage_root_ptr, encode_storage_value, encode_account_after_hash_storage_trie, rlp_pos_5, value_ptr, retdest - %jump(mpt_hash) -encode_account_after_hash_storage_trie: + DUP2 %add_const(2) %mload_trie_data // storage_root_ptr = value[2] + // stack: storage_root_ptr, rlp_pos_5, value_ptr, retdest + %mpt_hash_storage_trie // stack: storage_root_digest, rlp_pos_5, value_ptr, retdest SWAP1 %encode_rlp_256 // stack: rlp_pos_6, value_ptr, retdest @@ -113,7 +110,7 @@ global encode_storage_value: // which seems to imply that this should be %encode_rlp_256. But %encode_rlp_scalar // causes the tests to pass, so it seems storage values should be treated as variable- // length after all. - %encode_rlp_scalar + %doubly_encode_rlp_scalar // stack: rlp_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 8254e3e8..fd42fe52 100644 --- a/evm/src/cpu/kernel/asm/rlp/encode.asm +++ b/evm/src/cpu/kernel/asm/rlp/encode.asm @@ -1,55 +1,3 @@ -// RLP-encode a scalar, i.e. a variable-length integer. -// Pre stack: pos, scalar, retdest -// Post stack: pos -global encode_rlp_scalar: - // stack: pos, scalar, retdest - // If scalar > 0x7f, this is the "medium" case. - DUP2 - %gt_const(0x7f) - %jumpi(encode_rlp_scalar_medium) - - // Else, if scalar != 0, this is the "small" case, where the value is its own encoding. - DUP2 %jumpi(encode_rlp_scalar_small) - - // scalar = 0, so BE(scalar) is the empty string, which RLP encodes as a single byte 0x80. - // stack: pos, scalar, retdest - %stack (pos, scalar) -> (pos, 0x80, pos) - %mstore_rlp - // stack: pos, retdest - %increment - // stack: pos', retdest - SWAP1 - JUMP - -encode_rlp_scalar_small: - // stack: pos, scalar, retdest - %stack (pos, scalar) -> (pos, scalar, pos) - // stack: pos, scalar, pos, retdest - %mstore_rlp - // stack: pos, retdest - %increment - // 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: pos @@ -79,7 +27,7 @@ global encode_rlp_256: %endmacro // RLP-encode a fixed-length string with the given byte length. Assumes string < 2^(8 * len). -encode_rlp_fixed: +global encode_rlp_fixed: // stack: len, pos, string, retdest DUP1 %add_const(0x80) @@ -99,6 +47,31 @@ encode_rlp_fixed_finish: SWAP1 JUMP +// Doubly-RLP-encode a fixed-length string with the given byte length. +// I.e. writes encode(encode(string). Assumes string < 2^(8 * len). +global doubly_encode_rlp_fixed: + // stack: len, pos, string, retdest + DUP1 + %add_const(0x81) + // stack: first_byte, len, pos, string, retdest + DUP3 + // stack: pos, first_byte, len, pos, string, retdest + %mstore_rlp + // stack: len, pos, string, retdest + DUP1 + %add_const(0x80) + // stack: second_byte, len, original_pos, string, retdest + DUP3 %increment + // stack: pos', second_byte, len, pos, string, retdest + %mstore_rlp + // stack: len, pos, string, retdest + SWAP1 + %add_const(2) // advance past the two prefix bytes + // stack: pos'', len, string, retdest + %stack (pos, len, string) -> (pos, string, len, encode_rlp_fixed_finish) + // stack: context, segment, pos'', string, len, encode_rlp_fixed_finish, retdest + %jump(mstore_unpacking_rlp) + // Writes the RLP prefix for a string of the given length. This does not handle // the trivial encoding of certain single-byte strings, as handling that would // require access to the actual string, while this method only accesses its diff --git a/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm b/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm new file mode 100644 index 00000000..fdfec501 --- /dev/null +++ b/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm @@ -0,0 +1,99 @@ +// RLP-encode a scalar, i.e. a variable-length integer. +// Pre stack: pos, scalar, retdest +// Post stack: pos +global encode_rlp_scalar: + // stack: pos, scalar, retdest + // If scalar > 0x7f, this is the "medium" case. + DUP2 + %gt_const(0x7f) + %jumpi(encode_rlp_scalar_medium) + + // Else, if scalar != 0, this is the "small" case, where the value is its own encoding. + DUP2 %jumpi(encode_rlp_scalar_small) + + // scalar = 0, so BE(scalar) is the empty string, which RLP encodes as a single byte 0x80. + // stack: pos, scalar, retdest + %stack (pos, scalar) -> (pos, 0x80, pos) + %mstore_rlp + // stack: pos, retdest + %increment + // 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) + +// Doubly-RLP-encode a scalar, i.e. return encode(encode(scalar)). +// Pre stack: pos, scalar, retdest +// Post stack: pos +global doubly_encode_rlp_scalar: + // stack: pos, scalar, retdest + // If scalar > 0x7f, this is the "medium" case. + DUP2 + %gt_const(0x7f) + %jumpi(doubly_encode_rlp_scalar_medium) + + // Else, if scalar != 0, this is the "small" case, where the value is its own encoding. + DUP2 %jumpi(encode_rlp_scalar_small) + + // scalar = 0, so BE(scalar) is the empty string, encode(scalar) = 0x80, and encode(encode(scalar)) = 0x8180. + // stack: pos, scalar, retdest + %stack (pos, scalar) -> (pos, 0x81, pos, 0x80, pos) + %mstore_rlp + // stack: pos, 0x80, pos, retdest + %increment + %mstore_rlp + // stack: pos, retdest + %increment + // stack: pos, retdest + SWAP1 + JUMP + +doubly_encode_rlp_scalar_medium: + // This is the "medium" case, where + // encode(scalar) = [0x80 + len] || BE(scalar) + // and so + // encode(encode(scalar)) = [0x80 + len + 1] || [0x80 + len] || BE(scalar) + // We first compute the length of the scalar with %num_bytes, then treat the scalar 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(doubly_encode_rlp_fixed) + +// The "small" case of RLP-encoding a scalar, where the value is its own encoding. +// This can be used for both for singly encoding or doubly encoding, since encode(encode(x)) = encode(x) = x. +encode_rlp_scalar_small: + // stack: pos, scalar, retdest + %stack (pos, scalar) -> (pos, scalar, pos) + // stack: pos, scalar, pos, retdest + %mstore_rlp + // stack: pos, retdest + %increment + // stack: pos', retdest + SWAP1 + JUMP + +// 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 + +// Convenience macro to call doubly_encode_rlp_scalar and return where we left off. +%macro doubly_encode_rlp_scalar + %stack (pos, scalar) -> (pos, scalar, %%after) + %jump(doubly_encode_rlp_scalar) +%%after: +%endmacro