diff --git a/evm/src/cpu/kernel/asm/main.asm b/evm/src/cpu/kernel/asm/main.asm index 42e326a0..27047812 100644 --- a/evm/src/cpu/kernel/asm/main.asm +++ b/evm/src/cpu/kernel/asm/main.asm @@ -15,6 +15,9 @@ global main: // Initialize the RLP DATA pointer to its initial position (ctx == virt == 0, segment = RLP) PUSH @SEGMENT_RLP_RAW %mstore_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) + + // Encode constant nodes + %initialize_rlp_segment // Initialize the state, transaction and receipt trie root pointers. PROVER_INPUT(trie_ptr::state) diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm index dc1a9392..29348f97 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm @@ -47,10 +47,9 @@ mpt_hash_hash_rlp_after_unpacking: // The result is given as a (value, length) pair, where the length is given // in bytes. // -// Pre stack: node_ptr, encode_value, retdest -// Post stack: result, result_len +// Pre stack: node_ptr, encode_value, cur_len, retdest +// Post stack: result, result_len, cur_len global encode_or_hash_node: - // stack: node_ptr, encode_value, cur_len, retdest DUP1 %mload_trie_data // Check if we're dealing with a concrete node, i.e. not a hash node. @@ -118,19 +117,8 @@ encode_node: global encode_node_empty: // stack: node_type, node_payload_ptr, encode_value, cur_len, retdest - // Then length of `TrieData` is unchanged here. %pop3 - // stack: cur_len, retdest - // An empty node is encoded as a single byte, 0x80, which is the RLP encoding of the empty string. - // TODO: Write this byte just once to RLP memory, then we can always return (0, 1). - %alloc_rlp_block - // stack: rlp_start, cur_len, retdest - PUSH 0x80 - // stack: 0x80, rlp_start, cur_len, retdest - DUP2 - // stack: rlp_start, 0x80, rlp_start, cur_len, retdest - %mstore_rlp - %stack (rlp_start, cur_len, retdest) -> (retdest, rlp_start, 1, cur_len) + %stack (cur_len, retdest) -> (retdest, @ENCODED_EMPTY_NODE_POS, 1, cur_len) JUMP global encode_node_branch: @@ -141,33 +129,19 @@ global encode_node_branch: SWAP2 %add_const(18) SWAP2 // stack: node_payload_ptr, encode_value, cur_len, retdest - // Get the next unused offset within the encoded child buffers. - // Then immediately increment the next unused offset by 16, so any - // recursive calls will use nonoverlapping offsets. - // TODO: Allocate a block of RLP memory instead? - %mload_global_metadata(@GLOBAL_METADATA_TRIE_ENCODED_CHILD_SIZE) - DUP1 %add_const(16) - %mstore_global_metadata(@GLOBAL_METADATA_TRIE_ENCODED_CHILD_SIZE) - // stack: base_offset, node_payload_ptr, encode_value, cur_len, retdest - // We will call encode_or_hash_node on each child. For the i'th child, we - // will store the result in SEGMENT_TRIE_ENCODED_CHILD[base + i], and its length in - // SEGMENT_TRIE_ENCODED_CHILD_LEN[base + i]. + // Allocate a block of RLP memory + %alloc_rlp_block DUP1 + // stack: rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len retdest + + // Call encode_or_hash_node on each child %encode_child(0) %encode_child(1) %encode_child(2) %encode_child(3) %encode_child(4) %encode_child(5) %encode_child(6) %encode_child(7) %encode_child(8) %encode_child(9) %encode_child(10) %encode_child(11) %encode_child(12) %encode_child(13) %encode_child(14) %encode_child(15) - // stack: base_offset, node_payload_ptr, encode_value, cur_len, retdest - // Now, append each child to our RLP tape. - %alloc_rlp_block DUP1 - // stack: rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest - %append_child(0) %append_child(1) %append_child(2) %append_child(3) - %append_child(4) %append_child(5) %append_child(6) %append_child(7) - %append_child(8) %append_child(9) %append_child(10) %append_child(11) - %append_child(12) %append_child(13) %append_child(14) %append_child(15) - // stack: rlp_pos', rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest + // stack: rlp_pos', rlp_start, node_payload_ptr, encode_value, cur_len, retdest - %stack (rlp_pos, rlp_start, base_offset, node_payload_ptr) + %stack (rlp_pos, rlp_start, node_payload_ptr) -> (node_payload_ptr, rlp_pos, rlp_start) %add_const(16) // stack: value_ptr_ptr, rlp_pos', rlp_start, encode_value, cur_len, retdest @@ -199,48 +173,36 @@ encode_node_branch_prepend_prefix: -> (retdest, rlp_prefix_start, rlp_len, cur_len) JUMP + // Part of the encode_node_branch function. Encodes the i'th child. -// Stores the result in SEGMENT_TRIE_ENCODED_CHILD[base + i], and its length in -// SEGMENT_TRIE_ENCODED_CHILD_LEN[base + i]. %macro encode_child(i) - // stack: base_offset, node_payload_ptr, encode_value, cur_len, retdest + // stack: rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest PUSH %%after_encode - DUP4 DUP4 - // stack: node_payload_ptr, encode_value, %%after_encode, base_offset, node_payload_ptr, encode_value, cur_len, retdest + DUP6 DUP6 DUP6 + // stack: node_payload_ptr, encode_value, cur_len, %%after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest %add_const($i) %mload_trie_data - // stack: child_i_ptr, encode_value, %%after_encode, base_offset, node_payload_ptr, encode_value, cur_len, retdest - %stack(child_i_ptr, encode_value, after_encode, base_offset, node_payload_ptr, encode_value, cur_len) -> (child_i_ptr, encode_value, cur_len, after_encode, base_offset, node_payload_ptr, encode_value) + // stack: child_i_ptr, encode_value, cur_len, %%after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest + %stack + (child_i_ptr, encode_value, cur_len, after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest) -> + (child_i_ptr, encode_value, cur_len, after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest) %jump(encode_or_hash_node) %%after_encode: - // stack: result, result_len, cur_len, base_offset, node_payload_ptr, encode_value, retdest - %stack(result, result_len, cur_len, base_offset, node_payload_ptr, encode_value) -> (result, result_len, base_offset, node_payload_ptr, encode_value, cur_len) - DUP3 %add_const($i) %mstore_kernel(@SEGMENT_TRIE_ENCODED_CHILD) - // stack: result_len, base_offset, node_payload_ptr, encode_value, cur_len, retdest - DUP2 %add_const($i) %mstore_kernel(@SEGMENT_TRIE_ENCODED_CHILD_LEN) - // stack: base_offset, node_payload_ptr, encode_value, cur_len, retdest -%endmacro - -// Part of the encode_node_branch function. Appends the i'th child's RLP. -%macro append_child(i) - // stack: rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest - DUP3 %add_const($i) %mload_kernel(@SEGMENT_TRIE_ENCODED_CHILD) // load result - DUP4 %add_const($i) %mload_kernel(@SEGMENT_TRIE_ENCODED_CHILD_LEN) // load result_len - // stack: result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest + // stack: result, result_len, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest // If result_len != 32, result is raw RLP, with an appropriate RLP prefix already. - DUP1 %sub_const(32) %jumpi(%%unpack) + SWAP1 DUP1 %sub_const(32) %jumpi(%%unpack) // Otherwise, result is a hash, and we need to add the prefix 0x80 + 32 = 160. - // stack: result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest + // stack: result_len, result, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest PUSH 160 - DUP4 // rlp_pos + DUP5 // rlp_pos %mstore_rlp - SWAP2 %increment SWAP2 // rlp_pos += 1 + SWAP3 %increment SWAP3 // rlp_pos += 1 %%unpack: - %stack (result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest) + %stack (result_len, result, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest) -> (rlp_pos, result, result_len, %%after_unpacking, - rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest) + rlp_start, node_payload_ptr, encode_value, cur_len, retdest) %jump(mstore_unpacking) %%after_unpacking: - // stack: rlp_pos', rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest + // stack: rlp_pos', rlp_start, node_payload_ptr, encode_value, cur_len, retdest %endmacro global encode_node_extension: 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 84fbb853..6abca0b6 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 @@ -101,6 +101,10 @@ global encode_account: DUP3 %add_const(2) %mload_trie_data // storage_root_ptr = value[2] // stack: storage_root_ptr, cur_len, rlp_pos_5, value_ptr, cur_len, retdest + + PUSH debug_after_hash_storage_trie + POP + // Hash storage trie. %mpt_hash_storage_trie // stack: storage_root_digest, new_len, rlp_pos_5, value_ptr, cur_len, retdest diff --git a/evm/src/cpu/kernel/asm/mpt/util.asm b/evm/src/cpu/kernel/asm/mpt/util.asm index 80e5c6f7..27418b17 100644 --- a/evm/src/cpu/kernel/asm/mpt/util.asm +++ b/evm/src/cpu/kernel/asm/mpt/util.asm @@ -10,6 +10,12 @@ // stack: (empty) %endmacro +%macro initialize_rlp_segment + PUSH 0x80 + PUSH @ENCODED_EMPTY_NODE_POS + %mstore_rlp +%endmacro + %macro alloc_rlp_block // stack: (empty) %mload_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) @@ -17,7 +23,7 @@ // In our model it's fine to use memory in a sparse way, as long as the gaps aren't larger than // 2^16 or so. So instead of the caller specifying the size of the block they need, we'll just // allocate 0x10000 = 2^16 bytes, much larger than any RLP blob the EVM could possibly create. - DUP1 %add_const(0x10000) + DUP1 %add_const(@MAX_RLP_BLOB_SIZE) // stack: block_end, block_start %mstore_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) // stack: block_start diff --git a/evm/src/cpu/kernel/constants/global_metadata.rs b/evm/src/cpu/kernel/constants/global_metadata.rs index 4669674e..0b3f6648 100644 --- a/evm/src/cpu/kernel/constants/global_metadata.rs +++ b/evm/src/cpu/kernel/constants/global_metadata.rs @@ -37,10 +37,6 @@ pub(crate) enum GlobalMetadata { TransactionTrieRootDigestAfter, ReceiptTrieRootDigestAfter, - /// The sizes of the `TrieEncodedChild` and `TrieEncodedChildLen` buffers. In other words, the - /// next available offset in these buffers. - TrieEncodedChildSize, - // Block metadata. BlockBeneficiary, BlockTimestamp, @@ -98,7 +94,7 @@ pub(crate) enum GlobalMetadata { } impl GlobalMetadata { - pub(crate) const COUNT: usize = 48; + pub(crate) const COUNT: usize = 47; /// Unscales this virtual offset by their respective `Segment` value. pub(crate) const fn unscale(&self) -> usize { @@ -120,7 +116,6 @@ impl GlobalMetadata { Self::StateTrieRootDigestAfter, Self::TransactionTrieRootDigestAfter, Self::ReceiptTrieRootDigestAfter, - Self::TrieEncodedChildSize, Self::BlockBeneficiary, Self::BlockTimestamp, Self::BlockNumber, @@ -174,7 +169,6 @@ impl GlobalMetadata { Self::StateTrieRootDigestAfter => "GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER", Self::TransactionTrieRootDigestAfter => "GLOBAL_METADATA_TXN_TRIE_DIGEST_AFTER", Self::ReceiptTrieRootDigestAfter => "GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_AFTER", - Self::TrieEncodedChildSize => "GLOBAL_METADATA_TRIE_ENCODED_CHILD_SIZE", Self::BlockBeneficiary => "GLOBAL_METADATA_BLOCK_BENEFICIARY", Self::BlockTimestamp => "GLOBAL_METADATA_BLOCK_TIMESTAMP", Self::BlockNumber => "GLOBAL_METADATA_BLOCK_NUMBER", diff --git a/evm/src/cpu/kernel/constants/mod.rs b/evm/src/cpu/kernel/constants/mod.rs index 451d7beb..f6f021db 100644 --- a/evm/src/cpu/kernel/constants/mod.rs +++ b/evm/src/cpu/kernel/constants/mod.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use ethereum_types::U256; use hex_literal::hex; +use static_assertions::const_assert; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; @@ -89,12 +90,23 @@ pub(crate) fn evm_constants() -> HashMap { c } -const MISC_CONSTANTS: [(&str, [u8; 32]); 1] = [ +const MISC_CONSTANTS: [(&str, [u8; 32]); 3] = [ // Base for limbs used in bignum arithmetic. ( "BIGNUM_LIMB_BASE", hex!("0000000000000000000000000000000100000000000000000000000000000000"), ), + // Position in SEGMENT_RLP_RAW where the empty node encoding is stored. It is + // equal to u32::MAX + @SEGMENT_RLP_RAW so that all rlp pointers are much smaller than that. + ( + "ENCODED_EMPTY_NODE_POS", + hex!("0000000000000000000000000000000000000000000000000000000CFFFFFFFF"), + ), + // 0x10000 = 2^16 bytes, much larger than any RLP blob the EVM could possibly create. + ( + "MAX_RLP_BLOB_SIZE", + hex!("0000000000000000000000000000000000000000000000000000000000010000"), + ), ]; const HASH_CONSTANTS: [(&str, [u8; 32]); 2] = [ diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 4dc887de..b99ccaeb 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -140,12 +140,14 @@ enum InterpreterMemOpKind { impl<'a> Interpreter<'a> { pub(crate) fn new_with_kernel(initial_offset: usize, initial_stack: Vec) -> Self { - Self::new( + let mut result = Self::new( &KERNEL.code, initial_offset, initial_stack, &KERNEL.prover_inputs, - ) + ); + result.initialize_rlp_segment(); + result } pub(crate) fn new( @@ -1193,6 +1195,14 @@ impl<'a> Interpreter<'a> { } self.generation_state.registers.context = context; } + + /// Writes the encoding of 0 to position @ENCODED_EMPTY_NODE_POS. + pub(crate) fn initialize_rlp_segment(&mut self) { + self.generation_state.memory.set( + MemoryAddress::new(0, Segment::RlpRaw, 0xFFFFFFFF), + 128.into(), + ) + } } // Computes the two's complement of the given integer. diff --git a/evm/src/memory/segments.rs b/evm/src/memory/segments.rs index f3de1a21..ac90d210 100644 --- a/evm/src/memory/segments.rs +++ b/evm/src/memory/segments.rs @@ -1,3 +1,4 @@ +use ethereum_types::U256; use num::traits::AsPrimitive; pub(crate) const SEGMENT_SCALING_FACTOR: usize = 32; @@ -39,46 +40,40 @@ pub(crate) enum Segment { RlpRaw = 12 << SEGMENT_SCALING_FACTOR, /// Contains all trie data. It is owned by the kernel, so it only lives on context 0. TrieData = 13 << SEGMENT_SCALING_FACTOR, - /// A buffer used to store the encodings of a branch node's children. - TrieEncodedChild = 14 << SEGMENT_SCALING_FACTOR, - /// A buffer used to store the lengths of the encodings of a branch node's children. - TrieEncodedChildLen = 15 << SEGMENT_SCALING_FACTOR, - /// A table of values 2^i for i=0..255 for use with shift - /// instructions; initialised by `kernel/asm/shift.asm::init_shift_table()`. - ShiftTable = 16 << SEGMENT_SCALING_FACTOR, - JumpdestBits = 17 << SEGMENT_SCALING_FACTOR, - EcdsaTable = 18 << SEGMENT_SCALING_FACTOR, - BnWnafA = 19 << SEGMENT_SCALING_FACTOR, - BnWnafB = 20 << SEGMENT_SCALING_FACTOR, - BnTableQ = 21 << SEGMENT_SCALING_FACTOR, - BnPairing = 22 << SEGMENT_SCALING_FACTOR, + ShiftTable = 14 << SEGMENT_SCALING_FACTOR, + JumpdestBits = 15 << SEGMENT_SCALING_FACTOR, + EcdsaTable = 16 << SEGMENT_SCALING_FACTOR, + BnWnafA = 17 << SEGMENT_SCALING_FACTOR, + BnWnafB = 18 << SEGMENT_SCALING_FACTOR, + BnTableQ = 19 << SEGMENT_SCALING_FACTOR, + BnPairing = 20 << SEGMENT_SCALING_FACTOR, /// List of addresses that have been accessed in the current transaction. - AccessedAddresses = 23 << SEGMENT_SCALING_FACTOR, + AccessedAddresses = 21 << SEGMENT_SCALING_FACTOR, /// List of storage keys that have been accessed in the current transaction. - AccessedStorageKeys = 24 << SEGMENT_SCALING_FACTOR, + AccessedStorageKeys = 22 << SEGMENT_SCALING_FACTOR, /// List of addresses that have called SELFDESTRUCT in the current transaction. - SelfDestructList = 25 << SEGMENT_SCALING_FACTOR, + SelfDestructList = 23 << SEGMENT_SCALING_FACTOR, /// Contains the bloom filter of a transaction. - TxnBloom = 26 << SEGMENT_SCALING_FACTOR, + TxnBloom = 24 << SEGMENT_SCALING_FACTOR, /// Contains the bloom filter present in the block header. - GlobalBlockBloom = 27 << SEGMENT_SCALING_FACTOR, + GlobalBlockBloom = 25 << SEGMENT_SCALING_FACTOR, /// List of log pointers pointing to the LogsData segment. - Logs = 28 << SEGMENT_SCALING_FACTOR, - LogsData = 29 << SEGMENT_SCALING_FACTOR, + Logs = 26 << SEGMENT_SCALING_FACTOR, + LogsData = 27 << SEGMENT_SCALING_FACTOR, /// Journal of state changes. List of pointers to `JournalData`. Length in `GlobalMetadata`. - Journal = 30 << SEGMENT_SCALING_FACTOR, - JournalData = 31 << SEGMENT_SCALING_FACTOR, - JournalCheckpoints = 32 << SEGMENT_SCALING_FACTOR, + Journal = 28 << SEGMENT_SCALING_FACTOR, + JournalData = 29 << SEGMENT_SCALING_FACTOR, + JournalCheckpoints = 30 << SEGMENT_SCALING_FACTOR, /// List of addresses that have been touched in the current transaction. - TouchedAddresses = 33 << SEGMENT_SCALING_FACTOR, + TouchedAddresses = 31 << SEGMENT_SCALING_FACTOR, /// List of checkpoints for the current context. Length in `ContextMetadata`. - ContextCheckpoints = 34 << SEGMENT_SCALING_FACTOR, + ContextCheckpoints = 32 << SEGMENT_SCALING_FACTOR, /// List of 256 previous block hashes. - BlockHashes = 35 << SEGMENT_SCALING_FACTOR, + BlockHashes = 33 << SEGMENT_SCALING_FACTOR, } impl Segment { - pub(crate) const COUNT: usize = 36; + pub(crate) const COUNT: usize = 34; /// Unscales this segment by `SEGMENT_SCALING_FACTOR`. pub(crate) const fn unscale(&self) -> usize { @@ -101,8 +96,6 @@ impl Segment { Self::TxnData, Self::RlpRaw, Self::TrieData, - Self::TrieEncodedChild, - Self::TrieEncodedChildLen, Self::ShiftTable, Self::JumpdestBits, Self::EcdsaTable, @@ -143,8 +136,6 @@ impl Segment { Segment::TxnData => "SEGMENT_TXN_DATA", Segment::RlpRaw => "SEGMENT_RLP_RAW", Segment::TrieData => "SEGMENT_TRIE_DATA", - Segment::TrieEncodedChild => "SEGMENT_TRIE_ENCODED_CHILD", - Segment::TrieEncodedChildLen => "SEGMENT_TRIE_ENCODED_CHILD_LEN", Segment::ShiftTable => "SEGMENT_SHIFT_TABLE", Segment::JumpdestBits => "SEGMENT_JUMPDEST_BITS", Segment::EcdsaTable => "SEGMENT_KERNEL_ECDSA_TABLE", @@ -184,8 +175,6 @@ impl Segment { Segment::TxnData => 8, Segment::RlpRaw => 8, Segment::TrieData => 256, - Segment::TrieEncodedChild => 256, - Segment::TrieEncodedChildLen => 6, Segment::ShiftTable => 256, Segment::JumpdestBits => 1, Segment::EcdsaTable => 256, @@ -208,4 +197,17 @@ impl Segment { Segment::BlockHashes => 256, } } + + pub(crate) fn constant(&self, virt: usize) -> Option { + match self { + Segment::RlpRaw => { + if virt == 0xFFFFFFFF { + Some(U256::from(0x80)) + } else { + None + } + } + _ => None, + } + } } diff --git a/evm/src/witness/memory.rs b/evm/src/witness/memory.rs index ff4c9614..e6cb14f9 100644 --- a/evm/src/witness/memory.rs +++ b/evm/src/witness/memory.rs @@ -190,6 +190,11 @@ impl MemoryState { } let segment = Segment::all()[address.segment]; + + if let Some(constant) = Segment::constant(&segment, address.virt) { + return constant; + } + let val = self.contexts[address.context].segments[address.segment].get(address.virt); assert!( val.bits() <= segment.bit_range(), @@ -207,6 +212,15 @@ impl MemoryState { } let segment = Segment::all()[address.segment]; + + if let Some(constant) = Segment::constant(&segment, address.virt) { + assert!( + constant == val, + "Attempting to set constant {} to incorrect value", + address.virt + ); + return; + } assert!( val.bits() <= segment.bit_range(), "Value {} exceeds {:?} range of {} bits",