diff --git a/evm/src/cpu/kernel/aggregator.rs b/evm/src/cpu/kernel/aggregator.rs index efaa621d..76091a2e 100644 --- a/evm/src/cpu/kernel/aggregator.rs +++ b/evm/src/cpu/kernel/aggregator.rs @@ -43,6 +43,7 @@ pub(crate) fn combined_kernel() -> Kernel { include_str!("asm/rlp/decode.asm"), include_str!("asm/rlp/read_to_memory.asm"), include_str!("asm/mpt/hash.asm"), + include_str!("asm/mpt/hex_prefix.asm"), include_str!("asm/mpt/load.asm"), include_str!("asm/mpt/read.asm"), include_str!("asm/mpt/storage_read.asm"), diff --git a/evm/src/cpu/kernel/asm/main.asm b/evm/src/cpu/kernel/asm/main.asm index d0402f17..a27a1527 100644 --- a/evm/src/cpu/kernel/asm/main.asm +++ b/evm/src/cpu/kernel/asm/main.asm @@ -1,8 +1,20 @@ global main: + // First, load all MPT data from the prover. + PUSH txn_loop + %jump(load_all_mpts) + +hash_initial_tries: + // TODO: Hash each trie and set @GLOBAL_METADATA_STATE_TRIE_DIGEST_BEFORE, etc. + +txn_loop: // If the prover has no more txns for us to process, halt. PROVER_INPUT(end_of_txns) - %jumpi(halt) + %jumpi(hash_final_tries) - // Call route_txn, returning to main to continue the loop. - PUSH main + // Call route_txn. When we return, continue the txn loop. + PUSH txn_loop %jump(route_txn) + +hash_final_tries: + // TODO: Hash each trie and set @GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER, etc. + %jump(halt) diff --git a/evm/src/cpu/kernel/asm/memory/core.asm b/evm/src/cpu/kernel/asm/memory/core.asm index 73bafbee..f18a620a 100644 --- a/evm/src/cpu/kernel/asm/memory/core.asm +++ b/evm/src/cpu/kernel/asm/memory/core.asm @@ -98,3 +98,10 @@ %mstore_kernel(@SEGMENT_CODE) // stack: (empty) %endmacro + +// Store a single value to @SEGMENT_KERNEL_GENERAL. +%macro mstore_kernel_general + // stack: offset, value + %mstore_kernel(@SEGMENT_KERNEL_GENERAL) + // stack: (empty) +%endmacro diff --git a/evm/src/cpu/kernel/asm/mpt/hash.asm b/evm/src/cpu/kernel/asm/mpt/hash.asm index 41795d5d..b41e3dbf 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash.asm @@ -1,2 +1,48 @@ global mpt_hash: - // TODO + // stack: node_ptr, retdest + DUP1 + %mload_trie_data + // stack: node_type, node_ptr, retdest + // Increment node_ptr, so it points to the node payload instead of its type. + SWAP1 %add_const(1) SWAP1 + // stack: node_type, node_payload_ptr, retdest + + 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) + PANIC // Invalid node type? Shouldn't get here. + +mpt_hash_empty: + %stack (node_type, node_payload_ptr, retdest) -> (retdest, @EMPTY_NODE_HASH) + JUMP + +mpt_hash_hash: + // stack: node_type, node_payload_ptr, retdest + POP + // stack: node_payload_ptr, retdest + %mload_trie_data + // 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 new file mode 100644 index 00000000..a1319748 --- /dev/null +++ b/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm @@ -0,0 +1,51 @@ +// 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 + +loop: + // If i == 0, break to first_byte. + DUP1 ISZERO %jumpi(first_byte) + + // stack: i, num_nibbles, packed_nibbles, terminated, retdest + DUP3 // 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 + %sub_const(1) + SWAP2 %shr_const(8) SWAP2 // packed_nibbles >>= 8 + // stack: i, num_nibbles, packed_nibbles, terminated, retdest + %jump(loop) + +first_byte: + // stack: 0, 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 + %mul_const(2) + SWAP1 + // stack: num_nibbles, terminated * 2, first_nibble_or_zero, result_len, retdest + %mod_const(2) + ADD + // stack: parity + terminated * 2, first_nibble_or_zero, result_len, 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 + SWAP1 + JUMP diff --git a/evm/src/cpu/kernel/asm/mpt/load.asm b/evm/src/cpu/kernel/asm/mpt/load.asm index 57c84ddb..94d05dde 100644 --- a/evm/src/cpu/kernel/asm/mpt/load.asm +++ b/evm/src/cpu/kernel/asm/mpt/load.asm @@ -39,7 +39,6 @@ storage_trie_loop: // stack: i, num_storage_tries, retdest %jump(storage_trie_loop) storage_trie_loop_end: - // TODO: Hash tries and set @GLOBAL_METADATA_STATE_TRIE_DIGEST_BEFORE, etc. // stack: i, num_storage_tries, retdest %pop2 // stack: retdest diff --git a/evm/src/cpu/kernel/constants/mod.rs b/evm/src/cpu/kernel/constants/mod.rs index 0db347cf..2694b82a 100644 --- a/evm/src/cpu/kernel/constants/mod.rs +++ b/evm/src/cpu/kernel/constants/mod.rs @@ -18,6 +18,9 @@ pub fn evm_constants() -> HashMap { for (name, value) in EC_CONSTANTS { c.insert(name.into(), U256::from_big_endian(&value)); } + for (name, value) in HASH_CONSTANTS { + c.insert(name.into(), U256::from_big_endian(&value)); + } for (name, value) in GAS_CONSTANTS { c.insert(name.into(), U256::from(value)); } @@ -43,6 +46,14 @@ pub fn evm_constants() -> HashMap { c } +const HASH_CONSTANTS: [(&str, [u8; 32]); 1] = [ + // Hash of an empty node: keccak(rlp.encode(b'')).hex() + ( + "EMPTY_NODE_HASH", + hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), + ), +]; + const EC_CONSTANTS: [(&str, [u8; 32]); 3] = [ ( "BN_BASE", diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index 8a898cb3..04fc5ced 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -161,6 +161,10 @@ 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 new file mode 100644 index 00000000..4318b560 --- /dev/null +++ b/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs @@ -0,0 +1,50 @@ +use anyhow::Result; + +use crate::cpu::kernel::aggregator::KERNEL; +use crate::cpu::kernel::interpreter::Interpreter; + +#[test] +fn hex_prefix_even_nonterminated() -> Result<()> { + let hex_prefix = KERNEL.global_labels["hex_prefix"]; + + 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 mut interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack); + interpreter.run()?; + assert_eq!(interpreter.stack(), vec![4.into()]); + + assert_eq!( + interpreter.get_kernel_general_data(), + vec![0.into(), 0xAB.into(), 0xCD.into(), 0xEF.into(),] + ); + + Ok(()) +} + +#[test] +fn hex_prefix_odd_terminated() -> Result<()> { + let hex_prefix = KERNEL.global_labels["hex_prefix"]; + + 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 mut interpreter = Interpreter::new_with_kernel(hex_prefix, initial_stack); + interpreter.run()?; + assert_eq!(interpreter.stack(), vec![3.into()]); + + assert_eq!( + interpreter.get_kernel_general_data(), + vec![ + (terminated * 2 + 1u32) * 16 + 0xAu32, + 0xBC.into(), + 0xDE.into(), + ] + ); + + Ok(()) +} diff --git a/evm/src/cpu/kernel/tests/mpt/mod.rs b/evm/src/cpu/kernel/tests/mpt/mod.rs index 2d14c89a..3f7ef252 100644 --- a/evm/src/cpu/kernel/tests/mpt/mod.rs +++ b/evm/src/cpu/kernel/tests/mpt/mod.rs @@ -1,5 +1,6 @@ use eth_trie_utils::partial_trie::{Nibbles, PartialTrie}; +mod hex_prefix; mod load; mod read;