From 2dacbfe2ffd9bc5242caaafe17dd43095d664f28 Mon Sep 17 00:00:00 2001 From: Robin Salen <30937548+Nashtare@users.noreply.github.com> Date: Mon, 8 Jan 2024 11:46:26 +0100 Subject: [PATCH] Address bundling (#1426) * Start * Scale TxnFields * Speed-up * Misc fixes * Other fixes * Fix * Fix offset * One more fix * And one more fix * Fix * Fix * Fix init * More interpreter fixes * Final fixes * Add helper methods * Clippy * Apply suggestions * Comments * Update documentation * Regenerate pdf * minor * Rename some macros for consistency * Add utility method for unscaling segments and scaled metadata * Address comments --- evm/spec/cpulogic.tex | 45 ++- evm/spec/zkevm.pdf | Bin 295982 -> 315675 bytes evm/src/cpu/byte_unpacking.rs | 49 ++- evm/src/cpu/contextops.rs | 42 +-- evm/src/cpu/cpu_stark.rs | 62 ++-- evm/src/cpu/dup_swap.rs | 4 +- evm/src/cpu/jumps.rs | 9 +- evm/src/cpu/kernel/asm/account_code.asm | 21 +- evm/src/cpu/kernel/asm/bignum/util.asm | 14 +- evm/src/cpu/kernel/asm/core/access_lists.asm | 22 +- evm/src/cpu/kernel/asm/core/call.asm | 90 +++--- evm/src/cpu/kernel/asm/core/call_gas.asm | 2 +- evm/src/cpu/kernel/asm/core/create.asm | 19 +- .../cpu/kernel/asm/core/create_addresses.asm | 17 +- .../cpu/kernel/asm/core/create_receipt.asm | 20 +- .../cpu/kernel/asm/core/jumpdest_analysis.asm | 8 +- .../kernel/asm/core/precompiles/blake2_f.asm | 30 +- .../kernel/asm/core/precompiles/bn_add.asm | 12 +- .../kernel/asm/core/precompiles/bn_mul.asm | 11 +- .../cpu/kernel/asm/core/precompiles/ecrec.asm | 11 +- .../kernel/asm/core/precompiles/expmod.asm | 84 +++--- .../cpu/kernel/asm/core/precompiles/id.asm | 17 +- .../cpu/kernel/asm/core/precompiles/main.asm | 4 +- .../kernel/asm/core/precompiles/rip160.asm | 35 +-- .../kernel/asm/core/precompiles/sha256.asm | 37 +-- .../kernel/asm/core/precompiles/snarkv.asm | 9 +- evm/src/cpu/kernel/asm/core/process_txn.asm | 30 +- evm/src/cpu/kernel/asm/core/terminate.asm | 32 +- evm/src/cpu/kernel/asm/core/util.asm | 5 +- evm/src/cpu/kernel/asm/curve/wnaf.asm | 6 +- evm/src/cpu/kernel/asm/main.asm | 8 +- evm/src/cpu/kernel/asm/memory/core.asm | 122 ++++---- evm/src/cpu/kernel/asm/memory/memcpy.asm | 76 ++--- evm/src/cpu/kernel/asm/memory/memset.asm | 36 +-- evm/src/cpu/kernel/asm/memory/metadata.asm | 72 ++++- evm/src/cpu/kernel/asm/memory/packing.asm | 283 +++++++++--------- evm/src/cpu/kernel/asm/memory/syscalls.asm | 20 +- evm/src/cpu/kernel/asm/memory/txn_fields.asm | 17 +- evm/src/cpu/kernel/asm/mpt/hash/hash.asm | 34 +-- .../asm/mpt/hash/hash_trie_specific.asm | 193 ++++++------ evm/src/cpu/kernel/asm/mpt/hex_prefix.asm | 95 +++--- .../asm/mpt/insert/insert_trie_specific.asm | 10 +- evm/src/cpu/kernel/asm/rlp/decode.asm | 98 +++--- evm/src/cpu/kernel/asm/rlp/encode.asm | 209 ++++++------- .../cpu/kernel/asm/rlp/encode_rlp_scalar.asm | 63 ++-- .../cpu/kernel/asm/rlp/encode_rlp_string.asm | 97 +++--- evm/src/cpu/kernel/asm/rlp/read_to_memory.asm | 35 ++- evm/src/cpu/kernel/asm/shift.asm | 16 +- .../asm/transactions/common_decoding.asm | 149 +++++---- .../cpu/kernel/asm/transactions/router.asm | 14 +- .../cpu/kernel/asm/transactions/type_0.asm | 87 +++--- .../cpu/kernel/asm/transactions/type_1.asm | 67 ++--- .../cpu/kernel/asm/transactions/type_2.asm | 67 ++--- evm/src/cpu/kernel/asm/util/basic_macros.asm | 33 ++ evm/src/cpu/kernel/asm/util/keccak.asm | 17 +- .../cpu/kernel/constants/context_metadata.rs | 40 ++- .../cpu/kernel/constants/global_metadata.rs | 112 +++---- evm/src/cpu/kernel/constants/mod.rs | 11 +- evm/src/cpu/kernel/constants/txn_fields.rs | 45 ++- evm/src/cpu/kernel/interpreter.rs | 179 ++++++----- evm/src/cpu/kernel/tests/account_code.rs | 79 ++--- evm/src/cpu/kernel/tests/add11.rs | 8 +- evm/src/cpu/kernel/tests/core/access_lists.rs | 46 ++- .../kernel/tests/core/jumpdest_analysis.rs | 8 +- evm/src/cpu/kernel/tests/mpt/hex_prefix.rs | 17 +- evm/src/cpu/kernel/tests/packing.rs | 26 +- evm/src/cpu/kernel/tests/rlp/decode.rs | 35 ++- evm/src/cpu/kernel/tests/rlp/encode.rs | 30 +- evm/src/cpu/memio.rs | 53 ++-- evm/src/cpu/shift.rs | 4 +- evm/src/cpu/stack.rs | 25 +- evm/src/cpu/syscalls_exceptions.rs | 4 +- evm/src/generation/mod.rs | 3 +- evm/src/generation/prover_input.rs | 9 +- evm/src/generation/state.rs | 12 +- evm/src/generation/trie_extractor.rs | 2 +- evm/src/keccak_sponge/keccak_sponge_stark.rs | 6 +- evm/src/memory/memory_stark.rs | 4 +- evm/src/memory/segments.rs | 85 +++--- evm/src/recursive_verifier.rs | 82 +++-- evm/src/verifier.rs | 19 +- evm/src/witness/memory.rs | 26 +- evm/src/witness/operation.rs | 87 +++--- evm/src/witness/transition.rs | 24 +- evm/tests/log_opcode.rs | 2 +- 85 files changed, 2028 insertions(+), 1720 deletions(-) diff --git a/evm/spec/cpulogic.tex b/evm/spec/cpulogic.tex index df866daf..318e2db4 100644 --- a/evm/spec/cpulogic.tex +++ b/evm/spec/cpulogic.tex @@ -75,15 +75,14 @@ ecAdd, ecMul and ecPairing precompiles. \item[0x0F.] \texttt{SUBMOD}. Pops 3 elements from the stack, and pushes the modular difference of the first two elements of the stack by the third one. It is similar to the SUB instruction, with an extra pop for the custom modulus. - \item[0x21.] \texttt{KECCAK\_GENERAL}. Pops 4 elements (successively the context, segment, and offset portions of a Memory address, followed by a length $\ell$) - and pushes the hash of the memory portion starting at the constructed address and of length $\ell$. It is similar to KECCAK256 (0x20) instruction, but can be applied to - any memory section (i.e. even privileged ones). + \item[0x21.] \texttt{KECCAK\_GENERAL}. Pops 2 elements (a Memory address, followed by a length $\ell$) and pushes the hash of the memory portion starting at the + constructed address and of length $\ell$. It is similar to KECCAK256 (0x20) instruction, but can be applied to any memory section (i.e. even privileged ones). \item[0x49.] \texttt{PROVER\_INPUT}. Pushes a single prover input onto the stack. - \item[0xC0-0xDF.] \texttt{MSTORE\_32BYTES}. Pops 4 elements from the stack (successively the context, segment, and offset portions of a Memory address, and then a value), and pushes - a new offset' onto the stack. The value is being decomposed into bytes and written to memory, starting from the reconstructed address. The new offset being pushed is computed as the - initial address offset + the length of the byte sequence being written to memory. Note that similarly to PUSH (0x60-0x7F) instructions there are 31 MSTORE\_32BYTES instructions, each + \item[0xC0-0xDF.] \texttt{MSTORE\_32BYTES}. Pops 2 elements from the stack (a Memory address, and then a value), and pushes + a new address' onto the stack. The value is being decomposed into bytes and written to memory, starting from the fetched address. The new address being pushed is computed as the + initial address + the length of the byte sequence being written to memory. Note that similarly to PUSH (0x60-0x7F) instructions, there are 32 MSTORE\_32BYTES instructions, each corresponding to a target byte length (length 0 is ignored, for the same reasons as MLOAD\_32BYTES, see below). Writing to memory an integer fitting in $n$ bytes with a length $\ell < n$ will result in the integer being truncated. On the other hand, specifying a length $\ell$ greater than the byte size of the value being written will result in padding with zeroes. This process is heavily used when resetting memory sections (by calling MSTORE\_32BYTES\_32 with the value 0). @@ -93,29 +92,49 @@ ecAdd, ecMul and ecPairing precompiles. \item[0xF7.] \texttt{SET\_CONTEXT}. Pops the top element of the stack and updates the current context to this value. It is usually used when calling another contract or precompile, to distinguish the caller from the callee. - \item[0xF8.] \texttt{MLOAD\_32BYTES}. Pops 4 elements from the stack (successively the context, segment, and offset portions of a Memory address, and then a length $\ell$), and pushes - a value onto the stack. The pushed value corresponds to the U256 integer read from the big-endian sequence of length $\ell$ from the memory address being reconstructed. Note that an + \item[0xF8.] \texttt{MLOAD\_32BYTES}. Pops 2 elements from the stack (a Memory address, and then a length $\ell$), and pushes + a value onto the stack. The pushed value corresponds to the U256 integer read from the big-endian sequence of length $\ell$ from the memory address being fetched. Note that an empty length is not valid, nor is a length greater than 32 (as a U256 consists in at most 32 bytes). Missing these conditions will result in an unverifiable proof. \item[0xF9.] \texttt{EXIT\_KERNEL}. Pops 1 element from the stack. This instruction is used at the end of a syscall, before proceeding to the rest of the execution logic. The popped element, \textit{kexit\_info}, contains several pieces of information like the current program counter, the current amount of gas used, and whether we are in kernel (i.e. privileged) mode or not. - \item[0xFB.] \texttt{MLOAD\_GENERAL}. Pops 3 elements (successively the context, segment, and offset portions of a Memory address), and pushes the value stored at this memory + \item[0xFB.] \texttt{MLOAD\_GENERAL}. Pops 1 elements (a Memory address), and pushes the value stored at this memory address onto the stack. It can read any memory location, general (similarly to MLOAD (0x51) instruction) or privileged. - \item[0xFC.] \texttt{MSTORE\_GENERAL}. Pops 4 elements (successively a value, then the context, segment, and offset portions of a Memory address), and writes the popped value from - the stack at the reconstructed address. It can write to any memory location, general (similarly to MSTORE (0x52) / MSTORE8 (0x53) instructions) or privileged. - + \item[0xFC.] \texttt{MSTORE\_GENERAL}. Pops 2 elements (a value and a Memory address), and writes the popped value from + the stack at the fetched address. It can write to any memory location, general (similarly to MSTORE (0x52) / MSTORE8 (0x53) instructions) or privileged. \end{enumerate} +\subsection{Memory addresses} +\label{memoryaddresses} + +Kernel operations deal with memory addresses as single U256 elements. +However, when processing the operations to generate the proof witness, the CPU will decompose these into three components: + +\begin{itemize} + \item[context.] The context of the memory address. The Kernel context is special, and has value 0. + + \item[segment.] The segment of the memory address, corresponding to a specific section given a context (eg. MPT data, global metadata, etc.). + + \item[virtual.] The offset of the memory address, within a segment given a context. +\end{itemize} + +To easily retrieve these components, we scale them so that they can represent a memory address as: + +$$ \mathrm{addr} = 2^{64} \cdot \mathrm{context} + 2^{32} \cdot \mathrm{segment} + \mathrm{offset}$$ + +This allows to easily retrieve each component individually once a Memory address has been decomposed into 32-bit limbs. + + \subsection{Stack handling} \label{stackhandling} \subsubsection{Top of the stack} The majority of memory operations involve the stack. The stack is a segment in memory, and stack operations (popping or pushing) use the memory channels. -Every CPU instruction performs between 0 and 4 pops, and may push at most once. However, for efficiency purposes, we hold the top of the stack in +Every CPU instruction performs between 0 and 3 pops, and may push at most once. However, for efficiency purposes, we hold the top of the stack in the first memory channel \texttt{current\_row.mem\_channels[0]}, only writing it in memory if necessary. \paragraph*{Motivation:} diff --git a/evm/spec/zkevm.pdf b/evm/spec/zkevm.pdf index 4f4f0a00b0effa6b21385b1cf3e66f00d8f93e83..97217d5d8069f79d32afa655ebcfafe1cea12357 100644 GIT binary patch delta 129878 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⁣y6?kta2ql;)g{j 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%0AE*D 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

JR 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 // The MSTORE_32BYTES opcodes are differentiated from MLOAD_32BYTES // by the 5th bit set to 0. let filter = lv.op.m_op_32bytes * (lv.opcode_bits[5] - P::ONES); - let new_offset = nv.mem_channels[0].value; - let virt = lv.mem_channels[2].value[0]; + + // The address to write to is stored in the first memory channel. + // It contains virt, segment, ctx in its first 3 limbs, and 0 otherwise. + // The new address is identical, except for its `virtual` limb that is increased by the corresponding `len` offset. + let new_addr = nv.mem_channels[0].value; + let written_addr = lv.mem_channels[0].value; + // Read len from opcode bits and constrain the pushed new offset. let len_bits: P = lv.opcode_bits[..5] .iter() @@ -25,8 +30,16 @@ pub(crate) fn eval_packed( .map(|(i, &bit)| bit * P::Scalar::from_canonical_u64(1 << i)) .sum(); let len = len_bits + P::ONES; - yield_constr.constraint(filter * (new_offset[0] - virt - len)); - for &limb in &new_offset[1..] { + + // Check that `virt` is increased properly. + yield_constr.constraint(filter * (new_addr[0] - written_addr[0] - len)); + + // Check that `segment` and `ctx` do not change. + yield_constr.constraint(filter * (new_addr[1] - written_addr[1])); + yield_constr.constraint(filter * (new_addr[2] - written_addr[2])); + + // Check that the rest of the returned address is null. + for &limb in &new_addr[3..] { yield_constr.constraint(filter * limb); } } @@ -41,8 +54,13 @@ pub(crate) fn eval_ext_circuit, const D: usize>( // by the 5th bit set to 0. let filter = builder.mul_sub_extension(lv.op.m_op_32bytes, lv.opcode_bits[5], lv.op.m_op_32bytes); - let new_offset = nv.mem_channels[0].value; - let virt = lv.mem_channels[2].value[0]; + + // The address to write to is stored in the first memory channel. + // It contains virt, segment, ctx in its first 3 limbs, and 0 otherwise. + // The new address is identical, except for its `virtual` limb that is increased by the corresponding `len` offset. + let new_addr = nv.mem_channels[0].value; + let written_addr = lv.mem_channels[0].value; + // Read len from opcode bits and constrain the pushed new offset. let len_bits = lv.opcode_bits[..5].iter().enumerate().fold( builder.zero_extension(), @@ -50,11 +68,26 @@ pub(crate) fn eval_ext_circuit, const D: usize>( builder.mul_const_add_extension(F::from_canonical_u64(1 << i), bit, cumul) }, ); - let diff = builder.sub_extension(new_offset[0], virt); + + // Check that `virt` is increased properly. + let diff = builder.sub_extension(new_addr[0], written_addr[0]); let diff = builder.sub_extension(diff, len_bits); let constr = builder.mul_sub_extension(filter, diff, filter); yield_constr.constraint(builder, constr); - for &limb in &new_offset[1..] { + + // Check that `segment` and `ctx` do not change. + { + let diff = builder.sub_extension(new_addr[1], written_addr[1]); + let constr = builder.mul_extension(filter, diff); + yield_constr.constraint(builder, constr); + + let diff = builder.sub_extension(new_addr[2], written_addr[2]); + let constr = builder.mul_extension(filter, diff); + yield_constr.constraint(builder, constr); + } + + // Check that the rest of the returned address is null. + for &limb in &new_addr[3..] { let constr = builder.mul_extension(filter, limb); yield_constr.constraint(builder, constr); } diff --git a/evm/src/cpu/contextops.rs b/evm/src/cpu/contextops.rs index 2f0c72d4..0afc070f 100644 --- a/evm/src/cpu/contextops.rs +++ b/evm/src/cpu/contextops.rs @@ -11,7 +11,8 @@ use super::membus::NUM_GP_CHANNELS; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; use crate::cpu::kernel::constants::context_metadata::ContextMetadata; -use crate::memory::segments::Segment; +use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; +use crate::memory::VALUE_LIMBS; // If true, the instruction will keep the current context for the next row. // If false, next row's context is handled manually. @@ -83,8 +84,10 @@ fn eval_packed_get( // If the opcode is GET_CONTEXT, then lv.opcode_bits[0] = 0. let filter = lv.op.context_op * (P::ONES - lv.opcode_bits[0]); let new_stack_top = nv.mem_channels[0].value; - yield_constr.constraint(filter * (new_stack_top[0] - lv.context)); - for &limb in &new_stack_top[1..] { + // Context is scaled by 2^64, hence stored in the 3rd limb. + yield_constr.constraint(filter * (new_stack_top[2] - lv.context)); + + for (i, &limb) in new_stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { yield_constr.constraint(filter * limb); } @@ -113,12 +116,14 @@ fn eval_ext_circuit_get, const D: usize>( let prod = builder.mul_extension(lv.op.context_op, lv.opcode_bits[0]); let filter = builder.sub_extension(lv.op.context_op, prod); let new_stack_top = nv.mem_channels[0].value; + // Context is scaled by 2^64, hence stored in the 3rd limb. { - let diff = builder.sub_extension(new_stack_top[0], lv.context); + let diff = builder.sub_extension(new_stack_top[2], lv.context); let constr = builder.mul_extension(filter, diff); yield_constr.constraint(builder, constr); } - for &limb in &new_stack_top[1..] { + + for (i, &limb) in new_stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { let constr = builder.mul_extension(filter, limb); yield_constr.constraint(builder, constr); } @@ -155,13 +160,14 @@ fn eval_packed_set( let stack_top = lv.mem_channels[0].value; let write_old_sp_channel = lv.mem_channels[1]; let read_new_sp_channel = lv.mem_channels[2]; - let ctx_metadata_segment = P::Scalar::from_canonical_u64(Segment::ContextMetadata as u64); - let stack_size_field = P::Scalar::from_canonical_u64(ContextMetadata::StackSize as u64); + // We need to unscale the context metadata segment and related field. + let ctx_metadata_segment = P::Scalar::from_canonical_usize(Segment::ContextMetadata.unscale()); + let stack_size_field = P::Scalar::from_canonical_usize(ContextMetadata::StackSize.unscale()); let local_sp_dec = lv.stack_len - P::ONES; // The next row's context is read from stack_top. - yield_constr.constraint(filter * (stack_top[0] - nv.context)); - for &limb in &stack_top[1..] { + yield_constr.constraint(filter * (stack_top[2] - nv.context)); + for (i, &limb) in stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { yield_constr.constraint(filter * limb); } @@ -220,22 +226,23 @@ fn eval_ext_circuit_set, const D: usize>( let stack_top = lv.mem_channels[0].value; let write_old_sp_channel = lv.mem_channels[1]; let read_new_sp_channel = lv.mem_channels[2]; - let ctx_metadata_segment = builder.constant_extension(F::Extension::from_canonical_u32( - Segment::ContextMetadata as u32, + // We need to unscale the context metadata segment and related field. + let ctx_metadata_segment = builder.constant_extension(F::Extension::from_canonical_usize( + Segment::ContextMetadata.unscale(), )); - let stack_size_field = builder.constant_extension(F::Extension::from_canonical_u32( - ContextMetadata::StackSize as u32, + let stack_size_field = builder.constant_extension(F::Extension::from_canonical_usize( + ContextMetadata::StackSize.unscale(), )); let one = builder.one_extension(); let local_sp_dec = builder.sub_extension(lv.stack_len, one); // The next row's context is read from stack_top. { - let diff = builder.sub_extension(stack_top[0], nv.context); + let diff = builder.sub_extension(stack_top[2], nv.context); let constr = builder.mul_extension(filter, diff); yield_constr.constraint(builder, constr); } - for &limb in &stack_top[1..] { + for (i, &limb) in stack_top.iter().enumerate().filter(|(i, _)| *i != 2) { let constr = builder.mul_extension(filter, limb); yield_constr.constraint(builder, constr); } @@ -368,7 +375,8 @@ pub(crate) fn eval_packed( yield_constr.constraint(new_filter * (channel.addr_context - nv.context)); // Same segment for both. yield_constr.constraint( - new_filter * (channel.addr_segment - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + new_filter + * (channel.addr_segment - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); // The address is one less than stack_len. let addr_virtual = stack_len - P::ONES; @@ -429,7 +437,7 @@ pub(crate) fn eval_ext_circuit, const D: usize>( { let diff = builder.add_const_extension( channel.addr_segment, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), ); let constr = builder.mul_extension(new_filter, diff); yield_constr.constraint(builder, constr); diff --git a/evm/src/cpu/cpu_stark.rs b/evm/src/cpu/cpu_stark.rs index c93f1b87..478dc9c2 100644 --- a/evm/src/cpu/cpu_stark.rs +++ b/evm/src/cpu/cpu_stark.rs @@ -21,7 +21,7 @@ use crate::cpu::{ }; use crate::cross_table_lookup::{Column, Filter, TableWithColumns}; use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame}; -use crate::memory::segments::Segment; +use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; use crate::memory::{NUM_CHANNELS, VALUE_LIMBS}; use crate::stark::Stark; @@ -29,15 +29,14 @@ use crate::stark::Stark; /// the CPU reads the output of the sponge directly from the `KeccakSpongeStark` table. pub(crate) fn ctl_data_keccak_sponge() -> Vec> { // When executing KECCAK_GENERAL, the GP memory channels are used as follows: - // GP channel 0: stack[-1] = context - // GP channel 1: stack[-2] = segment - // GP channel 2: stack[-3] = virt - // GP channel 3: stack[-4] = len + // GP channel 0: stack[-1] = addr (context, segment, virt) + // GP channel 1: stack[-2] = len // Next GP channel 0: pushed = outputs - let context = Column::single(COL_MAP.mem_channels[0].value[0]); - let segment = Column::single(COL_MAP.mem_channels[1].value[0]); - let virt = Column::single(COL_MAP.mem_channels[2].value[0]); - let len = Column::single(COL_MAP.mem_channels[3].value[0]); + let (context, segment, virt) = get_addr(&COL_MAP, 0); + let context = Column::single(context); + let segment = Column::single(segment); + let virt = Column::single(virt); + let len = Column::single(COL_MAP.mem_channels[1].value[0]); let num_channels = F::from_canonical_usize(NUM_CHANNELS); let timestamp = Column::linear_combination([(COL_MAP.clock, num_channels)]); @@ -149,27 +148,30 @@ pub(crate) fn ctl_data_byte_unpacking() -> Vec> { let is_read = Column::constant(F::ZERO); // When executing MSTORE_32BYTES, the GP memory channels are used as follows: - // GP channel 0: stack[-1] = context - // GP channel 1: stack[-2] = segment - // GP channel 2: stack[-3] = virt - // GP channel 3: stack[-4] = val + // GP channel 0: stack[-1] = addr (context, segment, virt) + // GP channel 1: stack[-2] = val // Next GP channel 0: pushed = new_offset (virt + len) - let context = Column::single(COL_MAP.mem_channels[0].value[0]); - let segment = Column::single(COL_MAP.mem_channels[1].value[0]); - let virt = Column::single(COL_MAP.mem_channels[2].value[0]); - let val = Column::singles(COL_MAP.mem_channels[3].value); + let (context, segment, virt) = get_addr(&COL_MAP, 0); + let mut res = vec![ + is_read, + Column::single(context), + Column::single(segment), + Column::single(virt), + ]; // len can be reconstructed as new_offset - virt. let len = Column::linear_combination_and_next_row_with_constant( - [(COL_MAP.mem_channels[2].value[0], -F::ONE)], + [(COL_MAP.mem_channels[0].value[0], -F::ONE)], [(COL_MAP.mem_channels[0].value[0], F::ONE)], F::ZERO, ); + res.push(len); let num_channels = F::from_canonical_usize(NUM_CHANNELS); let timestamp = Column::linear_combination([(COL_MAP.clock, num_channels)]); + res.push(timestamp); - let mut res = vec![is_read, context, segment, virt, len, timestamp]; + let val = Column::singles(COL_MAP.mem_channels[1].value); res.extend(val); res @@ -224,6 +226,20 @@ pub(crate) const MEM_CODE_CHANNEL_IDX: usize = 0; /// Index of the first general purpose memory channel. pub(crate) const MEM_GP_CHANNELS_IDX_START: usize = MEM_CODE_CHANNEL_IDX + 1; +/// Recover the three components of an address, given a CPU row and +/// a provided memory channel index. +/// The components are recovered as follows: +/// +/// - `context`, shifted by 2^64 (i.e. at index 2) +/// - `segment`, shifted by 2^32 (i.e. at index 1) +/// - `virtual`, not shifted (i.e. at index 0) +pub(crate) const fn get_addr(lv: &CpuColumnsView, mem_channel: usize) -> (T, T, T) { + let addr_context = lv.mem_channels[mem_channel].value[2]; + let addr_segment = lv.mem_channels[mem_channel].value[1]; + let addr_virtual = lv.mem_channels[mem_channel].value[0]; + (addr_context, addr_segment, addr_virtual) +} + /// Make the time/channel column for memory lookups. fn mem_time_and_channel(channel: usize) -> Column { let scalar = F::from_canonical_usize(NUM_CHANNELS); @@ -234,10 +250,10 @@ fn mem_time_and_channel(channel: usize) -> Column { /// Creates the vector of `Columns` corresponding to the contents of the code channel when reading code values. pub(crate) fn ctl_data_code_memory() -> Vec> { let mut cols = vec![ - Column::constant(F::ONE), // is_read - Column::single(COL_MAP.code_context), // addr_context - Column::constant(F::from_canonical_u64(Segment::Code as u64)), // addr_segment - Column::single(COL_MAP.program_counter), // addr_virtual + Column::constant(F::ONE), // is_read + Column::single(COL_MAP.code_context), // addr_context + Column::constant(F::from_canonical_usize(Segment::Code.unscale())), // addr_segment + Column::single(COL_MAP.program_counter), // addr_virtual ]; // Low limb of the value matches the opcode bits diff --git a/evm/src/cpu/dup_swap.rs b/evm/src/cpu/dup_swap.rs index 78e5891a..44763fdc 100644 --- a/evm/src/cpu/dup_swap.rs +++ b/evm/src/cpu/dup_swap.rs @@ -54,7 +54,7 @@ fn constrain_channel_packed( yield_constr.constraint(filter * (channel.is_read - P::Scalar::from_bool(is_read))); yield_constr.constraint(filter * (channel.addr_context - lv.context)); yield_constr.constraint( - filter * (channel.addr_segment - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + filter * (channel.addr_segment - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); // Top of the stack is at `addr = lv.stack_len - 1`. let addr_virtual = lv.stack_len - P::ONES - offset; @@ -94,7 +94,7 @@ fn constrain_channel_ext_circuit, const D: usize>( { let constr = builder.arithmetic_extension( F::ONE, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), filter, channel.addr_segment, filter, diff --git a/evm/src/cpu/jumps.rs b/evm/src/cpu/jumps.rs index f2fd544c..fd7fcfd9 100644 --- a/evm/src/cpu/jumps.rs +++ b/evm/src/cpu/jumps.rs @@ -87,7 +87,8 @@ pub(crate) fn eval_packed_jump_jumpi( yield_constr.constraint_transition(new_filter * (channel.is_read - P::ONES)); yield_constr.constraint_transition(new_filter * (channel.addr_context - nv.context)); yield_constr.constraint_transition( - new_filter * (channel.addr_segment - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + new_filter + * (channel.addr_segment - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); let addr_virtual = nv.stack_len - P::ONES; yield_constr.constraint_transition(new_filter * (channel.addr_virtual - addr_virtual)); @@ -134,7 +135,7 @@ pub(crate) fn eval_packed_jump_jumpi( yield_constr.constraint( filter * (jumpdest_flag_channel.addr_segment - - P::Scalar::from_canonical_u64(Segment::JumpdestBits as u64)), + - P::Scalar::from_canonical_usize(Segment::JumpdestBits.unscale())), ); yield_constr.constraint(filter * (jumpdest_flag_channel.addr_virtual - dst[0])); @@ -205,7 +206,7 @@ pub(crate) fn eval_ext_circuit_jump_jumpi, const D: { let constr = builder.arithmetic_extension( F::ONE, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), new_filter, channel.addr_segment, new_filter, @@ -308,7 +309,7 @@ pub(crate) fn eval_ext_circuit_jump_jumpi, const D: { let constr = builder.arithmetic_extension( F::ONE, - -F::from_canonical_u64(Segment::JumpdestBits as u64), + -F::from_canonical_usize(Segment::JumpdestBits.unscale()), filter, jumpdest_flag_channel.addr_segment, filter, diff --git a/evm/src/cpu/kernel/asm/account_code.asm b/evm/src/cpu/kernel/asm/account_code.asm index 1a9262ee..6aedbd2e 100644 --- a/evm/src/cpu/kernel/asm/account_code.asm +++ b/evm/src/cpu/kernel/asm/account_code.asm @@ -86,6 +86,8 @@ global extcodesize: // Checks that the hash of the loaded code corresponds to the `codehash` in the state trie. // Pre stack: address, ctx, retdest // Post stack: code_size +// +// NOTE: The provided `dest` **MUST** have a virtual address of 0. global load_code: %stack (address, ctx, retdest) -> (extcodehash, address, load_code_ctd, ctx, retdest) JUMP @@ -94,8 +96,9 @@ load_code_ctd: DUP1 ISZERO %jumpi(load_code_non_existent_account) // Load the code non-deterministically in memory and return the length. PROVER_INPUT(account_code) - %stack (code_size, codehash, ctx, retdest) -> (ctx, @SEGMENT_CODE, 0, code_size, codehash, retdest, code_size) + %stack (code_size, codehash, ctx, retdest) -> (ctx, code_size, codehash, retdest, code_size) // Check that the hash of the loaded code equals `codehash`. + // ctx == DST, as SEGMENT_CODE == offset == 0. KECCAK_GENERAL // stack: shouldbecodehash, codehash, retdest, code_size %assert_eq @@ -103,9 +106,9 @@ load_code_ctd: JUMP load_code_non_existent_account: - // Write 0 at address 0 for soundness. - // stack: codehash, ctx, retdest - %stack (codehash, ctx, retdest) -> (0, ctx, @SEGMENT_CODE, 0, retdest, 0) + // Write 0 at address 0 for soundness: SEGMENT_CODE == 0, hence ctx == addr. + // stack: codehash, addr, retdest + %stack (codehash, addr, retdest) -> (0, addr, retdest, 0) MSTORE_GENERAL // stack: retdest, 0 JUMP @@ -120,10 +123,14 @@ global load_code_padded: %jump(load_code) load_code_padded_ctd: - %stack (code_size, ctx, retdest) -> (ctx, @SEGMENT_CODE, code_size, 0, ctx, retdest, code_size) + // SEGMENT_CODE == 0. + // stack: code_size, ctx, retdest + %stack (code_size, ctx, retdest) -> (ctx, code_size, 0, retdest, code_size) + ADD + // stack: addr, 0, retdest, code_size MSTORE_32BYTES_32 - // stack: last_offset, ctx, retdest, code_size - %stack (last_offset, ctx) -> (0, ctx, @SEGMENT_CODE, last_offset) + // stack: addr', retdest, code_size + PUSH 0 MSTORE_GENERAL // stack: retdest, code_size JUMP diff --git a/evm/src/cpu/kernel/asm/bignum/util.asm b/evm/src/cpu/kernel/asm/bignum/util.asm index 7bd6e0dc..0385deec 100644 --- a/evm/src/cpu/kernel/asm/bignum/util.asm +++ b/evm/src/cpu/kernel/asm/bignum/util.asm @@ -1,15 +1,21 @@ %macro memcpy_current_general // stack: dst, src, len - GET_CONTEXT - %stack (context, dst, src, len) -> (context, @SEGMENT_KERNEL_GENERAL, dst, context, @SEGMENT_KERNEL_GENERAL, src, len, %%after) + // DST and SRC are offsets, for the same memory segment + GET_CONTEXT PUSH @SEGMENT_KERNEL_GENERAL %build_address_no_offset + %stack (addr_no_offset, dst, src, len) -> (addr_no_offset, src, addr_no_offset, dst, len, %%after) + ADD + // stack: SRC, addr_no_offset, dst, len, %%after + SWAP2 + ADD + // stack: DST, SRC, len, %%after %jump(memcpy) %%after: %endmacro %macro clear_current_general // stack: dst, len - GET_CONTEXT - %stack (context, dst, len) -> (context, @SEGMENT_KERNEL_GENERAL, dst, len, %%after) + GET_CONTEXT PUSH @SEGMENT_KERNEL_GENERAL %build_address + %stack (DST, len) -> (DST, len, %%after) %jump(memset) %%after: %endmacro diff --git a/evm/src/cpu/kernel/asm/core/access_lists.asm b/evm/src/cpu/kernel/asm/core/access_lists.asm index b1b9fd5d..5019b684 100644 --- a/evm/src/cpu/kernel/asm/core/access_lists.asm +++ b/evm/src/cpu/kernel/asm/core/access_lists.asm @@ -120,14 +120,20 @@ insert_storage_key: // stack: i, len, addr, key, value, retdest DUP4 DUP4 %journal_add_storage_loaded // Add a journal entry for the loaded storage key. // stack: i, len, addr, key, value, retdest - DUP1 %increment - DUP1 %increment - %stack (i_plus_2, i_plus_1, i, len, addr, key, value) -> (i, addr, i_plus_1, key, i_plus_2, value, i_plus_2, value) - %mstore_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) // Store new address at the end of the array. - %mstore_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) // Store new key after that - %mstore_kernel(@SEGMENT_ACCESSED_STORAGE_KEYS) // Store new value after that - // stack: i_plus_2, value, retdest - %increment + DUP1 + PUSH @SEGMENT_ACCESSED_STORAGE_KEYS + %build_kernel_address + + %stack(dst, i, len, addr, key, value) -> (addr, dst, dst, key, dst, value, i, value) + MSTORE_GENERAL // Store new address at the end of the array. + // stack: dst, key, dst, value, i, value, retdest + %increment SWAP1 + MSTORE_GENERAL // Store new key after that + // stack: dst, value, i, value, retdest + %add_const(2) SWAP1 + MSTORE_GENERAL // Store new value after that + // stack: i, value, retdest + %add_const(3) %mstore_global_metadata(@GLOBAL_METADATA_ACCESSED_STORAGE_KEYS_LEN) // Store new length. %stack (value, retdest) -> (retdest, 1, value) // Return 1 to indicate that the storage key was inserted. JUMP diff --git a/evm/src/cpu/kernel/asm/core/call.asm b/evm/src/cpu/kernel/asm/core/call.asm index 2e7d1d73..aa8fbf0c 100644 --- a/evm/src/cpu/kernel/asm/core/call.asm +++ b/evm/src/cpu/kernel/asm/core/call.asm @@ -1,4 +1,5 @@ // Handlers for call-like operations, namely CALL, CALLCODE, STATICCALL and DELEGATECALL. +// Reminder: All context metadata hardcoded offsets are already scaled by `Segment::ContextMetadata`. // Creates a new sub context and executes the code of the given account. global sys_call: @@ -271,7 +272,10 @@ call_too_deep: // because it will already be 0 by default. %macro set_static_true // stack: new_ctx - %stack (new_ctx) -> (1, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_STATIC, new_ctx) + DUP1 + %build_address_with_ctx_no_segment(@CTX_METADATA_STATIC) + PUSH 1 + // stack: 1, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro @@ -279,74 +283,90 @@ call_too_deep: // Set @CTX_METADATA_STATIC of the next context to the current value. %macro set_static // stack: new_ctx + DUP1 + %build_address_with_ctx_no_segment(@CTX_METADATA_STATIC) %mload_context_metadata(@CTX_METADATA_STATIC) - %stack (is_static, new_ctx) -> (is_static, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_STATIC, new_ctx) + // stack: is_static, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro %macro set_new_ctx_addr // stack: called_addr, new_ctx - %stack (called_addr, new_ctx) - -> (called_addr, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_ADDRESS, new_ctx) + DUP2 + %build_address_with_ctx_no_segment(@CTX_METADATA_ADDRESS) + SWAP1 + // stack: called_addr, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro %macro set_new_ctx_caller // stack: sender, new_ctx - %stack (sender, new_ctx) - -> (sender, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CALLER, new_ctx) + DUP2 + %build_address_with_ctx_no_segment(@CTX_METADATA_CALLER) + SWAP1 + // stack: sender, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro %macro set_new_ctx_value // stack: value, new_ctx - %stack (value, new_ctx) - -> (value, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CALL_VALUE, new_ctx) + DUP2 + %build_address_with_ctx_no_segment(@CTX_METADATA_CALL_VALUE) + SWAP1 + // stack: value, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro %macro set_new_ctx_code_size // stack: code_size, new_ctx - %stack (code_size, new_ctx) - -> (code_size, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CODE_SIZE, new_ctx) + DUP2 + %build_address_with_ctx_no_segment(@CTX_METADATA_CODE_SIZE) + SWAP1 + // stack: code_size, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro %macro set_new_ctx_calldata_size // stack: calldata_size, new_ctx - %stack (calldata_size, new_ctx) - -> (calldata_size, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CALLDATA_SIZE, new_ctx) + DUP2 + %build_address_with_ctx_no_segment(@CTX_METADATA_CALLDATA_SIZE) + SWAP1 + // stack: calldata_size, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro %macro set_new_ctx_gas_limit // stack: gas_limit, new_ctx - %stack (gas_limit, new_ctx) - -> (gas_limit, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_GAS_LIMIT, new_ctx) + DUP2 + %build_address_with_ctx_no_segment(@CTX_METADATA_GAS_LIMIT) + SWAP1 + // stack: gas_limit, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro %macro set_new_ctx_parent_ctx // stack: new_ctx - PUSH @CTX_METADATA_PARENT_CONTEXT - PUSH @SEGMENT_CONTEXT_METADATA - DUP3 // new_ctx + DUP1 + %build_address_with_ctx_no_segment(@CTX_METADATA_PARENT_CONTEXT) GET_CONTEXT + // stack: ctx, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro %macro set_new_ctx_parent_pc(label) // stack: new_ctx - %stack (new_ctx) - -> ($label, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_PARENT_PC, new_ctx) + DUP1 + %build_address_with_ctx_no_segment(@CTX_METADATA_PARENT_PC) + PUSH $label + // stack: label, addr, new_ctx MSTORE_GENERAL // stack: new_ctx %endmacro @@ -381,17 +401,18 @@ call_too_deep: %macro copy_mem_to_calldata // stack: new_ctx, args_offset, args_size GET_CONTEXT - %stack (ctx, new_ctx, args_offset, args_size) -> - ( - new_ctx, @SEGMENT_CALLDATA, 0, // DST - ctx, @SEGMENT_MAIN_MEMORY, args_offset, // SRC - args_size, %%after, // count, retdest - new_ctx, args_size - ) + %stack(ctx, new_ctx, args_offset, args_size) -> (ctx, @SEGMENT_MAIN_MEMORY, args_offset, args_size, %%after, new_ctx, args_size) + %build_address + // stack: SRC, args_size, %%after, new_ctx, args_size + DUP4 + %build_address_with_ctx_no_offset(@SEGMENT_CALLDATA) + // stack: DST, SRC, args_size, %%after, new_ctx, args_size %jump(memcpy_bytes) %%after: - %stack (new_ctx, args_size) -> - (args_size, new_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_CALLDATA_SIZE) + // stack: new_ctx, args_size + %build_address_with_ctx_no_segment(@CTX_METADATA_CALLDATA_SIZE) + // stack: addr, args_size + SWAP1 MSTORE_GENERAL // stack: (empty) %endmacro @@ -403,13 +424,12 @@ call_too_deep: // stack: returndata_size, ret_size, new_ctx, success, ret_offset, kexit_info %min GET_CONTEXT - %stack (ctx, n, new_ctx, success, ret_offset, kexit_info) -> - ( - ctx, @SEGMENT_MAIN_MEMORY, ret_offset, // DST - ctx, @SEGMENT_RETURNDATA, 0, // SRC - n, %%after, // count, retdest - kexit_info, success - ) + %stack (ctx, n, new_ctx, success, ret_offset, kexit_info) -> (ctx, @SEGMENT_RETURNDATA, @SEGMENT_MAIN_MEMORY, ret_offset, ctx, n, %%after, kexit_info, success) + %build_address_no_offset + // stack: SRC, @SEGMENT_MAIN_MEMORY, ret_offset, ctx, n, %%after, kexit_info, success + SWAP3 + %build_address + // stack: DST, SRC, n, %%after, kexit_info, success %jump(memcpy_bytes) %%after: %endmacro diff --git a/evm/src/cpu/kernel/asm/core/call_gas.asm b/evm/src/cpu/kernel/asm/core/call_gas.asm index 69e27966..39613521 100644 --- a/evm/src/cpu/kernel/asm/core/call_gas.asm +++ b/evm/src/cpu/kernel/asm/core/call_gas.asm @@ -9,7 +9,7 @@ // Charge gas for *call opcodes and return the sub-context gas limit. // Doesn't include memory expansion costs. global call_charge_gas: - // Compute C_aaccess + // Compute C_access // stack: is_call_or_callcode, is_call_or_staticcall, cold_access, address, gas, kexit_info, value, retdest SWAP2 // stack: cold_access, is_call_or_staticcall, is_call_or_callcode, address, gas, kexit_info, value, retdest diff --git a/evm/src/cpu/kernel/asm/core/create.asm b/evm/src/cpu/kernel/asm/core/create.asm index 4756b407..80f8f461 100644 --- a/evm/src/cpu/kernel/asm/core/create.asm +++ b/evm/src/cpu/kernel/asm/core/create.asm @@ -57,6 +57,7 @@ global sys_create2: DUP5 // code_offset PUSH @SEGMENT_MAIN_MEMORY GET_CONTEXT + %build_address KECCAK_GENERAL // stack: hash, salt, create_common, value, code_offset, code_len, kexit_info @@ -99,11 +100,15 @@ global create_common: %set_new_ctx_code_size POP // Copy the code from memory to the new context's code segment. %stack (src_ctx, new_ctx, address, value, code_offset, code_len) - -> (new_ctx, @SEGMENT_CODE, 0, // DST - src_ctx, @SEGMENT_MAIN_MEMORY, code_offset, // SRC + -> (src_ctx, @SEGMENT_MAIN_MEMORY, code_offset, // SRC + new_ctx, // DST (SEGMENT_CODE == virt == 0) code_len, run_constructor, new_ctx, value, address) + %build_address + // stack: SRC, DST, code_len, run_constructor, new_ctx, value, address + SWAP1 + // stack: DST, SRC, code_len, run_constructor, new_ctx, value, address %jump(memcpy_bytes) run_constructor: @@ -144,7 +149,11 @@ after_constructor: POP // EIP-3541: Reject new contract code starting with the 0xEF byte - PUSH 0 %mload_current(@SEGMENT_RETURNDATA) %eq_const(0xEF) %jumpi(create_first_byte_ef) + PUSH @SEGMENT_RETURNDATA + GET_CONTEXT + %build_address_no_offset + MLOAD_GENERAL + %eq_const(0xEF) %jumpi(create_first_byte_ef) // Charge gas for the code size. // stack: leftover_gas, success, address, kexit_info @@ -160,9 +169,9 @@ after_constructor: %pop_checkpoint // Store the code hash of the new contract. - GET_CONTEXT %returndatasize - %stack (size, ctx) -> (ctx, @SEGMENT_RETURNDATA, 0, size) // context, segment, offset, len + PUSH @SEGMENT_RETURNDATA GET_CONTEXT %build_address_no_offset + // stack: addr, len KECCAK_GENERAL // stack: codehash, leftover_gas, success, address, kexit_info %observe_new_contract diff --git a/evm/src/cpu/kernel/asm/core/create_addresses.asm b/evm/src/cpu/kernel/asm/core/create_addresses.asm index 70f57b6f..1cd50f66 100644 --- a/evm/src/cpu/kernel/asm/core/create_addresses.asm +++ b/evm/src/cpu/kernel/asm/core/create_addresses.asm @@ -14,10 +14,7 @@ global get_create_address: %encode_rlp_scalar // stack: rlp_pos, rlp_start, retdest %prepend_rlp_list_prefix - // stack: rlp_prefix_start, rlp_len, retdest - PUSH @SEGMENT_RLP_RAW - PUSH 0 // context - // stack: RLP_ADDR: 3, rlp_len, retdest + // stack: RLP_ADDR, rlp_len, retdest KECCAK_GENERAL // stack: hash, retdest %u256_to_addr @@ -41,19 +38,23 @@ global get_create_address: global get_create2_address: // stack: sender, code_hash, salt, retdest PUSH 0xff PUSH 0 %mstore_kernel_general - %stack (sender, code_hash, salt, retdest) -> (0, @SEGMENT_KERNEL_GENERAL, 1, sender, 20, get_create2_address_contd, salt, code_hash, retdest) + %stack (sender, code_hash, salt, retdest) -> (@SEGMENT_KERNEL_GENERAL, 1, sender, 20, get_create2_address_contd, salt, code_hash, retdest) + ADD %jump(mstore_unpacking) get_create2_address_contd: POP - %stack (salt, code_hash, retdest) -> (0, @SEGMENT_KERNEL_GENERAL, 21, salt, 32, get_create2_address_contd2, code_hash, retdest) + %stack (salt, code_hash, retdest) -> (@SEGMENT_KERNEL_GENERAL, 21, salt, 32, get_create2_address_contd2, code_hash, retdest) + ADD %jump(mstore_unpacking) get_create2_address_contd2: POP - %stack (code_hash, retdest) -> (0, @SEGMENT_KERNEL_GENERAL, 53, code_hash, 32, get_create2_address_finish, retdest) + %stack (code_hash, retdest) -> (@SEGMENT_KERNEL_GENERAL, 53, code_hash, 32, get_create2_address_finish, retdest) + ADD %jump(mstore_unpacking) get_create2_address_finish: POP - %stack (retdest) -> (0, @SEGMENT_KERNEL_GENERAL, 0, 85, retdest) // context, segment, offset, len + %stack (retdest) -> (@SEGMENT_KERNEL_GENERAL, 85, retdest) // offset == context == 0 + // addr, len, retdest KECCAK_GENERAL // stack: hash, retdest %u256_to_addr diff --git a/evm/src/cpu/kernel/asm/core/create_receipt.asm b/evm/src/cpu/kernel/asm/core/create_receipt.asm index 16a4fcaa..9f7cb9f8 100644 --- a/evm/src/cpu/kernel/asm/core/create_receipt.asm +++ b/evm/src/cpu/kernel/asm/core/create_receipt.asm @@ -55,8 +55,8 @@ process_receipt_after_bloom: %get_trie_data_size // stack: receipt_ptr, payload_len, status, new_cum_gas, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest // Write transaction type if necessary. RLP_RAW contains, at index 0, the current transaction type. - PUSH 0 - %mload_kernel(@SEGMENT_RLP_RAW) + PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 + MLOAD_GENERAL // stack: first_txn_byte, receipt_ptr, payload_len, status, new_cum_gas, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest DUP1 %eq_const(1) %jumpi(receipt_nonzero_type) DUP1 %eq_const(2) %jumpi(receipt_nonzero_type) @@ -79,8 +79,10 @@ process_receipt_after_type: // stack: receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest // Write Bloom filter. PUSH 256 // Bloom length. - PUSH 0 PUSH @SEGMENT_TXN_BLOOM PUSH 0 // Bloom memory address. - %get_trie_data_size PUSH @SEGMENT_TRIE_DATA PUSH 0 // MPT dest address. + PUSH @SEGMENT_TXN_BLOOM // ctx == virt == 0 + // stack: bloom_addr, 256, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest + %get_trie_data_size + PUSH @SEGMENT_TRIE_DATA ADD // MPT dest address. // stack: DST, SRC, 256, receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest %memcpy_bytes // stack: receipt_ptr, txn_nb, new_cum_gas, txn_nb, num_nibbles, retdest @@ -204,16 +206,14 @@ process_receipt_after_write: %mpt_insert_receipt_trie // stack: new_cum_gas, txn_nb, num_nibbles, retdest // Now, we set the Bloom filter back to 0. We proceed by chunks of 32 bytes. - PUSH 0 + PUSH @SEGMENT_TXN_BLOOM // ctx == offset == 0 %rep 8 - // stack: counter, new_cum_gas, txn_nb, num_nibbles, retdest + // stack: addr, new_cum_gas, txn_nb, num_nibbles, retdest PUSH 0 // we will fill the memory segment with zeroes DUP2 - PUSH @SEGMENT_TXN_BLOOM - DUP3 // kernel context is 0 - // stack: ctx, segment, counter, 0, counter, new_cum_gas, txn_nb, num_nibbles, retdes + // stack: addr, 0, addr, new_cum_gas, txn_nb, num_nibbles, retdest MSTORE_32BYTES_32 - // stack: new_counter, counter, new_cum_gas, txn_nb, num_nibbles, retdest + // stack: new_addr, addr, new_cum_gas, txn_nb, num_nibbles, retdest SWAP1 POP %endrep POP diff --git a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm index bda6f96e..a43f301a 100644 --- a/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm +++ b/evm/src/cpu/kernel/asm/core/jumpdest_analysis.asm @@ -14,7 +14,8 @@ loop: %jumpi(return) // stack: i, ctx, code_len, retdest - %stack (i, ctx) -> (ctx, @SEGMENT_CODE, i, i, ctx) + %stack (i, ctx) -> (ctx, i, i, ctx) + ADD // combine context and offset to make an address (SEGMENT_CODE == 0) MLOAD_GENERAL // stack: opcode, i, ctx, code_len, retdest @@ -26,7 +27,10 @@ loop: %jumpi(continue) // stack: JUMPDEST, i, ctx, code_len, retdest - %stack (JUMPDEST, i, ctx) -> (1, ctx, @SEGMENT_JUMPDEST_BITS, i, JUMPDEST, i, ctx) + %stack (JUMPDEST, i, ctx) -> (ctx, @SEGMENT_JUMPDEST_BITS, i, JUMPDEST, i, ctx) + %build_address + PUSH 1 + // stack: 1, addr, JUMPDEST, i, ctx MSTORE_GENERAL continue: diff --git a/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm b/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm index 01c02715..500548ef 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/blake2_f.asm @@ -29,7 +29,8 @@ global precompile_blake2_f: // stack: flag_addr, flag_addr, blake2_f_contd, kexit_info PUSH @SEGMENT_CALLDATA GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, flag_addr, flag_addr, blake2_f_contd, kexit_info + %build_address + // stack: addr, flag_addr, blake2_f_contd, kexit_info MLOAD_GENERAL // stack: flag, flag_addr, blake2_f_contd, kexit_info DUP1 @@ -45,6 +46,7 @@ global precompile_blake2_f: // stack: @SEGMENT_CALLDATA, t1_addr, t1_addr, flag, blake2_f_contd, kexit_info GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, t1_addr, t1_addr, flag, blake2_f_contd, kexit_info + %build_address %mload_packing_u64_LE // stack: t_1, t1_addr, flag, blake2_f_contd, kexit_info SWAP1 @@ -56,6 +58,7 @@ global precompile_blake2_f: // stack: @SEGMENT_CALLDATA, t0_addr, t0_addr, t_1, flag, blake2_f_contd, kexit_info GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, t0_addr, t0_addr, t_1, flag, blake2_f_contd, kexit_info + %build_address %mload_packing_u64_LE // stack: t_0, t0_addr, t_1, flag, blake2_f_contd, kexit_info SWAP1 @@ -71,6 +74,7 @@ global precompile_blake2_f: // stack: @SEGMENT_CALLDATA, m0_addr + 8 * (16 - i - 1), m0_addr + 8 * (16 - i - 1), m_(i+1), ..., m_15, t_0, t_1, flag, blake2_f_contd, kexit_info GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, m0_addr + 8 * (16 - i - 1), m0_addr + 8 * (16 - i - 1), m_(i+1), ..., m_15, t_0, t_1, flag, blake2_f_contd, kexit_info + %build_address %mload_packing_u64_LE // stack: m_i, m0_addr + 8 * (16 - i - 1), m_(i+1), ..., m_15, t_0, t_1, flag, blake2_f_contd, kexit_info SWAP1 @@ -88,6 +92,7 @@ global precompile_blake2_f: // stack: @SEGMENT_CALLDATA, h0_addr + 8 * (8 - i), h0_addr + 8 * (8 - i), h_(i+1), ..., h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, h0_addr + 8 * (8 - i), h0_addr + 8 * (8 - i), h_(i+1), ..., h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info + %build_address %mload_packing_u64_LE // stack: h_i, h0_addr + 8 * (8 - i), h_(i+1), ..., h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info SWAP1 @@ -96,9 +101,10 @@ global precompile_blake2_f: // stack: h0_addr + 8 * 8 = 68, h_0, ..., h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info POP - %stack () -> (@SEGMENT_CALLDATA, 0, 4) + %stack () -> (@SEGMENT_CALLDATA, 4) GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 0, 4, h_0..h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info + // stack: ctx, @SEGMENT_CALLDATA, 4, h_0..h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info + %build_address_no_offset %mload_packing // stack: rounds, h_0..h_7, m_0..m_15, t_0, t_1, flag, blake2_f_contd, kexit_info @@ -113,20 +119,20 @@ blake2_f_contd: // Store the result hash to the parent's return data using `mstore_unpacking_u64_LE`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 64) - PUSH 0 - // stack: addr_0=0, h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', kexit_info + // stack: h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', kexit_info + PUSH @SEGMENT_RETURNDATA %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - // stack: parent_ctx, addr_0=0, h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', kexit_info + // stack: parent_ctx, segment, h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', kexit_info + %build_address_no_offset + // stack: addr0=0, h_0', h_1', h_2', h_3', h_4', h_5', h_6', h_7', kexit_info %rep 8 - // stack: parent_ctx, addr_i, h_i', ..., h_7', kexit_info - %stack (ctx, addr, h_i) -> (ctx, @SEGMENT_RETURNDATA, addr, h_i, addr, ctx) + // stack: addri, h_i', ..., h_7', kexit_info + %stack (addr, h_i) -> (addr, h_i, addr) %mstore_unpacking_u64_LE - // stack: addr_i, parent_ctx, h_(i+1)', ..., h_7', kexit_info + // stack: addr_i, h_(i+1)', ..., h_7', kexit_info %add_const(8) - // stack: addr_(i+1), parent_ctx, h_(i+1)', ..., h_7', kexit_info - SWAP1 - // stack: parent_ctx, addr_(i+1), h_(i+1)', ..., h_7', kexit_info + // stack: addr_(i+1), h_(i+1)', ..., h_7', kexit_info %endrep // stack: kexit_info diff --git a/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm b/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm index 1dafbe8a..dcd641a3 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/bn_add.asm @@ -20,21 +20,25 @@ global precompile_bn_add: %stack () -> (@SEGMENT_CALLDATA, 96, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 96, 32, bn_add_return, kexit_info + %build_address %mload_packing // stack: y1, bn_add_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 64, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 64, 32, y1, bn_add_return, kexit_info + %build_address %mload_packing // stack: x1, y1, bn_add_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 32, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 32, 32, x1, y1, bn_add_return, kexit_info + %build_address %mload_packing // stack: y0, x1, y1, bn_add_return, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 0, 32) + %stack () -> (@SEGMENT_CALLDATA, 32) GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 0, 32, y0, x1, y1, bn_add_return, kexit_info + // stack: ctx, @SEGMENT_CALLDATA, 32, y0, x1, y1, bn_add_return, kexit_info + %build_address_no_offset %mload_packing // stack: x0, y0, x1, y1, bn_add_return, kexit_info %jump(bn_add) @@ -49,9 +53,11 @@ bn_add_return: // Store the result (x, y) to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 64) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, x, y) -> (parent_ctx, @SEGMENT_RETURNDATA, 0, x, 32, bn_add_contd6, parent_ctx, y) + %stack (parent_ctx, x, y) -> (parent_ctx, @SEGMENT_RETURNDATA, x, 32, bn_add_contd6, parent_ctx, y) + %build_address_no_offset %jump(mstore_unpacking) bn_add_contd6: POP %stack (parent_ctx, y) -> (parent_ctx, @SEGMENT_RETURNDATA, 32, y, 32, pop_and_return_success) + %build_address %jump(mstore_unpacking) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm b/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm index b3865506..df2e27e9 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/bn_mul.asm @@ -20,16 +20,19 @@ global precompile_bn_mul: %stack () -> (@SEGMENT_CALLDATA, 64, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 64, 32, bn_mul_return, kexit_info + %build_address %mload_packing // stack: n, bn_mul_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 32, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 32, 32, n, bn_mul_return, kexit_info + %build_address %mload_packing // stack: y, n, bn_mul_return, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 0, 32) + %stack () -> (@SEGMENT_CALLDATA, 32) GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 0, 32, y, n, bn_mul_return, kexit_info + // stack: ctx, @SEGMENT_CALLDATA, 32, y, n, bn_mul_return, kexit_info + %build_address_no_offset %mload_packing // stack: x, y, n, bn_mul_return, kexit_info %jump(bn_mul) @@ -44,9 +47,11 @@ bn_mul_return: // Store the result (Px, Py) to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 64) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, Px, Py) -> (parent_ctx, @SEGMENT_RETURNDATA, 0, Px, 32, bn_mul_contd6, parent_ctx, Py) + %stack (parent_ctx, Px, Py) -> (parent_ctx, @SEGMENT_RETURNDATA, Px, 32, bn_mul_contd6, parent_ctx, Py) + %build_address_no_offset %jump(mstore_unpacking) bn_mul_contd6: POP %stack (parent_ctx, Py) -> (parent_ctx, @SEGMENT_RETURNDATA, 32, Py, 32, pop_and_return_success) + %build_address %jump(mstore_unpacking) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm b/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm index b38307c4..baa66196 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/ecrec.asm @@ -20,21 +20,25 @@ global precompile_ecrec: %stack () -> (@SEGMENT_CALLDATA, 96, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 96, 32, ecrec_return, kexit_info + %build_address %mload_packing // stack: s, ecrec_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 64, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 64, 32, s, ecrec_return, kexit_info + %build_address %mload_packing // stack: r, s, ecrec_return, kexit_info %stack () -> (@SEGMENT_CALLDATA, 32, 32) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 32, 32, r, s, ecrec_return, kexit_info + %build_address %mload_packing // stack: v, r, s, ecrec_return, kexit_info - %stack () -> (@SEGMENT_CALLDATA, 0, 32) + %stack () -> (@SEGMENT_CALLDATA, 32) GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 0, 32, v, r, s, ecrec_return, kexit_info + // stack: ctx, @SEGMENT_CALLDATA, 32, v, r, s, ecrec_return, kexit_info + %build_address_no_offset %mload_packing // stack: hash, v, r, s, ecrec_return, kexit_info %jump(ecrecover) @@ -45,7 +49,8 @@ ecrec_return: // Store the result address to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, address) -> (parent_ctx, @SEGMENT_RETURNDATA, 0, address, 32, pop_and_return_success) + %stack (parent_ctx, address) -> (parent_ctx, @SEGMENT_RETURNDATA, address, 32, pop_and_return_success) + %build_address_no_offset %jump(mstore_unpacking) // On bad input, return empty return data but still return success. diff --git a/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm b/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm index 2185ee2c..52bb220d 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/expmod.asm @@ -11,43 +11,43 @@ // We pass around total_num_limbs and len for conveience, because we can't access them from the stack // if they're hidden behind the variable number of limbs. mload_bytes_as_limbs: - // stack: ctx, segment, offset, num_bytes, retdest, total_num_limbs, len, ..limbs - DUP4 - // stack: num_bytes, ctx, segment, offset, num_bytes, retdest, total_num_limbs, len, ..limbs + // stack: addr, num_bytes, retdest, total_num_limbs, len, ..limbs + DUP2 + // stack: num_bytes, addr, num_bytes, retdest, total_num_limbs, len, ..limbs %mod_16 - // stack: min(16, num_bytes), ctx, segment, offset, num_bytes, retdest, total_num_limbs, len, ..limbs - %stack (len, addr: 3) -> (addr, len, addr) - // stack: ctx, segment, offset, min(16, num_bytes), ctx, segment, offset, num_bytes, retdest, total_num_limbs, len, ..limbs + // stack: min(16, num_bytes), addr, num_bytes, retdest, total_num_limbs, len, ..limbs + DUP2 + // stack: addr, min(16, num_bytes), addr, num_bytes, retdest, total_num_limbs, len, ..limbs %mload_packing - // stack: new_limb, ctx, segment, offset, num_bytes, retdest, total_num_limbs, len, ..limbs - %stack (new, addr: 3, numb, ret, tot, len) -> (numb, addr, ret, tot, len, new) - // stack: num_bytes, ctx, segment, offset, retdest, total_num_limbs, len, new_limb, ..limbs + // stack: new_limb, addr, num_bytes, retdest, total_num_limbs, len, ..limbs + %stack (new, addr, numb, ret, tot, len) -> (numb, addr, ret, tot, len, new) + // stack: num_bytes, addr, retdest, total_num_limbs, len, new_limb, ..limbs DUP1 %mod_16 - // stack: num_bytes%16, num_bytes, ctx, segment, offset, retdest, total_num_limbs, len, new_limb, ..limbs + // stack: num_bytes%16, num_bytes, addr, retdest, total_num_limbs, len, new_limb, ..limbs DUP1 SWAP2 SUB - // stack:num_bytes_new, num_bytes%16, ctx, segment, offset, retdest, total_num_limbs, len, new_limb, ..limbs + // stack: num_bytes_new, num_bytes%16, addr, retdest, total_num_limbs, len, new_limb, ..limbs DUP1 ISZERO %jumpi(mload_bytes_return) SWAP1 - // stack: num_bytes%16, num_bytes_new, ctx, segment, offset, retdest, total_num_limbs, len, new_limb, ..limbs - DUP5 // offset - ADD - // stack: offset_new, num_bytes_new, ctx, segment, offset, retdest, total_num_limbs, len, new_limb, ..limbs - SWAP4 POP - // stack: num_bytes_new, ctx, segment, offset_new, retdest, total_num_limbs, len, new_limb, ..limbs - %stack (num, addr: 3) -> (addr, num) + // stack: num_bytes%16, num_bytes_new, addr, retdest, total_num_limbs, len, new_limb, ..limbs + DUP3 // addr + ADD // increment offset + // stack: addr_new, num_bytes_new, addr, retdest, total_num_limbs, len, new_limb, ..limbs + SWAP2 POP + // stack: num_bytes_new, addr_new, retdest, total_num_limbs, len, new_limb, ..limbs + SWAP1 %jump(mload_bytes_as_limbs) mload_bytes_return: - // stack: num_bytes_new, num_bytes%16, ctx, segment, offset, retdest, total_num_limbs, len, new_limb, ..limbs - %pop5 + // stack: num_bytes_new, num_bytes%16, addr, retdest, total_num_limbs, len, new_limb, ..limbs + %pop3 // stack: retdest, total_num_limbs, len, ..limbs JUMP %macro mload_bytes_as_limbs - %stack (ctx, segment, offset, num_bytes, total_num_limbs) -> (ctx, segment, offset, num_bytes, %%after, total_num_limbs) + %stack (addr, num_bytes, total_num_limbs) -> (addr, num_bytes, %%after, total_num_limbs) %jump(mload_bytes_as_limbs) %%after: %endmacro @@ -112,6 +112,7 @@ calculate_l_E_prime: // stack: 96 + l_B, 32, l_E, l_B, retdest PUSH @SEGMENT_CALLDATA GET_CONTEXT + %build_address %mload_packing // stack: i[96 + l_B..128 + l_B], l_E, l_B, retdest %log2_floor @@ -142,6 +143,7 @@ case_le_32: // stack: 96 + l_B, l_E, retdest PUSH @SEGMENT_CALLDATA GET_CONTEXT + %build_address %mload_packing // stack: E, retdest %log2_floor @@ -165,22 +167,25 @@ global precompile_expmod: // stack: kexit_info // Load l_B from i[0..32]. - %stack () -> (@SEGMENT_CALLDATA, 0, 32) - // stack: @SEGMENT_CALLDATA, 0, 32, kexit_info + %stack () -> (@SEGMENT_CALLDATA, 32) + // stack: @SEGMENT_CALLDATA, 32, kexit_info GET_CONTEXT - // stack: ctx, @SEGMENT_CALLDATA, 0, 32, kexit_info + // stack: ctx, @SEGMENT_CALLDATA, 32, kexit_info + %build_address_no_offset %mload_packing // stack: l_B, kexit_info // Load l_E from i[32..64]. %stack () -> (@SEGMENT_CALLDATA, 32, 32) GET_CONTEXT + %build_address %mload_packing // stack: l_E, l_B, kexit_info // Load l_M from i[64..96]. %stack () -> (@SEGMENT_CALLDATA, 64, 32) GET_CONTEXT + %build_address %mload_packing // stack: l_M, l_E, l_B, kexit_info DUP3 ISZERO DUP2 ISZERO @@ -247,6 +252,7 @@ l_E_prime_return: %stack () -> (@SEGMENT_CALLDATA, 96) GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 96, num_bytes, num_limbs, len, len, l_M, l_E, l_B, kexit_info + %build_address %mload_bytes_as_limbs // stack: num_limbs, len, limbs[num_limbs-1], .., limbs[0], len, l_M, l_E, l_B, kexit_info SWAP1 @@ -282,6 +288,7 @@ copy_b_end: PUSH @SEGMENT_CALLDATA GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 96 + l_B, num_bytes, num_limbs, len, len, l_M, l_E, l_B, kexit_info + %build_address %mload_bytes_as_limbs // stack: num_limbs, len, limbs[num_limbs-1], .., limbs[0], len, l_M, l_E, l_B, kexit_info SWAP1 @@ -316,6 +323,7 @@ copy_e_end: PUSH @SEGMENT_CALLDATA GET_CONTEXT // stack: ctx, @SEGMENT_CALLDATA, 96 + l_B + l_E, num_bytes, num_limbs, len, len, l_M, l_E, l_B, kexit_info + %build_address %mload_bytes_as_limbs // stack: num_limbs, len, limbs[num_limbs-1], .., limbs[0], len, l_M, l_E, l_B, kexit_info SWAP1 @@ -410,33 +418,33 @@ expmod_contd: DUP2 DUP2 ADD - // stack: cur_address=out+l_M_128-1, end_address=out-1, l_M_128, l_M%16, kexit_info + // stack: cur_offset=out+l_M_128-1, end_offset=out-1, l_M_128, l_M%16, kexit_info DUP1 %mload_current_general - %stack (cur_limb, cur_address, end_address, l_M_128, l_M_mod16, kexit_info) -> - (@SEGMENT_RETURNDATA, 0, cur_limb, l_M_mod16, cur_address, end_address, l_M_128, kexit_info) + %stack (cur_limb, cur_offset, end_offset, l_M_128, l_M_mod16, kexit_info) -> + (@SEGMENT_RETURNDATA, cur_limb, l_M_mod16, cur_offset, end_offset, l_M_128, kexit_info) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) + %build_address_no_offset %mstore_unpacking - // stack: offset, cur_address, end_address, l_M_128, kexit_info + // stack: address, cur_offset, end_offset, l_M_128, kexit_info SWAP1 %decrement - // stack: cur_address, offset, end_address, l_M_128, kexit_info + // stack: cur_offset, address, end_offset, l_M_128, kexit_info // Store in big-endian format. expmod_store_loop: - // stack: cur_address, offset, end_address, l_M_128, kexit_info + // stack: cur_offset, address, end_offset, l_M_128, kexit_info DUP3 DUP2 EQ %jumpi(expmod_store_end) - // stack: cur_address, offset, end_address, l_M_128, kexit_info + // stack: cur_offset, address, end_offset, l_M_128, kexit_info DUP1 %mload_current_general - %stack (cur_limb, cur_address, offset, end_address, l_M_128, kexit_info) -> - (offset, cur_limb, cur_address, end_address, l_M_128, kexit_info) - %stack (offset, cur_limb) -> (@SEGMENT_RETURNDATA, offset, cur_limb, 16) - %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) + %stack (cur_limb, cur_offset, address, end_offset, l_M_128, kexit_info) -> + (address, cur_limb, cur_offset, end_offset, l_M_128, kexit_info) + %stack (address, cur_limb) -> (address, cur_limb, 16) %mstore_unpacking - // stack: offset', cur_address, end_address, l_M_128, kexit_info) + // stack: address', cur_offset, end_offset, l_M_128, kexit_info) SWAP1 %decrement - // stack: cur_address-1, offset', end_address, l_M_128, kexit_info) + // stack: cur_offset-1, address', end_offset, l_M_128, kexit_info) %jump(expmod_store_loop) expmod_store_end: - // stack: cur_address, offset, end_address, l_M_128, kexit_info + // stack: cur_offset, address, end_offset, l_M_128, kexit_info %pop4 the_end: // stack: kexit_info diff --git a/evm/src/cpu/kernel/asm/core/precompiles/id.asm b/evm/src/cpu/kernel/asm/core/precompiles/id.asm index 83cee0d0..a606ef4a 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/id.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/id.asm @@ -24,14 +24,19 @@ global precompile_id: // Simply copy the call data to the parent's return data. %calldatasize DUP1 %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE) + + PUSH id_contd SWAP1 + + PUSH @SEGMENT_CALLDATA GET_CONTEXT + %build_address_no_offset + // stack: SRC, size, id_contd + + PUSH @SEGMENT_RETURNDATA %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, ctx, size) -> - ( - parent_ctx, @SEGMENT_RETURNDATA, 0, // DST - ctx, @SEGMENT_CALLDATA, 0, // SRC - size, id_contd // count, retdest - ) + %build_address_no_offset + + // stack: DST, SRC, size, id_contd %jump(memcpy_bytes) id_contd: diff --git a/evm/src/cpu/kernel/asm/core/precompiles/main.asm b/evm/src/cpu/kernel/asm/core/precompiles/main.asm index d6cb100b..b7c916e9 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/main.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/main.asm @@ -58,7 +58,9 @@ global handle_precompiles_from_eoa: %mload_txn_field(@TXN_FIELD_DATA_LEN) %stack (calldata_size, new_ctx) -> (calldata_size, new_ctx, calldata_size) %set_new_ctx_calldata_size - %stack (new_ctx, calldata_size) -> (new_ctx, @SEGMENT_CALLDATA, 0, 0, @SEGMENT_TXN_DATA, 0, calldata_size, handle_precompiles_from_eoa_finish, new_ctx) + %stack (new_ctx, calldata_size) -> (@SEGMENT_TXN_DATA, @SEGMENT_CALLDATA, new_ctx, calldata_size, handle_precompiles_from_eoa_finish, new_ctx) + SWAP2 %build_address_no_offset // DST + // stack: DST, SRC, calldata_size, handle_precompiles_from_eoa_finish, new_ctx %jump(memcpy_bytes) handle_precompiles_from_eoa_finish: diff --git a/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm b/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm index 0e5aee8c..0231baf0 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/rip160.asm @@ -25,27 +25,17 @@ global precompile_rip160: %calldatasize GET_CONTEXT - // The next block of code is equivalent to the following %stack macro call - // (unfortunately the macro call takes too long to expand dynamically). - // - // %stack (ctx, size) -> - // ( - // ctx, @SEGMENT_KERNEL_GENERAL, 200, // DST - // ctx, @SEGMENT_CALLDATA, 0, // SRC - // size, ripemd, // count, retdest - // 200, size, rip160_contd // ripemd input: virt, num_bytes, retdest - // ) - PUSH 200 - PUSH ripemd - DUP4 - PUSH 0 - PUSH @SEGMENT_CALLDATA - PUSH rip160_contd - SWAP7 - SWAP6 - PUSH 200 - PUSH @SEGMENT_KERNEL_GENERAL - DUP3 + %stack (ctx, size) -> + ( + ctx, @SEGMENT_CALLDATA, // SRC + ctx, + size, ripemd, // count, retdest + 200, size, rip160_contd // ripemd input: virt, num_bytes, retdest + ) + %build_address_no_offset + %stack(addr, ctx) -> (ctx, @SEGMENT_KERNEL_GENERAL, 200, addr) + %build_address + // stack: DST, SRC, count, retdest, virt, num_bytes, retdest %jump(memcpy_bytes) @@ -54,5 +44,6 @@ rip160_contd: // Store the result hash to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, hash) -> (parent_ctx, @SEGMENT_RETURNDATA, 0, hash, 32, pop_and_return_success) + %stack (parent_ctx, hash) -> (parent_ctx, @SEGMENT_RETURNDATA, hash, 32, pop_and_return_success) + %build_address_no_offset %jump(mstore_unpacking) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm b/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm index 6dad0745..b537cd4c 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/sha256.asm @@ -24,30 +24,18 @@ global precompile_sha256: // Copy the call data to the kernel general segment (sha2 expects it there) and call sha2. %calldatasize GET_CONTEXT - // stack: ctx, size - // The next block of code is equivalent to the following %stack macro call - // (unfortunately the macro call takes too long to expand dynamically). - // - // %stack (ctx, size) -> - // ( - // ctx, @SEGMENT_KERNEL_GENERAL, 1, // DST - // ctx, @SEGMENT_CALLDATA, 0, // SRC - // size, sha2, // count, retdest - // 0, size, sha256_contd // sha2 input: virt, num_bytes, retdest - // ) - // - PUSH 0 - PUSH sha2 - DUP4 - PUSH 0 - PUSH @SEGMENT_CALLDATA - PUSH sha256_contd - SWAP7 - SWAP6 - PUSH 1 - PUSH @SEGMENT_KERNEL_GENERAL - DUP3 + %stack (ctx, size) -> + ( + ctx, @SEGMENT_CALLDATA, // SRC + ctx, + size, sha2, // count, retdest + 0, size, sha256_contd // sha2 input: virt, num_bytes, retdest + ) + %build_address_no_offset + %stack(addr, ctx) -> (ctx, @SEGMENT_KERNEL_GENERAL, 1, addr) + %build_address + // stack: DST, SRC, count, retdest, virt, num_bytes, retdest %jump(memcpy_bytes) @@ -56,5 +44,6 @@ sha256_contd: // Store the result hash to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, hash) -> (parent_ctx, @SEGMENT_RETURNDATA, 0, hash, 32, pop_and_return_success) + %stack (parent_ctx, hash) -> (parent_ctx, @SEGMENT_RETURNDATA, hash, 32, pop_and_return_success) + %build_address_no_offset %jump(mstore_unpacking) diff --git a/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm b/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm index f128cd51..2d990d09 100644 --- a/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm +++ b/evm/src/cpu/kernel/asm/core/precompiles/snarkv.asm @@ -31,18 +31,21 @@ loading_loop: // stack: px, i, k, kexit_info GET_CONTEXT %stack (ctx, px) -> (ctx, @SEGMENT_CALLDATA, px, 32, loading_loop_contd, px) + %build_address %jump(mload_packing) loading_loop_contd: // stack: x, px, i, k, kexit_info SWAP1 %add_const(32) GET_CONTEXT %stack (ctx, py) -> (ctx, @SEGMENT_CALLDATA, py, 32, loading_loop_contd2, py) + %build_address %jump(mload_packing) loading_loop_contd2: // stack: y, py, x, i, k, kexit_info SWAP1 %add_const(32) GET_CONTEXT %stack (ctx, px_im) -> (ctx, @SEGMENT_CALLDATA, px_im, 32, loading_loop_contd3, px_im) + %build_address %jump(mload_packing) loading_loop_contd3: // stack: x_im, px_im, y, x, i, k, kexit_info @@ -50,6 +53,7 @@ loading_loop_contd3: // stack: px_re, x_im, y, x, i, k, kexit_info GET_CONTEXT %stack (ctx, px_re) -> (ctx, @SEGMENT_CALLDATA, px_re, 32, loading_loop_contd4, px_re) + %build_address %jump(mload_packing) loading_loop_contd4: // stack: x_re, px_re, x_im, y, x, i, k, kexit_info @@ -57,6 +61,7 @@ loading_loop_contd4: // stack: py_im, x_re, x_im, y, x, i, k, kexit_info GET_CONTEXT %stack (ctx, py_im) -> (ctx, @SEGMENT_CALLDATA, py_im, 32, loading_loop_contd5, py_im) + %build_address %jump(mload_packing) loading_loop_contd5: // stack: y_im, py_im, x_re, x_im, y, x, i, k, kexit_info @@ -64,6 +69,7 @@ loading_loop_contd5: // stack: py_re, y_im, x_re, x_im, y, x, i, k, kexit_info GET_CONTEXT %stack (ctx, py_re) -> (ctx, @SEGMENT_CALLDATA, py_re, 32, loading_loop_contd6) + %build_address %jump(mload_packing) loading_loop_contd6: // stack: y_re, y_im, x_re, x_im, y, x, i, k, kexit_info @@ -118,5 +124,6 @@ got_result: // Store the result bool (repr. by a U256) to the parent's return data using `mstore_unpacking`. %mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 32) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, address) -> (parent_ctx, @SEGMENT_RETURNDATA, 0, address, 32, pop_and_return_success) + %stack (parent_ctx, address) -> (parent_ctx, @SEGMENT_RETURNDATA, address, 32, pop_and_return_success) + %build_address_no_offset %jump(mstore_unpacking) diff --git a/evm/src/cpu/kernel/asm/core/process_txn.asm b/evm/src/cpu/kernel/asm/core/process_txn.asm index df39b9d8..c70287a6 100644 --- a/evm/src/cpu/kernel/asm/core/process_txn.asm +++ b/evm/src/cpu/kernel/asm/core/process_txn.asm @@ -12,11 +12,11 @@ global process_normalized_txn: // Compute this transaction's intrinsic gas and store it. %intrinsic_gas + DUP1 %mstore_txn_field(@TXN_FIELD_INTRINSIC_GAS) - // stack: retdest + // stack: intrinsic_gas, retdest // Assert gas_limit >= intrinsic_gas. - %mload_txn_field(@TXN_FIELD_INTRINSIC_GAS) %mload_txn_field(@TXN_FIELD_GAS_LIMIT) %assert_ge(invalid_txn) @@ -146,23 +146,20 @@ global process_contract_creation_txn: // Store constructor code length PUSH @CTX_METADATA_CODE_SIZE - PUSH @SEGMENT_CONTEXT_METADATA - // stack: segment, offset, new_ctx, address, retdest - DUP3 // new_ctx + // stack: offset, new_ctx, address, retdest + DUP2 // new_ctx + ADD // CTX_METADATA_CODE_SIZE is already scaled by its segment + // stack: addr, new_ctx, address, retdest %mload_txn_field(@TXN_FIELD_DATA_LEN) - // stack: data_len, new_ctx, segment, offset, new_ctx, address, retdest + // stack: data_len, addr, new_ctx, address, retdest MSTORE_GENERAL // stack: new_ctx, address, retdest // Copy the code from txdata to the new context's code segment. PUSH process_contract_creation_txn_after_code_loaded %mload_txn_field(@TXN_FIELD_DATA_LEN) - PUSH 0 // SRC.offset - PUSH @SEGMENT_TXN_DATA // SRC.segment - PUSH 0 // SRC.context - PUSH 0 // DST.offset - PUSH @SEGMENT_CODE // DST.segment - DUP8 // DST.context = new_ctx + PUSH @SEGMENT_TXN_DATA // SRC (context == offset == 0) + DUP4 // DST (segment == 0 (i.e. CODE), and offset == 0) %jump(memcpy_bytes) global process_contract_creation_txn_after_code_loaded: @@ -203,9 +200,11 @@ global process_contract_creation_txn_after_constructor: // Store the code hash of the new contract. // stack: leftover_gas, new_ctx, address, retdest, success - GET_CONTEXT %returndatasize - %stack (size, ctx) -> (ctx, @SEGMENT_RETURNDATA, 0, size) // context, segment, offset, len + PUSH @SEGMENT_RETURNDATA + GET_CONTEXT + %build_address_no_offset + // stack: addr, len KECCAK_GENERAL // stack: codehash, leftover_gas, new_ctx, address, retdest, success %observe_new_contract @@ -292,7 +291,8 @@ global process_message_txn_code_loaded: %mload_txn_field(@TXN_FIELD_DATA_LEN) %stack (calldata_size, new_ctx, retdest) -> (calldata_size, new_ctx, calldata_size, retdest) %set_new_ctx_calldata_size - %stack (new_ctx, calldata_size, retdest) -> (new_ctx, @SEGMENT_CALLDATA, 0, 0, @SEGMENT_TXN_DATA, 0, calldata_size, process_message_txn_code_loaded_finish, new_ctx, retdest) + %stack (new_ctx, calldata_size, retdest) -> (new_ctx, @SEGMENT_CALLDATA, @SEGMENT_TXN_DATA, calldata_size, process_message_txn_code_loaded_finish, new_ctx, retdest) + %build_address_no_offset // DST %jump(memcpy_bytes) process_message_txn_code_loaded_finish: diff --git a/evm/src/cpu/kernel/asm/core/terminate.asm b/evm/src/cpu/kernel/asm/core/terminate.asm index d2c16b51..9b525919 100644 --- a/evm/src/cpu/kernel/asm/core/terminate.asm +++ b/evm/src/cpu/kernel/asm/core/terminate.asm @@ -29,18 +29,26 @@ return_after_gas: // Store the return data size in the parent context's metadata. %stack (parent_ctx, kexit_info, offset, size) -> - (size, parent_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_RETURNDATA_SIZE, offset, size, parent_ctx, kexit_info) + (parent_ctx, @CTX_METADATA_RETURNDATA_SIZE, size, offset, size, parent_ctx, kexit_info) + ADD // addr (CTX offsets are already scaled by their segment) + SWAP1 + // stack: size, addr, offset, size, parent_ctx, kexit_info MSTORE_GENERAL // stack: offset, size, parent_ctx, kexit_info // Store the return data in the parent context's returndata segment. + PUSH @SEGMENT_MAIN_MEMORY GET_CONTEXT - %stack (ctx, offset, size, parent_ctx, kexit_info) -> + %build_address + + %stack (addr, size, parent_ctx, kexit_info) -> ( - parent_ctx, @SEGMENT_RETURNDATA, 0, // DST - ctx, @SEGMENT_MAIN_MEMORY, offset, // SRC + parent_ctx, @SEGMENT_RETURNDATA, // DST + addr, // SRC size, sys_return_finish, kexit_info // count, retdest, ... ) + %build_address_no_offset + // stack: DST, SRC, size, sys_return_finish, kexit_info %jump(memcpy_bytes) sys_return_finish: @@ -129,18 +137,26 @@ revert_after_gas: // Store the return data size in the parent context's metadata. %stack (parent_ctx, kexit_info, offset, size) -> - (size, parent_ctx, @SEGMENT_CONTEXT_METADATA, @CTX_METADATA_RETURNDATA_SIZE, offset, size, parent_ctx, kexit_info) + (parent_ctx, @CTX_METADATA_RETURNDATA_SIZE, size, offset, size, parent_ctx, kexit_info) + ADD // addr (CTX offsets are already scaled by their segment) + SWAP1 + // stack: size, addr, offset, size, parent_ctx, kexit_info MSTORE_GENERAL // stack: offset, size, parent_ctx, kexit_info // Store the return data in the parent context's returndata segment. + PUSH @SEGMENT_MAIN_MEMORY GET_CONTEXT - %stack (ctx, offset, size, parent_ctx, kexit_info) -> + %build_address + + %stack (addr, size, parent_ctx, kexit_info) -> ( - parent_ctx, @SEGMENT_RETURNDATA, 0, // DST - ctx, @SEGMENT_MAIN_MEMORY, offset, // SRC + parent_ctx, @SEGMENT_RETURNDATA, // DST + addr, // SRC size, sys_revert_finish, kexit_info // count, retdest, ... ) + %build_address_no_offset + // stack: DST, SRC, size, sys_revert_finish, kexit_info %jump(memcpy_bytes) sys_revert_finish: diff --git a/evm/src/cpu/kernel/asm/core/util.asm b/evm/src/cpu/kernel/asm/core/util.asm index ee33ff26..a77329bd 100644 --- a/evm/src/cpu/kernel/asm/core/util.asm +++ b/evm/src/cpu/kernel/asm/core/util.asm @@ -11,7 +11,7 @@ %macro next_context_id // stack: (empty) %mload_global_metadata(@GLOBAL_METADATA_LARGEST_CONTEXT) - %increment + %add_const(0x10000000000000000) // scale each context by 2^64 // stack: new_ctx DUP1 %mstore_global_metadata(@GLOBAL_METADATA_LARGEST_CONTEXT) @@ -83,7 +83,6 @@ SET_CONTEXT // stack: (empty) // We can now read this stack length from memory. - push @CTX_METADATA_STACK_SIZE - %mload_current(@SEGMENT_CONTEXT_METADATA) + %mload_context_metadata(@CTX_METADATA_STACK_SIZE) // stack: stack_length %endmacro diff --git a/evm/src/cpu/kernel/asm/curve/wnaf.asm b/evm/src/cpu/kernel/asm/curve/wnaf.asm index a416d1ba..f554bc64 100644 --- a/evm/src/cpu/kernel/asm/curve/wnaf.asm +++ b/evm/src/cpu/kernel/asm/curve/wnaf.asm @@ -34,8 +34,12 @@ wnaf_loop_contd: DUP2 SWAP1 SUB %stack (n, m, segment, o, retdest) -> (129, o, m, o, segment, n, retdest) SUB + // stack: i, m, o, segment, n, retdest + DUP4 GET_CONTEXT - %stack (ctx, i, m, o, segment, n, retdest) -> (m, ctx, segment, i, o, segment, n, retdest) + %build_address + // stack: addr, m, o, segment, n, retdest + SWAP1 MSTORE_GENERAL // stack: o, segment, n, retdest DUP3 ISZERO %jumpi(wnaf_end) diff --git a/evm/src/cpu/kernel/asm/main.asm b/evm/src/cpu/kernel/asm/main.asm index b495d499..42e326a0 100644 --- a/evm/src/cpu/kernel/asm/main.asm +++ b/evm/src/cpu/kernel/asm/main.asm @@ -2,9 +2,7 @@ global main: // First, hash the kernel code %mload_global_metadata(@GLOBAL_METADATA_KERNEL_LEN) PUSH 0 - PUSH 0 - PUSH 0 - // stack: context, segment, virt, len + // stack: addr, len KECCAK_GENERAL // stack: hash %mload_global_metadata(@GLOBAL_METADATA_KERNEL_HASH) @@ -13,6 +11,10 @@ global main: // Initialise the shift table %shift_table_init + + // 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) // Initialize the state, transaction and receipt trie root pointers. PROVER_INPUT(trie_ptr::state) diff --git a/evm/src/cpu/kernel/asm/memory/core.asm b/evm/src/cpu/kernel/asm/memory/core.asm index a4c99cec..3a3a17a5 100644 --- a/evm/src/cpu/kernel/asm/memory/core.asm +++ b/evm/src/cpu/kernel/asm/memory/core.asm @@ -1,39 +1,31 @@ // Load a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0). %macro mload_u32 - // stack: context, segment, offset - %stack (addr: 3) -> (addr, 4, %%after) + // stack: addr + %stack (addr) -> (addr, 4, %%after) %jump(mload_packing) %%after: %endmacro // Load a little-endian u32, consisting of 4 bytes (c_0, c_1, c_2, c_3). %macro mload_u32_LE - // stack: context, segment, offset - DUP3 - DUP3 - DUP3 + // stack: addr + DUP1 MLOAD_GENERAL - // stack: c0, context, segment, offset - DUP4 + // stack: c0, addr + DUP2 %increment - DUP4 - DUP4 MLOAD_GENERAL %shl_const(8) ADD - // stack: c0 | (c1 << 8), context, segment, offset - DUP4 + // stack: c0 | (c1 << 8), addr + DUP2 %add_const(2) - DUP4 - DUP4 MLOAD_GENERAL %shl_const(16) ADD - // stack: c0 | (c1 << 8) | (c2 << 16), context, segment, offset - SWAP3 - %add_const(3) - SWAP2 + // stack: c0 | (c1 << 8) | (c2 << 16), addr SWAP1 + %add_const(3) MLOAD_GENERAL %shl_const(24) ADD // OR @@ -42,16 +34,12 @@ // Load a little-endian u64, consisting of 8 bytes (c_0, ..., c_7). %macro mload_u64_LE - // stack: context, segment, offset - DUP3 - DUP3 - DUP3 + // stack: addr + DUP1 %mload_u32_LE - // stack: lo, context, segment, offset - SWAP3 - %add_const(4) - SWAP2 + // stack: lo, addr SWAP1 + %add_const(4) %mload_u32_LE // stack: hi, lo %shl_const(32) @@ -62,16 +50,16 @@ // Load a big-endian u256. %macro mload_u256 - // stack: context, segment, offset - %stack (addr: 3) -> (addr, 32, %%after) + // stack: addr + %stack (addr) -> (addr, 32, %%after) %jump(mload_packing) %%after: %endmacro // Store a big-endian u32, consisting of 4 bytes (c_3, c_2, c_1, c_0). %macro mstore_u32 - // stack: context, segment, offset, value - %stack (addr: 3, value) -> (addr, value, 4, %%after) + // stack: addr, value + %stack (addr, value) -> (addr, value, 4, %%after) %jump(mstore_unpacking) %%after: // stack: offset @@ -88,6 +76,7 @@ // stack: segment, offset GET_CONTEXT // stack: context, segment, offset + %build_address MLOAD_GENERAL // stack: value %endmacro @@ -102,7 +91,8 @@ // stack: segment, offset, value GET_CONTEXT // stack: context, segment, offset, value - %stack(context, segment, offset, value) -> (value, context, segment, offset) + %build_address + SWAP1 MSTORE_GENERAL // stack: (empty) %endmacro @@ -115,7 +105,8 @@ // stack: segment, offset, value GET_CONTEXT // stack: context, segment, offset, value - %stack(context, segment, offset, value) -> (value, context, segment, offset) + %build_address + SWAP1 MSTORE_GENERAL // stack: (empty) %endmacro @@ -123,7 +114,10 @@ // Load a single byte from user code. %macro mload_current_code // stack: offset - %mload_current(@SEGMENT_CODE) + // SEGMENT_CODE == 0 + GET_CONTEXT ADD + // stack: addr + MLOAD_GENERAL // stack: value %endmacro @@ -141,6 +135,7 @@ // stack: segment, offset GET_CONTEXT // stack: context, segment, offset + %build_address %mload_u32 // stack: value %endmacro @@ -152,6 +147,7 @@ // stack: segment, offset GET_CONTEXT // stack: context, segment, offset + %build_address %mload_u32_LE // stack: value %endmacro @@ -163,6 +159,7 @@ // stack: segment, offset GET_CONTEXT // stack: context, segment, offset + %build_address %mload_u64_LE // stack: value %endmacro @@ -174,6 +171,7 @@ // stack: segment, offset GET_CONTEXT // stack: context, segment, offset + %build_address %mload_u256 // stack: value %endmacro @@ -185,7 +183,8 @@ // stack: segment, offset, value GET_CONTEXT // stack: context, segment, offset, value - %stack(context, segment, offset, value) -> (value, context, segment, offset) + %build_address + SWAP1 MSTORE_GENERAL // stack: (empty) %endmacro @@ -205,6 +204,7 @@ // stack: segment, offset, value GET_CONTEXT // stack: context, segment, offset, value + %build_address %mstore_u32 // stack: (empty) %endmacro @@ -224,8 +224,7 @@ // stack: offset PUSH $segment // stack: segment, offset - PUSH 0 // kernel has context 0 - // stack: context, segment, offset + %build_kernel_address MLOAD_GENERAL // stack: value %endmacro @@ -235,9 +234,9 @@ // stack: offset, value PUSH $segment // stack: segment, offset, value - PUSH 0 // kernel has context 0 - // stack: context, segment, offset, value - %stack(context, segment, offset, value) -> (value, context, segment, offset) + %build_kernel_address + // stack: addr, value + SWAP1 MSTORE_GENERAL // stack: (empty) %endmacro @@ -249,9 +248,9 @@ // stack: offset, value PUSH $segment // stack: segment, offset, value - PUSH 0 // kernel has context 0 - // stack: context, segment, offset, value - %stack(context, segment, offset, value) -> (value, context, segment, offset) + %build_kernel_address + // stack: addr, value + SWAP1 MSTORE_GENERAL // stack: (empty) %endmacro @@ -261,8 +260,7 @@ // stack: offset PUSH $segment // stack: segment, offset - PUSH 0 // kernel has context 0 - // stack: context, segment, offset + %build_kernel_address %mload_u32 %endmacro @@ -271,8 +269,7 @@ // stack: offset PUSH $segment // stack: segment, offset - PUSH 0 // kernel has context 0 - // stack: context, segment, offset + %build_kernel_address %mload_u32_LE %endmacro @@ -281,8 +278,7 @@ // stack: offset PUSH $segment // stack: segment, offset - PUSH 0 // kernel has context 0 - // stack: context, segment, offset + %build_kernel_address %mload_u64_LE %endmacro @@ -291,8 +287,7 @@ // stack: offset PUSH $segment // stack: segment, offset - PUSH 0 // kernel has context 0 - // stack: context, segment, offset + %build_kernel_address %mload_u256 %endmacro @@ -302,15 +297,16 @@ // stack: offset, value PUSH $segment // stack: segment, offset, value - PUSH 0 // kernel has context 0 - // stack: context, segment, offset, value + %build_kernel_address + // stack: addr, value %mstore_u32 %endmacro // Load a single byte from kernel code. %macro mload_kernel_code // stack: offset - %mload_kernel(@SEGMENT_CODE) + // ctx == SEGMENT_CODE == 0 + MLOAD_GENERAL // stack: value %endmacro @@ -327,7 +323,8 @@ // from kernel code. %macro mload_kernel_code_u32 // stack: offset - %mload_kernel_u32(@SEGMENT_CODE) + // ctx == SEGMENT_CODE == 0 + %mload_u32 // stack: value %endmacro @@ -338,7 +335,8 @@ PUSH $label ADD // stack: offset - %mload_kernel_u32(@SEGMENT_CODE) + // ctx == SEGMENT_CODE == 0 + %mload_u32 // stack: value %endmacro @@ -383,7 +381,8 @@ // Load a u256 (big-endian) from kernel code. %macro mload_kernel_code_u256 // stack: offset - %mload_kernel_u256(@SEGMENT_CODE) + // ctx == SEGMENT_CODE == 0 + %mload_u256 // stack: value %endmacro @@ -397,7 +396,8 @@ // Store a single byte to kernel code. %macro mstore_kernel_code // stack: offset, value - %mstore_kernel(@SEGMENT_CODE) + // ctx == SEGMENT_CODE == 0 + MLOAD_GENERAL // stack: (empty) %endmacro @@ -405,13 +405,15 @@ // to kernel code. %macro mstore_kernel_code_u32 // stack: offset, value - %mstore_kernel_u32(@SEGMENT_CODE) + // ctx == SEGMENT_CODE == 0 + %mstore_u32 %endmacro // Store a single byte to @SEGMENT_RLP_RAW. %macro mstore_rlp - // stack: offset, value - %mstore_kernel(@SEGMENT_RLP_RAW) + // stack: addr, value + SWAP1 + MSTORE_GENERAL // stack: (empty) %endmacro diff --git a/evm/src/cpu/kernel/asm/memory/memcpy.asm b/evm/src/cpu/kernel/asm/memory/memcpy.asm index 9dd2305d..a7819bf6 100644 --- a/evm/src/cpu/kernel/asm/memory/memcpy.asm +++ b/evm/src/cpu/kernel/asm/memory/memcpy.asm @@ -1,25 +1,16 @@ -// Copies `count` values from -// SRC = (src_ctx, src_segment, src_addr) -// to -// DST = (dst_ctx, dst_segment, dst_addr). -// These tuple definitions are used for brevity in the stack comments below. +// Copies `count` values from SRC to DST. global memcpy: // stack: DST, SRC, count, retdest - DUP7 + DUP3 // stack: count, DST, SRC, count, retdest ISZERO // stack: count == 0, DST, SRC, count, retdest %jumpi(memcpy_finish) // stack: DST, SRC, count, retdest - DUP3 - DUP3 - DUP3 + DUP1 - // Copy the next value - // stack: DST, DST, SRC, count, retdest - DUP9 - DUP9 - DUP9 + // Copy the next value. + DUP3 // stack: SRC, DST, DST, SRC, count, retdest MLOAD_GENERAL // stack: value, DST, DST, SRC, count, retdest @@ -27,23 +18,19 @@ global memcpy: // stack: DST, SRC, count, retdest // Increment dst_addr. - SWAP2 %increment - SWAP2 // Increment src_addr. - SWAP5 + SWAP1 %increment - SWAP5 + SWAP1 // Decrement count. - SWAP6 - %decrement - SWAP6 + PUSH 1 DUP4 SUB SWAP3 POP // Continue the loop. %jump(memcpy) %macro memcpy - %stack (dst: 3, src: 3, count) -> (dst, src, count, %%after) + %stack (dst, src, count) -> (dst, src, count, %%after) %jump(memcpy) %%after: %endmacro @@ -53,7 +40,7 @@ global memcpy_bytes: // stack: DST, SRC, count, retdest // Handle small case - DUP7 + DUP3 // stack: count, DST, SRC, count, retdest %lt_const(0x21) // stack: count <= 32, DST, SRC, count, retdest @@ -61,31 +48,22 @@ global memcpy_bytes: // We will pack 32 bytes into a U256 from the source, and then unpack it at the destination. // Copy the next chunk of bytes. + // stack: DST, SRC, count, retdest PUSH 32 - DUP7 - DUP7 - DUP7 + DUP3 // stack: SRC, 32, DST, SRC, count, retdest MLOAD_32BYTES // stack: value, DST, SRC, count, retdest - DUP4 - DUP4 - DUP4 - // stack: DST, value, DST, SRC, count, retdest + SWAP1 + // stack: DST, value, SRC, count, retdest MSTORE_32BYTES_32 - // stack: new_offset, DST, SRC, count, retdest - // Increment dst_addr by 32. - SWAP3 - POP - // stack: DST, SRC, count, retdest - // Increment src_addr by 32. - SWAP5 + // stack: DST', SRC, count, retdest + // Increment SRC by 32. + SWAP1 %add_const(0x20) - SWAP5 + SWAP1 // Decrement count by 32. - SWAP6 - %sub_const(0x20) - SWAP6 + PUSH 32 DUP4 SUB SWAP3 POP // Continue the loop. %jump(memcpy_bytes) @@ -94,7 +72,7 @@ memcpy_bytes_finish: // stack: DST, SRC, count, retdest // Handle empty case - DUP7 + DUP3 // stack: count, DST, SRC, count, retdest ISZERO // stack: count == 0, DST, SRC, count, retdest @@ -103,17 +81,13 @@ memcpy_bytes_finish: // stack: DST, SRC, count, retdest // Copy the last chunk of `count` bytes. - DUP7 + DUP3 DUP1 - DUP8 - DUP8 - DUP8 + DUP4 // stack: SRC, count, count, DST, SRC, count, retdest MLOAD_32BYTES // stack: value, count, DST, SRC, count, retdest - DUP5 - DUP5 - DUP5 + DUP3 // stack: DST, value, count, DST, SRC, count, retdest %mstore_unpacking // stack: new_offset, DST, SRC, count, retdest @@ -121,12 +95,12 @@ memcpy_bytes_finish: memcpy_finish: // stack: DST, SRC, count, retdest - %pop7 + %pop3 // stack: retdest JUMP %macro memcpy_bytes - %stack (dst: 3, src: 3, count) -> (dst, src, count, %%after) + %stack (dst, src, count) -> (dst, src, count, %%after) %jump(memcpy_bytes) %%after: %endmacro diff --git a/evm/src/cpu/kernel/asm/memory/memset.asm b/evm/src/cpu/kernel/asm/memory/memset.asm index 97e5dae8..792aeabc 100644 --- a/evm/src/cpu/kernel/asm/memory/memset.asm +++ b/evm/src/cpu/kernel/asm/memory/memset.asm @@ -1,11 +1,9 @@ -// Sets `count` values to 0 at -// DST = (dst_ctx, dst_segment, dst_addr). -// This tuple definition is used for brevity in the stack comments below. +// Sets `count` values to 0 at DST. global memset: // stack: DST, count, retdest // Handle small case - DUP4 + DUP2 // stack: count, DST, count, retdest %lt_const(0x21) // stack: count <= 32, DST, count, retdest @@ -13,20 +11,12 @@ global memset: // stack: DST, count, retdest PUSH 0 - DUP4 - DUP4 - DUP4 - // stack: DST, 0, DST, count, retdest + SWAP1 + // stack: DST, 0, count, retdest MSTORE_32BYTES_32 - // stack: new_offset, DST, count, retdest - - // Update dst_addr. - SWAP3 - POP + // stack: DST', count, retdest // Decrement count. - SWAP3 - %sub_const(0x20) - SWAP3 + PUSH 32 DUP3 SUB SWAP2 POP // Continue the loop. %jump(memset) @@ -35,27 +25,25 @@ memset_finish: // stack: DST, final_count, retdest // Handle empty case - DUP4 + DUP2 // stack: final_count, DST, final_count, retdest ISZERO // stack: final_count == 0, DST, final_count, retdest %jumpi(memset_bytes_empty) // stack: DST, final_count, retdest - DUP4 + DUP2 PUSH 0 - DUP5 - DUP5 - DUP5 + DUP3 // stack: DST, 0, final_count, DST, final_count, retdest %mstore_unpacking - // stack: new_offset, DST, final_count, retdest - %pop5 + // stack: DST, final_count, retdest + %pop3 // stack: retdest JUMP memset_bytes_empty: // stack: DST, 0, retdest - %pop4 + %pop2 // stack: retdest JUMP diff --git a/evm/src/cpu/kernel/asm/memory/metadata.asm b/evm/src/cpu/kernel/asm/memory/metadata.asm index 625b57f1..dfbfb646 100644 --- a/evm/src/cpu/kernel/asm/memory/metadata.asm +++ b/evm/src/cpu/kernel/asm/memory/metadata.asm @@ -1,62 +1,104 @@ // Load the given global metadata field from memory. %macro mload_global_metadata(field) + // Global metadata are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + // stack: (empty) PUSH $field - // stack: offset - %mload_kernel(@SEGMENT_GLOBAL_METADATA) + MLOAD_GENERAL // stack: value %endmacro // Store the given global metadata field to memory. %macro mstore_global_metadata(field) + // Global metadata are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + // stack: value PUSH $field - // stack: offset, value - %mstore_kernel(@SEGMENT_GLOBAL_METADATA) + SWAP1 + MSTORE_GENERAL // stack: (empty) %endmacro // Load the given context metadata field from memory. %macro mload_context_metadata(field) + // Context metadata are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + // stack: (empty) PUSH $field - // stack: offset - %mload_current(@SEGMENT_CONTEXT_METADATA) + GET_CONTEXT + ADD + // stack: addr + MLOAD_GENERAL // stack: value %endmacro // Store the given context metadata field to memory. %macro mstore_context_metadata(field) + // Context metadata are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + // stack: value PUSH $field - // stack: offset, value - %mstore_current(@SEGMENT_CONTEXT_METADATA) + GET_CONTEXT + ADD + // stack: addr, value + SWAP1 + MSTORE_GENERAL // stack: (empty) %endmacro // Store the given context metadata field to memory. %macro mstore_context_metadata(field, value) - PUSH $value + // Context metadata are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + PUSH $field - // stack: offset, value - %mstore_current(@SEGMENT_CONTEXT_METADATA) + GET_CONTEXT + ADD + // stack: addr + PUSH $value + // stack: value, addr + MSTORE_GENERAL // stack: (empty) %endmacro %macro mstore_parent_context_metadata(field) + // Context metadata are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + // stack: value %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx, value) -> - (value, parent_ctx, @SEGMENT_CONTEXT_METADATA, $field) + + // stack: parent_ctx, value + PUSH $field ADD + // stack: addr, value + SWAP1 MSTORE_GENERAL // stack: (empty) %endmacro %macro mstore_parent_context_metadata(field, value) + // Context metadata are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + // stack: (empty) %mload_context_metadata(@CTX_METADATA_PARENT_CONTEXT) - %stack (parent_ctx) -> - ($value, parent_ctx, @SEGMENT_CONTEXT_METADATA, $field) + + // stack: parent_ctx + PUSH $field ADD + // stack: addr + PUSH $value + // stack: value, addr MSTORE_GENERAL // stack: (empty) %endmacro diff --git a/evm/src/cpu/kernel/asm/memory/packing.asm b/evm/src/cpu/kernel/asm/memory/packing.asm index 1feeeaf8..9b2d6cdd 100644 --- a/evm/src/cpu/kernel/asm/memory/packing.asm +++ b/evm/src/cpu/kernel/asm/memory/packing.asm @@ -2,11 +2,10 @@ // decoding bytes as integers. All big-endian. // Given a pointer to some bytes in memory, pack them into a word. Assumes 0 < len <= 32. -// Pre stack: addr: 3, len, retdest +// Pre stack: addr, len, retdest // Post stack: packed_value -// NOTE: addr: 3 denotes a (context, segment, virtual) tuple global mload_packing: - // stack: addr: 3, len, retdest + // stack: addr, len, retdest MLOAD_32BYTES // stack: packed_value, retdest SWAP1 @@ -14,50 +13,50 @@ global mload_packing: JUMP %macro mload_packing - %stack (addr: 3, len) -> (addr, len, %%after) + %stack (addr, len) -> (addr, len, %%after) %jump(mload_packing) %%after: %endmacro global mload_packing_u64_LE: - // stack: context, segment, offset, retdest - DUP3 DUP3 DUP3 MLOAD_GENERAL - DUP4 %add_const(1) DUP4 DUP4 MLOAD_GENERAL %shl_const( 8) ADD - DUP4 %add_const(2) DUP4 DUP4 MLOAD_GENERAL %shl_const(16) ADD - DUP4 %add_const(3) DUP4 DUP4 MLOAD_GENERAL %shl_const(24) ADD - DUP4 %add_const(4) DUP4 DUP4 MLOAD_GENERAL %shl_const(32) ADD - DUP4 %add_const(5) DUP4 DUP4 MLOAD_GENERAL %shl_const(40) ADD - DUP4 %add_const(6) DUP4 DUP4 MLOAD_GENERAL %shl_const(48) ADD - DUP4 %add_const(7) DUP4 DUP4 MLOAD_GENERAL %shl_const(56) ADD - %stack (value, context, segment, offset, retdest) -> (retdest, value) + // stack: addr, retdest + DUP1 MLOAD_GENERAL + DUP2 %add_const(1) MLOAD_GENERAL %shl_const( 8) ADD + DUP2 %add_const(2) MLOAD_GENERAL %shl_const(16) ADD + DUP2 %add_const(3) MLOAD_GENERAL %shl_const(24) ADD + DUP2 %add_const(4) MLOAD_GENERAL %shl_const(32) ADD + DUP2 %add_const(5) MLOAD_GENERAL %shl_const(40) ADD + DUP2 %add_const(6) MLOAD_GENERAL %shl_const(48) ADD + DUP2 %add_const(7) MLOAD_GENERAL %shl_const(56) ADD + %stack (value, addr, retdest) -> (retdest, value) JUMP %macro mload_packing_u64_LE - %stack (addr: 3) -> (addr, %%after) + %stack (addr) -> (addr, %%after) %jump(mload_packing_u64_LE) %%after: %endmacro -// Pre stack: context, segment, offset, value, len, retdest -// Post stack: offset' +// Pre stack: addr, value, len, retdest +// Post stack: addr' global mstore_unpacking: - // stack: context, segment, offset, value, len, retdest - DUP5 ISZERO - // stack: len == 0, context, segment, offset, value, len, retdest + // stack: addr, value, len, retdest + DUP3 ISZERO + // stack: len == 0, addr, value, len, retdest %jumpi(mstore_unpacking_empty) - %stack(context, segment, offset, value, len, retdest) -> (len, context, segment, offset, value, retdest) + %stack(addr, value, len, retdest) -> (len, addr, value, retdest) PUSH 3 - // stack: BYTES_PER_JUMP, len, context, segment, offset, value, retdest + // stack: BYTES_PER_JUMP, len, addr, value, retdest MUL - // stack: jump_offset, context, segment, offset, value, retdest + // stack: jump_offset, addr, value, retdest PUSH mstore_unpacking_0 - // stack: mstore_unpacking_0, jump_offset, context, segment, offset, value, retdest + // stack: mstore_unpacking_0, jump_offset, addr, value, retdest ADD - // stack: address_unpacking, context, segment, offset, value, retdest + // stack: address_unpacking, addr, value, retdest JUMP mstore_unpacking_empty: - %stack(context, segment, offset, value, len, retdest) -> (retdest, offset) + %stack(addr, value, len, retdest) -> (retdest, addr) JUMP // This case can never be reached. It's only here to offset the table correctly. @@ -66,274 +65,274 @@ mstore_unpacking_0: PANIC %endrep mstore_unpacking_1: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_1 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_2: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_2 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_3: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_3 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_4: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_4 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_5: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_5 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_6: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_6 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_7: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_7 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_8: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_8 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_9: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_9 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_10: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_10 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_11: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_11 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_12: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_12 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_13: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_13 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_14: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_14 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_15: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_15 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_16: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_16 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_17: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_17 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_18: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_18 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_19: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_19 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_20: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_20 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_21: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_21 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_22: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_22 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_23: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_23 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_24: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_24 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_25: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_25 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_26: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_26 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_27: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_27 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_28: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_28 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_29: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_29 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_30: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_30 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_31: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_31 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP mstore_unpacking_32: - // stack: context, segment, offset, value, retdest + // stack: addr, value, retdest MSTORE_32BYTES_32 - // stack: offset', retdest + // stack: addr', retdest SWAP1 - // stack: retdest, offset' + // stack: retdest, addr' JUMP %macro mstore_unpacking - %stack (addr: 3, value, len) -> (addr, value, len, %%after) + %stack (addr, value, len) -> (addr, value, len, %%after) %jump(mstore_unpacking) %%after: %endmacro -// Pre stack: context, segment, offset, value, retdest -// Post stack: offset' +// Pre stack: addr, value, retdest +// Post stack: addr' global mstore_unpacking_u64_LE: - %stack (context, segment, offset, value) -> (0xff, value, context, segment, offset, context, segment, offset, value) + %stack (addr, value) -> (0xff, value, addr, addr, value) AND MSTORE_GENERAL // First byte - DUP3 %add_const(1) - %stack (new_offset, context, segment, offset, value) -> (0xff00, value, context, segment, new_offset, context, segment, offset, value) + DUP1 %add_const(1) + %stack (new_addr, addr, value) -> (0xff00, value, new_addr, addr, value) AND %shr_const(8) MSTORE_GENERAL // Second byte - DUP3 %add_const(2) - %stack (new_offset, context, segment, offset, value) -> (0xff0000, value, context, segment, new_offset, context, segment, offset, value) + DUP1 %add_const(2) + %stack (new_addr, addr, value) -> (0xff0000, value, new_addr, addr, value) AND %shr_const(16) MSTORE_GENERAL // Third byte - DUP3 %add_const(3) - %stack (new_offset, context, segment, offset, value) -> (0xff000000, value, context, segment, new_offset, context, segment, offset, value) + DUP1 %add_const(3) + %stack (new_addr, addr, value) -> (0xff000000, value, new_addr, addr, value) AND %shr_const(24) MSTORE_GENERAL // Fourth byte - DUP3 %add_const(4) - %stack (new_offset, context, segment, offset, value) -> (0xff00000000, value, context, segment, new_offset, context, segment, offset, value) + DUP1 %add_const(4) + %stack (new_addr, addr, value) -> (0xff00000000, value, new_addr, addr, value) AND %shr_const(32) MSTORE_GENERAL // Fifth byte - DUP3 %add_const(5) - %stack (new_offset, context, segment, offset, value) -> (0xff0000000000, value, context, segment, new_offset, context, segment, offset, value) + DUP1 %add_const(5) + %stack (new_addr, addr, value) -> (0xff0000000000, value, new_addr, addr, value) AND %shr_const(40) MSTORE_GENERAL // Sixth byte - DUP3 %add_const(6) - %stack (new_offset, context, segment, offset, value) -> (0xff000000000000, value, context, segment, new_offset, context, segment, offset, value) + DUP1 %add_const(6) + %stack (new_addr, addr, value) -> (0xff000000000000, value, new_addr, addr, value) AND %shr_const(48) MSTORE_GENERAL // Seventh byte - DUP3 %add_const(7) - %stack (new_offset, context, segment, offset, value) -> (0xff00000000000000, value, context, segment, new_offset, context, segment, offset, value) + DUP1 %add_const(7) + %stack (new_addr, addr, value) -> (0xff00000000000000, value, new_addr, addr, value) AND %shr_const(56) MSTORE_GENERAL // Eighth byte - %pop4 JUMP + %pop2 JUMP %macro mstore_unpacking_u64_LE - %stack (addr: 3, value) -> (addr, value, %%after) + %stack (addr, value) -> (addr, value, %%after) %jump(mstore_unpacking_u64_LE) %%after: %endmacro diff --git a/evm/src/cpu/kernel/asm/memory/syscalls.asm b/evm/src/cpu/kernel/asm/memory/syscalls.asm index 9798f424..a0af8b07 100644 --- a/evm/src/cpu/kernel/asm/memory/syscalls.asm +++ b/evm/src/cpu/kernel/asm/memory/syscalls.asm @@ -11,7 +11,8 @@ global sys_mload: %stack(kexit_info, offset) -> (offset, 32, kexit_info) PUSH @SEGMENT_MAIN_MEMORY GET_CONTEXT - // stack: addr: 3, len, kexit_info + %build_address + // stack: addr, len, kexit_info MLOAD_32BYTES %stack (value, kexit_info) -> (kexit_info, value) EXIT_KERNEL @@ -29,7 +30,8 @@ global sys_mstore: %stack(kexit_info, offset, value) -> (offset, value, kexit_info) PUSH @SEGMENT_MAIN_MEMORY GET_CONTEXT - // stack: addr: 3, value, kexit_info + %build_address + // stack: addr, value, kexit_info MSTORE_32BYTES_32 POP // stack: kexit_info @@ -60,7 +62,8 @@ global sys_calldataload: LT %jumpi(calldataload_large_offset) %stack (kexit_info, i) -> (@SEGMENT_CALLDATA, i, 32, sys_calldataload_after_mload_packing, kexit_info) GET_CONTEXT - // stack: ADDR: 3, 32, sys_calldataload_after_mload_packing, kexit_info + %build_address + // stack: addr, 32, sys_calldataload_after_mload_packing, kexit_info %jump(mload_packing) sys_calldataload_after_mload_packing: // stack: value, kexit_info @@ -113,7 +116,10 @@ wcopy_within_bounds: // stack: segment, src_ctx, kexit_info, dest_offset, offset, size GET_CONTEXT %stack (context, segment, src_ctx, kexit_info, dest_offset, offset, size) -> - (context, @SEGMENT_MAIN_MEMORY, dest_offset, src_ctx, segment, offset, size, wcopy_after, kexit_info) + (src_ctx, segment, offset, @SEGMENT_MAIN_MEMORY, dest_offset, context, size, wcopy_after, kexit_info) + %build_address + SWAP3 %build_address + // stack: DST, SRC, size, wcopy_after, kexit_info %jump(memcpy_bytes) wcopy_empty: @@ -132,6 +138,7 @@ wcopy_large_offset: GET_CONTEXT %stack (context, kexit_info, dest_offset, offset, size) -> (context, @SEGMENT_MAIN_MEMORY, dest_offset, size, wcopy_after, kexit_info) + %build_address %jump(memset) wcopy_after: @@ -241,6 +248,9 @@ extcodecopy_contd: GET_CONTEXT %stack (context, new_dest_offset, copy_size, extra_size, segment, src_ctx, kexit_info, dest_offset, offset, size) -> - (context, @SEGMENT_MAIN_MEMORY, dest_offset, src_ctx, segment, offset, copy_size, wcopy_large_offset, kexit_info, new_dest_offset, offset, extra_size) + (src_ctx, segment, offset, @SEGMENT_MAIN_MEMORY, dest_offset, context, copy_size, wcopy_large_offset, kexit_info, new_dest_offset, offset, extra_size) + %build_address + SWAP3 %build_address + // stack: DST, SRC, copy_size, wcopy_large_offset, kexit_info, new_dest_offset, offset, extra_size %jump(memcpy_bytes) %endmacro diff --git a/evm/src/cpu/kernel/asm/memory/txn_fields.asm b/evm/src/cpu/kernel/asm/memory/txn_fields.asm index e4e6b875..a8c1c078 100644 --- a/evm/src/cpu/kernel/asm/memory/txn_fields.asm +++ b/evm/src/cpu/kernel/asm/memory/txn_fields.asm @@ -1,18 +1,27 @@ // Load the given normalized transaction field from memory. %macro mload_txn_field(field) + // Transaction fields are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + // stack: (empty) PUSH $field - // stack: offset - %mload_kernel(@SEGMENT_NORMALIZED_TXN) + // stack: addr + MLOAD_GENERAL // stack: value %endmacro // Store the given normalized transaction field to memory. %macro mstore_txn_field(field) + // Transaction fields are already scaled by their corresponding segment, + // effectively making them the direct memory position to read from / + // write to. + // stack: value PUSH $field - // stack: offset, value - %mstore_kernel(@SEGMENT_NORMALIZED_TXN) + // stack: addr, value + SWAP1 + MSTORE_GENERAL // stack: (empty) %endmacro diff --git a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm index df7e1d74..dc1a9392 100644 --- a/evm/src/cpu/kernel/asm/mpt/hash/hash.asm +++ b/evm/src/cpu/kernel/asm/mpt/hash/hash.asm @@ -29,15 +29,13 @@ mpt_hash_hash_if_rlp: mpt_hash_hash_rlp: // stack: result, result_len, new_len, retdest %stack (result, result_len, new_len) - // context, segment, offset, value, len, trie_len, retdest - -> (0, @SEGMENT_RLP_RAW, 0, result, result_len, mpt_hash_hash_rlp_after_unpacking, new_len) + -> (@SEGMENT_RLP_RAW, result, result_len, mpt_hash_hash_rlp_after_unpacking, result_len, new_len) + // stack: addr, result, result_len, mpt_hash_hash_rlp_after_unpacking, result_len, new_len %jump(mstore_unpacking) mpt_hash_hash_rlp_after_unpacking: - // stack: result_len, new_len, retdest - PUSH 0 // offset - PUSH @SEGMENT_RLP_RAW // segment - PUSH 0 // context - // stack: result_addr: 3, result_len, new_len, retdest + // stack: result_addr, result_len, new_len, retdest + POP PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 + // stack: result_addr, result_len, new_len, retdest KECCAK_GENERAL // stack: hash, new_len, retdest %stack(hash, new_len, retdest) -> (retdest, hash, new_len) @@ -80,23 +78,19 @@ encode_or_hash_concrete_node: %stack (node_type, node_ptr, encode_value, cur_len) -> (node_type, node_ptr, encode_value, cur_len, maybe_hash_node) %jump(encode_node) maybe_hash_node: - // stack: result_ptr, result_len, cur_len, retdest + // stack: result_addr, result_len, cur_len, retdest DUP2 %lt_const(32) %jumpi(pack_small_rlp) // result_len >= 32, so we hash the result. - // stack: result_ptr, result_len, cur_len, retdest - PUSH @SEGMENT_RLP_RAW // segment - PUSH 0 // context - // stack: result_addr: 3, result_len, cur_len, retdest + // stack: result_addr, result_len, cur_len, retdest KECCAK_GENERAL %stack (hash, cur_len, retdest) -> (retdest, hash, 32, cur_len) JUMP pack_small_rlp: // stack: result_ptr, result_len, cur_len, retdest %stack (result_ptr, result_len, cur_len) - -> (0, @SEGMENT_RLP_RAW, result_ptr, result_len, - after_packed_small_rlp, result_len, cur_len) + -> (result_ptr, result_len, after_packed_small_rlp, result_len, cur_len) %jump(mload_packing) after_packed_small_rlp: %stack (result, result_len, cur_len, retdest) -> (retdest, result, result_len, cur_len) @@ -130,13 +124,13 @@ global encode_node_empty: // 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_pos, cur_len, retdest + // stack: rlp_start, cur_len, retdest PUSH 0x80 - // stack: 0x80, rlp_pos, cur_len, retdest + // stack: 0x80, rlp_start, cur_len, retdest DUP2 - // stack: rlp_pos, 0x80, rlp_pos, cur_len, retdest + // stack: rlp_start, 0x80, rlp_start, cur_len, retdest %mstore_rlp - %stack (rlp_pos, cur_len, retdest) -> (retdest, rlp_pos, 1, cur_len) + %stack (rlp_start, cur_len, retdest) -> (retdest, rlp_start, 1, cur_len) JUMP global encode_node_branch: @@ -244,7 +238,7 @@ encode_node_branch_prepend_prefix: %stack (result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest) -> (rlp_pos, result, result_len, %%after_unpacking, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest) - %jump(mstore_unpacking_rlp) + %jump(mstore_unpacking) %%after_unpacking: // stack: rlp_pos', rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest %endmacro @@ -284,7 +278,7 @@ encode_node_extension_after_hex_prefix: encode_node_extension_unpack: %stack (rlp_pos, rlp_start, result, result_len, node_payload_ptr, cur_len) -> (rlp_pos, result, result_len, encode_node_extension_after_unpacking, rlp_start, cur_len) - %jump(mstore_unpacking_rlp) + %jump(mstore_unpacking) encode_node_extension_after_unpacking: // stack: rlp_pos, rlp_start, cur_len, retdest %prepend_rlp_list_prefix 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 2ffefb7d..84fbb853 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 @@ -57,7 +57,7 @@ global mpt_hash_receipt_trie: %endmacro global encode_account: - // stack: rlp_pos, value_ptr, cur_len, retdest + // stack: rlp_addr, value_ptr, cur_len, retdest // First, we compute the length of the RLP data we're about to write. // We also update the length of the trie data segment. // The nonce and balance fields are variable-length, so we need to load them @@ -69,22 +69,22 @@ global encode_account: SWAP2 %add_const(4) SWAP2 // Now, we start the encoding. - // stack: rlp_pos, value_ptr, cur_len, retdest + // stack: rlp_addr, value_ptr, cur_len, retdest DUP2 %mload_trie_data // nonce = value[0] %rlp_scalar_len - // stack: nonce_rlp_len, rlp_pos, value_ptr, cur_len, retdest + // stack: nonce_rlp_len, rlp_addr, value_ptr, cur_len, retdest DUP3 %increment %mload_trie_data // balance = value[1] %rlp_scalar_len - // stack: balance_rlp_len, nonce_rlp_len, rlp_pos, value_ptr, cur_len, retdest + // stack: balance_rlp_len, nonce_rlp_len, rlp_addr, value_ptr, cur_len, retdest PUSH 66 // storage_root and code_hash fields each take 1 + 32 bytes ADD ADD - // stack: payload_len, rlp_pos, value_ptr, cur_len, retdest + // stack: payload_len, rlp_addr, value_ptr, cur_len, retdest SWAP1 - // stack: rlp_pos, payload_len, value_ptr, cur_len, retdest + // stack: rlp_addr, payload_len, value_ptr, cur_len, retdest DUP2 %rlp_list_len - // stack: list_len, rlp_pos, payload_len, value_ptr, cur_len, retdest + // stack: list_len, rlp_addr, payload_len, value_ptr, cur_len, retdest SWAP1 - // stack: rlp_pos, list_len, payload_len, value_ptr, cur_len, retdest + // stack: rlp_addr, list_len, payload_len, value_ptr, cur_len, retdest %encode_rlp_multi_byte_string_prefix // stack: rlp_pos_2, payload_len, value_ptr, cur_len, retdest %encode_rlp_list_prefix @@ -115,232 +115,237 @@ global encode_account: JUMP global encode_txn: - // stack: rlp_pos, value_ptr, cur_len, retdest + // stack: rlp_addr, value_ptr, cur_len, retdest // Load the txn_rlp_len which is at the beginning of value_ptr DUP2 %mload_trie_data - // stack: txn_rlp_len, rlp_pos, value_ptr, cur_len, retdest + // stack: txn_rlp_len, rlp_addr, value_ptr, cur_len, retdest // We need to add 1+txn_rlp_len to the length of the trie data. SWAP3 DUP4 %increment ADD - // stack: new_len, rlp_pos, value_ptr, txn_rlp_len, retdest + // stack: new_len, rlp_addr, value_ptr, txn_rlp_len, retdest SWAP3 SWAP2 %increment - // stack: txn_rlp_ptr=value_ptr+1, rlp_pos, txn_rlp_len, new_len, retdest + // stack: txn_rlp_ptr=value_ptr+1, rlp_addr, txn_rlp_len, new_len, retdest - %stack (txn_rlp_ptr, rlp_pos, txn_rlp_len) -> (rlp_pos, txn_rlp_len, txn_rlp_len, txn_rlp_ptr) + %stack (txn_rlp_ptr, rlp_addr, txn_rlp_len) -> (rlp_addr, txn_rlp_len, txn_rlp_len, txn_rlp_ptr) // Encode the txn rlp prefix - // stack: rlp_pos, txn_rlp_len, txn_rlp_len, txn_rlp_ptr, cur_len, retdest + // stack: rlp_addr, txn_rlp_len, txn_rlp_len, txn_rlp_ptr, cur_len, retdest %encode_rlp_multi_byte_string_prefix // copy txn_rlp to the new block - // stack: rlp_pos, txn_rlp_len, txn_rlp_ptr, new_len, retdest - %stack (rlp_pos, txn_rlp_len, txn_rlp_ptr) -> ( - 0, @SEGMENT_RLP_RAW, rlp_pos, // dest addr - 0, @SEGMENT_TRIE_DATA, txn_rlp_ptr, // src addr. Kernel has context 0 + // stack: rlp_addr, txn_rlp_len, txn_rlp_ptr, new_len, retdest + %stack (rlp_addr, txn_rlp_len, txn_rlp_ptr) -> ( + @SEGMENT_TRIE_DATA, txn_rlp_ptr, // src addr. Kernel has context 0 + rlp_addr, // dest addr txn_rlp_len, // mcpy len - txn_rlp_len, rlp_pos) + txn_rlp_len, rlp_addr) + %build_kernel_address + SWAP1 + // stack: DST, SRC, txn_rlp_len, txn_rlp_len, rlp_addr, new_len, retdest %memcpy_bytes ADD - // stack new_rlp_pos, new_len, retdest - %stack(new_rlp_pos, new_len, retdest) -> (retdest, new_rlp_pos, new_len) + // stack new_rlp_addr, new_len, retdest + %stack(new_rlp_addr, new_len, retdest) -> (retdest, new_rlp_addr, new_len) JUMP // We assume a receipt in memory is stored as: // [payload_len, status, cum_gas_used, bloom, logs_payload_len, num_logs, [logs]]. // A log is [payload_len, address, num_topics, [topics], data_len, [data]]. global encode_receipt: - // stack: rlp_pos, value_ptr, cur_len, retdest + // stack: rlp_addr, value_ptr, cur_len, retdest // First, we add 261 to the trie data length for all values before the logs besides the type. // These are: the payload length, the status, cum_gas_used, the bloom filter (256 elements), // the length of the logs payload and the length of the logs. SWAP2 %add_const(261) SWAP2 - // There is a double encoding! What we compute is: - // either RLP(RLP(receipt)) for Legacy transactions or RLP(txn_type||RLP(receipt)) for transactions of type 1 or 2. + // There is a double encoding! + // What we compute is: + // - either RLP(RLP(receipt)) for Legacy transactions + // - or RLP(txn_type||RLP(receipt)) for transactions of type 1 or 2. // First encode the wrapper prefix. DUP2 %mload_trie_data - // stack: first_value, rlp_pos, value_ptr, cur_len, retdest + // stack: first_value, rlp_addr, value_ptr, cur_len, retdest // The first value is either the transaction type or the payload length. // Since the receipt contains at least the 256-bytes long bloom filter, payload_len > 3. DUP1 %lt_const(3) %jumpi(encode_nonzero_receipt_type) // If we are here, then the first byte is the payload length. %rlp_list_len - // stack: rlp_receipt_len, rlp_pos, value_ptr, cur_len, retdest + // stack: rlp_receipt_len, rlp_addr, value_ptr, cur_len, retdest SWAP1 %encode_rlp_multi_byte_string_prefix - // stack: rlp_pos, value_ptr, cur_len, retdest + // stack: rlp_addr, value_ptr, cur_len, retdest encode_receipt_after_type: - // stack: rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, payload_len_ptr, cur_len, retdest // Then encode the receipt prefix. // `payload_ptr` is either `value_ptr` or `value_ptr+1`, depending on the transaction type. DUP2 %mload_trie_data - // stack: payload_len, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: payload_len, rlp_addr, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_list_prefix - // stack: rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, payload_len_ptr, cur_len, retdest // Encode status. DUP2 %increment %mload_trie_data - // stack: status, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: status, rlp_addr, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_scalar - // stack: rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, payload_len_ptr, cur_len, retdest // Encode cum_gas_used. DUP2 %add_const(2) %mload_trie_data - // stack: cum_gas_used, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: cum_gas_used, rlp_addr, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_scalar - // stack: rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, payload_len_ptr, cur_len, retdest // Encode bloom. PUSH 256 // Bloom length. - DUP3 %add_const(3) PUSH @SEGMENT_TRIE_DATA PUSH 0 // MPT src address. - DUP5 - // stack: rlp_pos, SRC, 256, rlp_pos, payload_len_ptr, cur_len, retdest + DUP3 %add_const(3) PUSH @SEGMENT_TRIE_DATA %build_kernel_address // MPT src address. + DUP3 + // stack: rlp_addr, SRC, 256, rlp_addr, payload_len_ptr, cur_len, retdest %encode_rlp_string - // stack: rlp_pos, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, old_rlp_pos, payload_len_ptr, cur_len, retdest SWAP1 POP - // stack: rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, payload_len_ptr, cur_len, retdest // Encode logs prefix. DUP2 %add_const(259) %mload_trie_data - // stack: logs_payload_len, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: logs_payload_len, rlp_addr, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_list_prefix - // stack: rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, payload_len_ptr, cur_len, retdest DUP2 %add_const(261) - // stack: logs_ptr, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: logs_ptr, rlp_addr, payload_len_ptr, cur_len, retdest DUP3 %add_const(260) %mload_trie_data - // stack: num_logs, logs_ptr, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: num_logs, logs_ptr, rlp_addr, payload_len_ptr, cur_len, retdest PUSH 0 encode_receipt_logs_loop: - // stack: i, num_logs, current_log_ptr, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: i, num_logs, current_log_ptr, rlp_addr, payload_len_ptr, cur_len, retdest DUP2 DUP2 EQ - // stack: i == num_logs, i, num_logs, current_log_ptr, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: i == num_logs, i, num_logs, current_log_ptr, rlp_addr, payload_len_ptr, cur_len, retdest %jumpi(encode_receipt_end) // We add 4 to the trie data length for the fixed size elements in the current log. SWAP5 %add_const(4) SWAP5 - // stack: i, num_logs, current_log_ptr, rlp_pos, payload_len_ptr, cur_len, retdest + // stack: i, num_logs, current_log_ptr, rlp_addr, payload_len_ptr, cur_len, retdest DUP3 DUP5 - // stack: rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest // Encode log prefix. DUP2 %mload_trie_data - // stack: payload_len, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: payload_len, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_list_prefix - // stack: rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest // Encode address. DUP2 %increment %mload_trie_data - // stack: address, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: address, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest SWAP1 %encode_rlp_160 - // stack: rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest DUP2 %add_const(2) %mload_trie_data - // stack: num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest // Encode topics prefix. DUP1 %mul_const(33) - // stack: topics_payload_len, num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: topics_payload_len, num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest DUP3 %encode_rlp_list_prefix - // stack: new_rlp_pos, num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: new_rlp_pos, num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest SWAP2 POP - // stack: num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest + // stack: num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len, retdest // Add `num_topics` to the length of the trie data segment. DUP1 SWAP9 - // stack: cur_len, num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, num_topics, retdest + // stack: cur_len, num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, num_topics, retdest ADD SWAP8 - // stack: num_topics, rlp_pos, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: num_topics, rlp_addr, current_log_ptr, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest SWAP2 %add_const(3) - // stack: topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest PUSH 0 encode_receipt_topics_loop: - // stack: j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest DUP4 DUP2 EQ - // stack: j == num_topics, j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: j == num_topics, j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest %jumpi(encode_receipt_topics_end) - // stack: j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest DUP2 DUP2 ADD %mload_trie_data - // stack: current_topic, j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: current_topic, j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest DUP4 - // stack: rlp_pos, current_topic, j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: rlp_addr, current_topic, j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest %encode_rlp_256 - // stack: new_rlp_pos, j, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: new_rlp_pos, j, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest SWAP3 POP // stack: j, topics_ptr, new_rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest %increment %jump(encode_receipt_topics_loop) encode_receipt_topics_end: - // stack: num_topics, topics_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: num_topics, topics_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest ADD - // stack: data_len_ptr, rlp_pos, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: data_len_ptr, rlp_addr, num_topics, i, num_logs, current_log_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest SWAP5 POP - // stack: rlp_pos, num_topics, i, num_logs, data_len_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest + // stack: rlp_addr, num_topics, i, num_logs, data_len_ptr, old_rlp_pos, payload_len_ptr, cur_len', retdest SWAP5 POP - // stack: num_topics, i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, cur_len', retdest + // stack: num_topics, i, num_logs, data_len_ptr, rlp_addr, payload_len_ptr, cur_len', retdest POP - // stack: i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, cur_len', retdest + // stack: i, num_logs, data_len_ptr, rlp_addr, payload_len_ptr, cur_len', retdest // Encode data prefix. DUP3 %mload_trie_data - // stack: data_len, i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, cur_len', retdest + // stack: data_len, i, num_logs, data_len_ptr, rlp_addr, payload_len_ptr, cur_len', retdest // Add `data_len` to the length of the trie data. DUP1 SWAP7 ADD SWAP6 - // stack: data_len, i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest + // stack: data_len, i, num_logs, data_len_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest DUP4 %increment DUP2 ADD - // stack: next_log_ptr, data_len, i, num_logs, data_len_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest + // stack: next_log_ptr, data_len, i, num_logs, data_len_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest SWAP4 %increment - // stack: data_ptr, data_len, i, num_logs, next_log_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest - PUSH @SEGMENT_TRIE_DATA PUSH 0 - // stack: SRC, data_len, i, num_logs, next_log_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest - DUP8 - // stack: rlp_pos, SRC, data_len, i, num_logs, next_log_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest + // stack: data_ptr, data_len, i, num_logs, next_log_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest + PUSH @SEGMENT_TRIE_DATA %build_kernel_address + // stack: SRC, data_len, i, num_logs, next_log_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest + DUP6 + // stack: rlp_addr, SRC, data_len, i, num_logs, next_log_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest %encode_rlp_string - // stack: new_rlp_pos, i, num_logs, next_log_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest + // stack: new_rlp_pos, i, num_logs, next_log_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest SWAP4 POP // stack: i, num_logs, next_log_ptr, new_rlp_pos, payload_len_ptr, cur_len'', retdest %increment %jump(encode_receipt_logs_loop) encode_receipt_end: - // stack: num_logs, num_logs, current_log_ptr, rlp_pos, payload_len_ptr, cur_len'', retdest + // stack: num_logs, num_logs, current_log_ptr, rlp_addr, payload_len_ptr, cur_len'', retdest %pop3 - // stack: rlp_pos, payload_len_ptr, cur_len'', retdest + // stack: rlp_addr, payload_len_ptr, cur_len'', retdest SWAP1 POP - // stack: rlp_pos, cur_len'', retdest - %stack(rlp_pos, new_len, retdest) -> (retdest, rlp_pos, new_len) + // stack: rlp_addr, cur_len'', retdest + %stack(rlp_addr, new_len, retdest) -> (retdest, rlp_addr, new_len) JUMP encode_nonzero_receipt_type: - // stack: txn_type, rlp_pos, value_ptr, cur_len, retdest + // stack: txn_type, rlp_addr, value_ptr, cur_len, retdest // We have a nonlegacy receipt, so the type is also stored in the trie data segment. SWAP3 %increment SWAP3 - // stack: txn_type, rlp_pos, value_ptr, cur_len, retdest + // stack: txn_type, rlp_addr, value_ptr, cur_len, retdest DUP3 %increment %mload_trie_data - // stack: payload_len, txn_type, rlp_pos, value_ptr, retdest + // stack: payload_len, txn_type, rlp_addr, value_ptr, retdest // The transaction type is encoded in 1 byte %increment %rlp_list_len - // stack: rlp_receipt_len, txn_type, rlp_pos, value_ptr, retdest + // stack: rlp_receipt_len, txn_type, rlp_addr, value_ptr, retdest DUP3 %encode_rlp_multi_byte_string_prefix - // stack: rlp_pos, txn_type, old_rlp_pos, value_ptr, retdest + // stack: rlp_addr, txn_type, old_rlp_addr, value_ptr, retdest DUP2 DUP2 %mstore_rlp %increment - // stack: rlp_pos, txn_type, old_rlp_pos, value_ptr, retdest - %stack (rlp_pos, txn_type, old_rlp_pos, value_ptr, retdest) -> (rlp_pos, value_ptr, retdest) + // stack: rlp_addr, txn_type, old_rlp_addr, value_ptr, retdest + %stack (rlp_addr, txn_type, old_rlp_addr, value_ptr, retdest) -> (rlp_addr, value_ptr, retdest) // We replace `value_ptr` with `paylaod_len_ptr` so we can encode the rest of the data more easily SWAP1 %increment SWAP1 - // stack: rlp_pos, payload_len_ptr, retdest + // stack: rlp_addr, payload_len_ptr, retdest %jump(encode_receipt_after_type) global encode_storage_value: - // stack: rlp_pos, value_ptr, cur_len, retdest + // stack: rlp_addr, value_ptr, cur_len, retdest SWAP1 %mload_trie_data SWAP1 // A storage value is a scalar, so we only need to add 1 to the trie data length. SWAP2 %increment SWAP2 - // stack: rlp_pos, value, cur_len, retdest + // stack: rlp_addr, value, cur_len, retdest // The YP says storage trie is a map "... to the RLP-encoded 256-bit integer values" // 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. %doubly_encode_rlp_scalar - // stack: rlp_pos', cur_len, retdest - %stack (rlp_pos, cur_len, retdest) -> (retdest, rlp_pos, cur_len) + // stack: rlp_addr', cur_len, retdest + %stack (rlp_addr, cur_len, retdest) -> (retdest, rlp_addr, cur_len) JUMP diff --git a/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm b/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm index 5bb9fa9d..7dd02c34 100644 --- a/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm +++ b/evm/src/cpu/kernel/asm/mpt/hex_prefix.asm @@ -3,8 +3,8 @@ // 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 +// Pre stack: rlp_start_addr, num_nibbles, packed_nibbles, terminated, retdest +// Post stack: rlp_end_addr global hex_prefix_rlp: DUP2 %assert_lt_const(65) @@ -12,7 +12,7 @@ global hex_prefix_rlp: // Compute the length of the hex-prefix string, in bytes: // hp_len = num_nibbles / 2 + 1 = i + 1 %increment - // stack: hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + // stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest // Write the RLP header. DUP1 %gt_const(55) %jumpi(rlp_header_large) @@ -21,113 +21,112 @@ global hex_prefix_rlp: // 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, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + // stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest POP first_byte: - // stack: rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest // get the first nibble, if num_nibbles is odd, or zero otherwise SWAP2 - // stack: packed_nibbles, num_nibbbles, rlp_pos, terminated, retdest + // stack: packed_nibbles, num_nibbbles, rlp_addr, terminated, retdest DUP2 DUP1 %mod_const(2) - // stack: parity, num_nibbles, packed_nibbles, num_nibbles, rlp_pos, terminated, retdest + // stack: parity, num_nibbles, packed_nibbles, num_nibbles, rlp_addr, terminated, retdest SWAP1 SUB %mul_const(4) SHR - // stack: first_nibble_or_zero, num_nibbles, rlp_pos, terminated, retdest + // stack: first_nibble_or_zero, num_nibbles, rlp_addr, terminated, retdest SWAP2 - // stack: rlp_pos, num_nibbles, first_nibble_or_zero, terminated, retdest + // stack: rlp_addr, num_nibbles, first_nibble_or_zero, terminated, retdest SWAP3 - // stack: terminated, num_nibbles, first_nibble_or_zero, rlp_pos, retdest + // stack: terminated, num_nibbles, first_nibble_or_zero, rlp_addr, retdest %mul_const(2) - // stack: terminated * 2, num_nibbles, first_nibble_or_zero, rlp_pos, retdest + // stack: terminated * 2, num_nibbles, first_nibble_or_zero, rlp_addr, retdest SWAP1 - // stack: num_nibbles, terminated * 2, first_nibble_or_zero, rlp_pos, retdest + // stack: num_nibbles, terminated * 2, first_nibble_or_zero, rlp_addr, retdest %mod_const(2) // parity ADD - // stack: parity + terminated * 2, first_nibble_or_zero, rlp_pos, retdest + // stack: parity + terminated * 2, first_nibble_or_zero, rlp_addr, retdest %mul_const(16) ADD - // stack: first_byte, rlp_pos, retdest + // stack: first_byte, rlp_addr, retdest DUP2 %mstore_rlp %increment - // stack: rlp_pos', retdest + // stack: rlp_addr', retdest SWAP1 JUMP remaining_bytes: - // stack: rlp_pos, num_nibbles, packed_nibbles, retdest + // stack: rlp_addr, num_nibbles, packed_nibbles, retdest SWAP2 PUSH @U256_MAX - // stack: U256_MAX, packed_nibbles, num_nibbles, rlp_pos, ret_dest + // stack: U256_MAX, packed_nibbles, num_nibbles, rlp_addr, ret_dest SWAP1 SWAP2 DUP1 %mod_const(2) - // stack: parity, num_nibbles, U256_MAX, packed_nibbles, rlp_pos, ret_dest + // stack: parity, num_nibbles, U256_MAX, packed_nibbles, rlp_addr, ret_dest SWAP1 SUB DUP1 - // stack: num_nibbles - parity, num_nibbles - parity, U256_MAX, packed_nibbles, rlp_pos, ret_dest + // stack: num_nibbles - parity, num_nibbles - parity, U256_MAX, packed_nibbles, rlp_addr, ret_dest %div_const(2) - // stack: rem_bytes, num_nibbles - parity, U256_MAX, packed_nibbles, rlp_pos, ret_dest + // stack: rem_bytes, num_nibbles - parity, U256_MAX, packed_nibbles, rlp_addr, ret_dest SWAP2 SWAP1 - // stack: num_nibbles - parity, U256_MAX, rem_bytes, packed_nibbles, rlp_pos, ret_dest + // stack: num_nibbles - parity, U256_MAX, rem_bytes, packed_nibbles, rlp_addr, ret_dest %mul_const(4) - // stack: 4*(num_nibbles - parity), U256_MAX, rem_bytes, packed_nibbles, rlp_pos, ret_dest + // stack: 4*(num_nibbles - parity), U256_MAX, rem_bytes, packed_nibbles, rlp_addr, ret_dest PUSH 256 SUB - // stack: 256 - 4*(num_nibbles - parity), U256_MAX, rem_bytes, packed_nibbles, rlp_pos, ret_dest + // stack: 256 - 4*(num_nibbles - parity), U256_MAX, rem_bytes, packed_nibbles, rlp_addr, ret_dest SHR - // stack: mask, rem_bytes, packed_nibbles, rlp_pos, ret_dest + // stack: mask, rem_bytes, packed_nibbles, rlp_addr, ret_dest SWAP1 SWAP2 AND - %stack - (remaining_nibbles, rem_bytes, rlp_pos) -> - (rlp_pos, remaining_nibbles, rem_bytes) - %mstore_unpacking_rlp + %stack(remaining_nibbles, rem_bytes, rlp_addr) -> (rlp_addr, remaining_nibbles, rem_bytes) + %mstore_unpacking SWAP1 JUMP rlp_header_medium: - // stack: hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + // stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest %add_const(0x80) // value = 0x80 + hp_len - DUP2 // offset = rlp_pos + DUP2 %mstore_rlp - // stack: rlp_pos, num_nibbles, packed_nibbles, terminated, retdest - // rlp_pos += 1 + // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest + // rlp_addr += 1 %increment - // stack: rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest SWAP3 DUP3 DUP3 - // stack: num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_pos, retdest + // stack: num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_addr, retdest PUSH remaining_bytes - // stack: remaining_bytes, num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_pos, retdest + // stack: remaining_bytes, num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_addr, retdest SWAP4 SWAP5 SWAP6 - // stack: rlp_pos, num_nibbles, packed_nibbles, terminated, remaining_bytes, num_nibbles, packed_nibbles, retdest + // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, remaining_bytes, num_nibbles, packed_nibbles, retdest %jump(first_byte) rlp_header_large: - // stack: hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + // stack: hp_len, rlp_addr, 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 0xb8 // value = 0xb7 + len_of_len = 0xb8 - DUP3 // offset = rlp_pos + DUP3 + %mstore_rlp + // stack: rlp_addr, value, hp_len, i, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest + + // stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest + DUP2 %increment %mstore_rlp - // stack: hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest - DUP2 %increment // offset = rlp_pos + 1 - %mstore_rlp - - // stack: rlp_pos, num_nibbles, packed_nibbles, terminated, retdest - // rlp_pos += 2 + // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest + // rlp_addr += 2 %add_const(2) - // stack: rlp_pos, num_nibbles, packed_nibbles, terminated, retdest + // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, retdest SWAP3 DUP3 DUP3 - // stack: num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_pos, retdest + // stack: num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_addr, retdest PUSH remaining_bytes - // stack: remaining_bytes, num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_pos, retdest + // stack: remaining_bytes, num_nibbles, packed_nibbles, terminated, num_nibbles, packed_nibbles, rlp_addr, retdest SWAP4 SWAP5 SWAP6 - // stack: rlp_pos, num_nibbles, packed_nibbles, terminated, remaining_bytes, num_nibbles, packed_nibbles, retdest + // stack: rlp_addr, num_nibbles, packed_nibbles, terminated, remaining_bytes, num_nibbles, packed_nibbles, retdest %jump(first_byte) diff --git a/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm b/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm index 1bf9f6f8..d21e917b 100644 --- a/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm +++ b/evm/src/cpu/kernel/asm/mpt/insert/insert_trie_specific.asm @@ -71,18 +71,18 @@ mpt_insert_receipt_trie_save: global scalar_to_rlp: // stack: scalar, retdest %mload_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) - // stack: pos, scalar, retdest + // stack: init_addr, scalar, retdest SWAP1 DUP2 %encode_rlp_scalar - // stack: pos', init_pos, retdest + // stack: addr', init_addr, retdest // Now our rlp_encoding is in RlpRaw. // Set new RlpRaw data size DUP1 %mstore_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE) DUP2 DUP2 SUB // len of the key - // stack: len, pos', init_pos, retdest - DUP3 PUSH @SEGMENT_RLP_RAW PUSH 0 // address where we get the key from + // stack: len, addr', init_addr, retdest + DUP3 %mload_packing - // stack: packed_key, pos', init_pos, retdest + // stack: packed_key, addr', init_addr, retdest SWAP2 %pop2 // stack: key, retdest SWAP1 diff --git a/evm/src/cpu/kernel/asm/rlp/decode.asm b/evm/src/cpu/kernel/asm/rlp/decode.asm index dd990bbd..43c6627d 100644 --- a/evm/src/cpu/kernel/asm/rlp/decode.asm +++ b/evm/src/cpu/kernel/asm/rlp/decode.asm @@ -7,143 +7,141 @@ // assets. // Parse the length of a bytestring from RLP memory. The next len bytes after -// pos' will contain the string. +// rlp_addr' will contain the string. // -// Pre stack: pos, retdest -// Post stack: pos', len +// Pre stack: rlp_addr, retdest +// Post stack: rlp_addr', len global decode_rlp_string_len: - // stack: pos, retdest + // stack: rlp_addr, retdest DUP1 - %mload_kernel(@SEGMENT_RLP_RAW) - // stack: first_byte, pos, retdest + MLOAD_GENERAL + // stack: first_byte, rlp_addr, retdest DUP1 %gt_const(0xb7) - // stack: first_byte >= 0xb8, first_byte, pos, retdest + // stack: first_byte >= 0xb8, first_byte, rlp_addr, retdest %jumpi(decode_rlp_string_len_large) - // stack: first_byte, pos, retdest + // stack: first_byte, rlp_addr, retdest DUP1 %gt_const(0x7f) - // stack: first_byte >= 0x80, first_byte, pos, retdest + // stack: first_byte >= 0x80, first_byte, rlp_addr, retdest %jumpi(decode_rlp_string_len_medium) // String is a single byte in the range [0x00, 0x7f]. - %stack (first_byte, pos, retdest) -> (retdest, pos, 1) + %stack (first_byte, rlp_addr, retdest) -> (retdest, rlp_addr, 1) JUMP decode_rlp_string_len_medium: // String is 0-55 bytes long. First byte contains the len. - // stack: first_byte, pos, retdest + // stack: first_byte, rlp_addr, retdest %sub_const(0x80) - // stack: len, pos, retdest + // stack: len, rlp_addr, retdest SWAP1 %increment - // stack: pos', len, retdest - %stack (pos, len, retdest) -> (retdest, pos, len) + // stack: rlp_addr', len, retdest + %stack (rlp_addr, len, retdest) -> (retdest, rlp_addr, len) JUMP decode_rlp_string_len_large: // String is >55 bytes long. First byte contains the len of the len. - // stack: first_byte, pos, retdest + // stack: first_byte, rlp_addr, retdest %sub_const(0xb7) - // stack: len_of_len, pos, retdest + // stack: len_of_len, rlp_addr, retdest SWAP1 %increment - // stack: pos', len_of_len, retdest + // stack: rlp_addr', len_of_len, retdest %jump(decode_int_given_len) // Convenience macro to call decode_rlp_string_len and return where we left off. %macro decode_rlp_string_len - %stack (pos) -> (pos, %%after) + %stack (rlp_addr) -> (rlp_addr, %%after) %jump(decode_rlp_string_len) %%after: %endmacro // Parse a scalar from RLP memory. -// Pre stack: pos, retdest -// Post stack: pos', scalar +// Pre stack: rlp_addr, retdest +// Post stack: rlp_addr', scalar // // Scalars are variable-length, but this method assumes a max length of 32 // bytes, so that the result can be returned as a single word on the stack. // As per the spec, scalars must not have leading zeros. global decode_rlp_scalar: - // stack: pos, retdest + // stack: rlp_addr, retdest PUSH decode_int_given_len - // stack: decode_int_given_len, pos, retdest + // stack: decode_int_given_len, rlp_addr, retdest SWAP1 - // stack: pos, decode_int_given_len, retdest + // stack: rlp_addr, decode_int_given_len, retdest // decode_rlp_string_len will return to decode_int_given_len, at which point - // the stack will contain (pos', len, retdest), which are the proper args + // the stack will contain (rlp_addr', len, retdest), which are the proper args // to decode_int_given_len. %jump(decode_rlp_string_len) // Convenience macro to call decode_rlp_scalar and return where we left off. %macro decode_rlp_scalar - %stack (pos) -> (pos, %%after) + %stack (rlp_addr) -> (rlp_addr, %%after) %jump(decode_rlp_scalar) %%after: %endmacro // Parse the length of an RLP list from memory. -// Pre stack: pos, retdest -// Post stack: pos', len +// Pre stack: rlp_addr, retdest +// Post stack: rlp_addr', len global decode_rlp_list_len: - // stack: pos, retdest + // stack: rlp_addr, retdest DUP1 - %mload_kernel(@SEGMENT_RLP_RAW) - // stack: first_byte, pos, retdest + MLOAD_GENERAL + // stack: first_byte, rlp_addr, retdest SWAP1 - %increment // increment pos + %increment // increment rlp_addr SWAP1 - // stack: first_byte, pos', retdest + // stack: first_byte, rlp_addr', retdest // If first_byte is >= 0xf8, it's a > 55 byte list, and // first_byte - 0xf7 is the length of the length. DUP1 %gt_const(0xf7) // GT is native while GE is not, so compare to 0xf6 instead - // stack: first_byte >= 0xf7, first_byte, pos', retdest + // stack: first_byte >= 0xf7, first_byte, rlp_addr', retdest %jumpi(decode_rlp_list_len_big) // This is the "small list" case. // The list length is first_byte - 0xc0. - // stack: first_byte, pos', retdest + // stack: first_byte, rlp_addr', retdest %sub_const(0xc0) - // stack: len, pos', retdest - %stack (len, pos, retdest) -> (retdest, pos, len) + // stack: len, rlp_addr', retdest + %stack (len, rlp_addr, retdest) -> (retdest, rlp_addr, len) JUMP decode_rlp_list_len_big: // The length of the length is first_byte - 0xf7. - // stack: first_byte, pos', retdest + // stack: first_byte, rlp_addr', retdest %sub_const(0xf7) - // stack: len_of_len, pos', retdest + // stack: len_of_len, rlp_addr', retdest SWAP1 - // stack: pos', len_of_len, retdest + // stack: rlp_addr', len_of_len, retdest %jump(decode_int_given_len) // Convenience macro to call decode_rlp_list_len and return where we left off. %macro decode_rlp_list_len - %stack (pos) -> (pos, %%after) + %stack (rlp_addr) -> (rlp_addr, %%after) %jump(decode_rlp_list_len) %%after: %endmacro // Parse an integer of the given length. It is assumed that the integer will // fit in a single (256-bit) word on the stack. -// Pre stack: pos, len, retdest -// Post stack: pos', int +// Pre stack: rlp_addr, len, retdest +// Post stack: rlp_addr', int global decode_int_given_len: DUP2 ISZERO %jumpi(empty_int) - %stack (pos, len, retdest) -> (pos, len, pos, len, retdest) + %stack (rlp_addr, len, retdest) -> (rlp_addr, len, rlp_addr, len, retdest) ADD - %stack(pos_two, pos, len, retdest) -> (pos, len, pos_two, retdest) - PUSH @SEGMENT_RLP_RAW - PUSH 0 //context + %stack(rlp_addr_two, rlp_addr, len, retdest) -> (rlp_addr, len, rlp_addr_two, retdest) MLOAD_32BYTES - // stack: int, pos', retdest - %stack(int, pos, retdest) -> (retdest, pos, int) + // stack: int, rlp_addr', retdest + %stack(int, rlp_addr, retdest) -> (retdest, rlp_addr, int) JUMP empty_int: - // stack: pos, len, retdest - %stack(pos, len, retdest) -> (retdest, pos, 0) + // stack: rlp_addr, len, retdest + %stack(rlp_addr, len, retdest) -> (retdest, rlp_addr, 0) JUMP diff --git a/evm/src/cpu/kernel/asm/rlp/encode.asm b/evm/src/cpu/kernel/asm/rlp/encode.asm index 23b0db46..2319e780 100644 --- a/evm/src/cpu/kernel/asm/rlp/encode.asm +++ b/evm/src/cpu/kernel/asm/rlp/encode.asm @@ -1,76 +1,76 @@ // RLP-encode a fixed-length 160 bit (20 byte) string. Assumes string < 2^160. -// Pre stack: pos, string, retdest -// Post stack: pos +// Pre stack: rlp_addr, string, retdest +// Post stack: rlp_addr global encode_rlp_160: PUSH 20 %jump(encode_rlp_fixed) // Convenience macro to call encode_rlp_160 and return where we left off. %macro encode_rlp_160 - %stack (pos, string) -> (pos, string, %%after) + %stack (rlp_addr, string) -> (rlp_addr, 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: pos +// Pre stack: rlp_addr, string, retdest +// Post stack: rlp_addr global encode_rlp_256: 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) + %stack (rlp_addr, string) -> (rlp_addr, string, %%after) %jump(encode_rlp_256) %%after: %endmacro // RLP-encode a fixed-length string with the given byte length. Assumes string < 2^(8 * len). global encode_rlp_fixed: - // stack: len, pos, string, retdest + // stack: len, rlp_addr, string, retdest DUP1 %add_const(0x80) - // stack: first_byte, len, pos, string, retdest + // stack: first_byte, len, rlp_addr, string, retdest DUP3 - // stack: pos, first_byte, len, pos, string, retdest + // stack: rlp_addr, first_byte, len, rlp_addr, string, retdest %mstore_rlp - // stack: len, pos, string, retdest + // stack: len, rlp_addr, string, retdest SWAP1 - %increment // increment pos - // stack: pos, len, string, retdest - %stack (pos, len, string) -> (pos, string, len, encode_rlp_fixed_finish) - // stack: pos, string, len, encode_rlp_fixed_finish, retdest - %jump(mstore_unpacking_rlp) + %increment // increment rlp_addr + // stack: rlp_addr, len, string, retdest + %stack (rlp_addr, len, string) -> (rlp_addr, string, len, encode_rlp_fixed_finish) + // stack: rlp_addr, string, len, encode_rlp_fixed_finish, retdest + %jump(mstore_unpacking) encode_rlp_fixed_finish: - // stack: pos', retdest + // stack: rlp_addr', retdest 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 + // stack: len, rlp_addr, string, retdest DUP1 %add_const(0x81) - // stack: first_byte, len, pos, string, retdest + // stack: first_byte, len, rlp_addr, string, retdest DUP3 - // stack: pos, first_byte, len, pos, string, retdest + // stack: rlp_addr, first_byte, len, rlp_addr, string, retdest %mstore_rlp - // stack: len, pos, string, retdest + // stack: len, rlp_addr, string, retdest DUP1 %add_const(0x80) - // stack: second_byte, len, original_pos, string, retdest + // stack: second_byte, len, original_rlp_addr, string, retdest DUP3 %increment - // stack: pos', second_byte, len, pos, string, retdest + // stack: rlp_addr', second_byte, len, rlp_addr, string, retdest %mstore_rlp - // stack: len, pos, string, retdest + // stack: len, rlp_addr, 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) + // stack: rlp_addr'', len, string, retdest + %stack (rlp_addr, len, string) -> (rlp_addr, string, len, encode_rlp_fixed_finish) + // stack: context, segment, rlp_addr'', string, len, encode_rlp_fixed_finish, retdest + %jump(mstore_unpacking) // 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 @@ -78,156 +78,156 @@ global doubly_encode_rlp_fixed: // length. This method should generally be used only when we know a string // contains at least two bytes. // -// Pre stack: pos, str_len, retdest -// Post stack: pos' +// Pre stack: rlp_addr, str_len, retdest +// Post stack: rlp_addr' global encode_rlp_multi_byte_string_prefix: - // stack: pos, str_len, retdest + // stack: rlp_addr, str_len, retdest DUP2 %gt_const(55) - // stack: str_len > 55, pos, str_len, retdest + // stack: str_len > 55, rlp_addr, str_len, retdest %jumpi(encode_rlp_multi_byte_string_prefix_large) // Medium case; prefix is 0x80 + str_len. - // stack: pos, str_len, retdest + // stack: rlp_addr, str_len, retdest SWAP1 %add_const(0x80) - // stack: prefix, pos, retdest + // stack: prefix, rlp_addr, retdest DUP2 - // stack: pos, prefix, pos, retdest + // stack: rlp_addr, prefix, rlp_addr, retdest %mstore_rlp - // stack: pos, retdest + // stack: rlp_addr, retdest %increment - // stack: pos', retdest + // stack: rlp_addr', retdest SWAP1 JUMP encode_rlp_multi_byte_string_prefix_large: // Large case; prefix is 0xb7 + len_of_len, followed by str_len. - // stack: pos, str_len, retdest + // stack: rlp_addr, str_len, retdest DUP2 %num_bytes - // stack: len_of_len, pos, str_len, retdest + // stack: len_of_len, rlp_addr, str_len, retdest SWAP1 DUP2 // len_of_len %add_const(0xb7) - // stack: first_byte, pos, len_of_len, str_len, retdest + // stack: first_byte, rlp_addr, len_of_len, str_len, retdest DUP2 - // stack: pos, first_byte, pos, len_of_len, str_len, retdest + // stack: rlp_addr, first_byte, rlp_addr, len_of_len, str_len, retdest %mstore_rlp - // stack: pos, len_of_len, str_len, retdest + // stack: rlp_addr, len_of_len, str_len, retdest %increment - // stack: pos', len_of_len, str_len, retdest - %stack (pos, len_of_len, str_len) -> (pos, str_len, len_of_len) - %jump(mstore_unpacking_rlp) + // stack: rlp_addr', len_of_len, str_len, retdest + %stack (rlp_addr, len_of_len, str_len) -> (rlp_addr, str_len, len_of_len) + %jump(mstore_unpacking) %macro encode_rlp_multi_byte_string_prefix - %stack (pos, str_len) -> (pos, str_len, %%after) + %stack (rlp_addr, str_len) -> (rlp_addr, str_len, %%after) %jump(encode_rlp_multi_byte_string_prefix) %%after: %endmacro // Writes the RLP prefix for a list with the given payload length. // -// Pre stack: pos, payload_len, retdest -// Post stack: pos' +// Pre stack: rlp_addr, payload_len, retdest +// Post stack: rlp_addr' global encode_rlp_list_prefix: - // stack: pos, payload_len, retdest + // stack: rlp_addr, payload_len, retdest DUP2 %gt_const(55) %jumpi(encode_rlp_list_prefix_large) // Small case: prefix is just 0xc0 + length. - // stack: pos, payload_len, retdest + // stack: rlp_addr, payload_len, retdest SWAP1 %add_const(0xc0) - // stack: prefix, pos, retdest + // stack: prefix, rlp_addr, retdest DUP2 - // stack: pos, prefix, pos, retdest + // stack: rlp_addr, prefix, rlp_addr, retdest %mstore_rlp - // stack: pos, retdest + // stack: rlp_addr, retdest %increment SWAP1 JUMP encode_rlp_list_prefix_large: // Write 0xf7 + len_of_len. - // stack: pos, payload_len, retdest + // stack: rlp_addr, payload_len, retdest DUP2 %num_bytes - // stack: len_of_len, pos, payload_len, retdest + // stack: len_of_len, rlp_addr, payload_len, retdest DUP1 %add_const(0xf7) - // stack: first_byte, len_of_len, pos, payload_len, retdest - DUP3 // pos + // stack: first_byte, len_of_len, rlp_addr, payload_len, retdest + DUP3 // rlp_addr %mstore_rlp - // stack: len_of_len, pos, payload_len, retdest + // stack: len_of_len, rlp_addr, payload_len, retdest SWAP1 %increment - // stack: pos', len_of_len, payload_len, retdest - %stack (pos, len_of_len, payload_len) - -> (pos, payload_len, len_of_len, + // stack: rlp_addr', len_of_len, payload_len, retdest + %stack (rlp_addr, len_of_len, payload_len) + -> (rlp_addr, payload_len, len_of_len, encode_rlp_list_prefix_large_done_writing_len) - %jump(mstore_unpacking_rlp) + %jump(mstore_unpacking) encode_rlp_list_prefix_large_done_writing_len: - // stack: pos'', retdest + // stack: rlp_addr'', retdest SWAP1 JUMP %macro encode_rlp_list_prefix - %stack (pos, payload_len) -> (pos, payload_len, %%after) + %stack (rlp_addr, payload_len) -> (rlp_addr, payload_len, %%after) %jump(encode_rlp_list_prefix) %%after: %endmacro -// Given an RLP list payload which starts and ends at the given positions, -// prepend the appropriate RLP list prefix. Returns the updated start position, +// Given an RLP list payload which starts and ends at the given rlp_address, +// prepend the appropriate RLP list prefix. Returns the updated start rlp_address, // as well as the length of the RLP data (including the newly-added prefix). // -// Pre stack: end_pos, start_pos, retdest -// Post stack: prefix_start_pos, rlp_len +// Pre stack: end_rlp_addr, start_rlp_addr, retdest +// Post stack: prefix_start_rlp_addr, rlp_len global prepend_rlp_list_prefix: - // stack: end_pos, start_pos, retdest - DUP2 DUP2 SUB // end_pos - start_pos - // stack: payload_len, end_pos, start_pos, retdest + // stack: end_rlp_addr, start_rlp_addr, retdest + DUP2 DUP2 SUB // end_rlp_addr - start_rlp_addr + // stack: payload_len, end_rlp_addr, start_rlp_addr, 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, start_pos, retdest + // If we got here, we have a small list, so we prepend 0xc0 + len at rlp_address 8. + // stack: payload_len, end_rlp_addr, start_rlp_addr, retdest DUP1 %add_const(0xc0) - // stack: prefix_byte, payload_len, end_pos, start_pos, retdest + // stack: prefix_byte, payload_len, end_rlp_addr, start_rlp_addr, retdest DUP4 %decrement // offset of prefix %mstore_rlp - // stack: payload_len, end_pos, start_pos, retdest + // stack: payload_len, end_rlp_addr, start_rlp_addr, retdest %increment - // stack: rlp_len, end_pos, start_pos, retdest + // stack: rlp_len, end_rlp_addr, start_rlp_addr, retdest SWAP2 %decrement - // stack: prefix_start_pos, end_pos, rlp_len, retdest - %stack (prefix_start_pos, end_pos, rlp_len, retdest) -> (retdest, prefix_start_pos, rlp_len) + // stack: prefix_start_rlp_addr, end_rlp_addr, rlp_len, retdest + %stack (prefix_start_rlp_addr, end_rlp_addr, rlp_len, retdest) -> (retdest, prefix_start_rlp_addr, rlp_len) JUMP prepend_rlp_list_prefix_big: - // We have a large list, so we prepend 0xf7 + len_of_len at position - // prefix_start_pos = start_pos - 1 - len_of_len + // We have a large list, so we prepend 0xf7 + len_of_len at rlp_address + // prefix_start_rlp_addr = start_rlp_addr - 1 - len_of_len // followed by the length itself. - // stack: payload_len, end_pos, start_pos, retdest + // stack: payload_len, end_rlp_addr, start_rlp_addr, retdest DUP1 %num_bytes - // stack: len_of_len, payload_len, end_pos, start_pos, retdest + // stack: len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest DUP1 - DUP5 %decrement // start_pos - 1 + DUP5 %decrement // start_rlp_addr - 1 SUB - // stack: prefix_start_pos, len_of_len, payload_len, end_pos, start_pos, retdest - DUP2 %add_const(0xf7) DUP2 %mstore_rlp // rlp[prefix_start_pos] = 0xf7 + len_of_len - // stack: prefix_start_pos, len_of_len, payload_len, end_pos, start_pos, retdest - DUP1 %increment // start_len_pos = prefix_start_pos + 1 - %stack (start_len_pos, prefix_start_pos, len_of_len, payload_len, end_pos, start_pos, retdest) - -> (start_len_pos, payload_len, len_of_len, + // stack: prefix_start_rlp_addr, len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest + DUP2 %add_const(0xf7) DUP2 %mstore_rlp // rlp[prefix_start_rlp_addr] = 0xf7 + len_of_len + // stack: prefix_start_rlp_addr, len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest + DUP1 %increment // start_len_rlp_addr = prefix_start_rlp_addr + 1 + %stack (start_len_rlp_addr, prefix_start_rlp_addr, len_of_len, payload_len, end_rlp_addr, start_rlp_addr, retdest) + -> (start_len_rlp_addr, payload_len, len_of_len, prepend_rlp_list_prefix_big_done_writing_len, - prefix_start_pos, end_pos, retdest) - %jump(mstore_unpacking_rlp) + prefix_start_rlp_addr, end_rlp_addr, retdest) + %jump(mstore_unpacking) prepend_rlp_list_prefix_big_done_writing_len: - // stack: start_pos, prefix_start_pos, end_pos, retdest - %stack (start_pos, prefix_start_pos, end_pos) - -> (end_pos, prefix_start_pos, prefix_start_pos) - // stack: end_pos, prefix_start_pos, prefix_start_pos, retdest + // stack: start_rlp_addr, prefix_start_rlp_addr, end_rlp_addr, retdest + %stack (start_rlp_addr, prefix_start_rlp_addr, end_rlp_addr) + -> (end_rlp_addr, prefix_start_rlp_addr, prefix_start_rlp_addr) + // stack: end_rlp_addr, prefix_start_rlp_addr, prefix_start_rlp_addr, retdest SUB - // stack: rlp_len, prefix_start_pos, retdest - %stack (rlp_len, prefix_start_pos, retdest) -> (retdest, prefix_start_pos, rlp_len) + // stack: rlp_len, prefix_start_rlp_addr, retdest + %stack (rlp_len, prefix_start_rlp_addr, retdest) -> (retdest, prefix_start_rlp_addr, rlp_len) JUMP // Convenience macro to call prepend_rlp_list_prefix and return where we left off. %macro prepend_rlp_list_prefix - %stack (end_pos, start_pos) -> (end_pos, start_pos, %%after) + %stack (end_rlp_addr, start_rlp_addr) -> (end_rlp_addr, start_rlp_addr, %%after) %jump(prepend_rlp_list_prefix) %%after: %endmacro @@ -274,18 +274,3 @@ prepend_rlp_list_prefix_big_done_writing_len: ADD %%finish: %endmacro - -// Like mstore_unpacking, but specifically for the RLP segment. -// Pre stack: offset, value, len, retdest -// Post stack: offset' -global mstore_unpacking_rlp: - // stack: offset, value, len, retdest - PUSH @SEGMENT_RLP_RAW - PUSH 0 // context - %jump(mstore_unpacking) - -%macro mstore_unpacking_rlp - %stack (offset, value, len) -> (offset, value, len, %%after) - %jump(mstore_unpacking_rlp) -%%after: -%endmacro diff --git a/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm b/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm index cd4a837e..8196a452 100644 --- a/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm +++ b/evm/src/cpu/kernel/asm/rlp/encode_rlp_scalar.asm @@ -1,8 +1,8 @@ // RLP-encode a scalar, i.e. a variable-length integer. -// Pre stack: pos, scalar, retdest -// Post stack: pos +// Pre stack: rlp_addr, scalar, retdest +// Post stack: rlp_addr global encode_rlp_scalar: - // stack: pos, scalar, retdest + // stack: rlp_addr, scalar, retdest // If scalar > 0x7f, this is the "medium" case. DUP2 %gt_const(0x7f) @@ -12,12 +12,12 @@ global encode_rlp_scalar: 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 + // stack: rlp_addr, scalar, retdest + %stack (rlp_addr, scalar) -> (0x80, rlp_addr, rlp_addr) + MSTORE_GENERAL + // stack: rlp_addr, retdest %increment - // stack: pos', retdest + // stack: rlp_addr', retdest SWAP1 JUMP @@ -26,17 +26,17 @@ encode_rlp_scalar_medium: // (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 + // stack: rlp_addr, scalar, retdest DUP2 %num_bytes - // stack: scalar_bytes, pos, scalar, retdest + // stack: scalar_bytes, rlp_addr, 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 +// Pre stack: rlp_addr, scalar, retdest +// Post stack: rlp_addr global doubly_encode_rlp_scalar: - // stack: pos, scalar, retdest + // stack: rlp_addr, scalar, retdest // If scalar > 0x7f, this is the "medium" case. DUP2 %gt_const(0x7f) @@ -46,15 +46,16 @@ global doubly_encode_rlp_scalar: 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 + // stack: rlp_addr, scalar, retdest + %stack (rlp_addr, scalar) -> (0x81, rlp_addr, rlp_addr) + MSTORE_GENERAL + // stack: rlp_addr, retdest %increment - %mstore_rlp - // stack: pos, retdest - %add_const(2) - // stack: pos, retdest + DUP1 PUSH 0x80 + MSTORE_GENERAL + // stack: rlp_addr, retdest + %increment + // stack: rlp_addr, retdest SWAP1 JUMP @@ -65,35 +66,35 @@ doubly_encode_rlp_scalar_medium: // 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 + // stack: rlp_addr, scalar, retdest DUP2 %num_bytes - // stack: scalar_bytes, pos, scalar, retdest + // stack: scalar_bytes, rlp_addr, 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 + // stack: rlp_addr, scalar, retdest + %stack (rlp_addr, scalar) -> (scalar, rlp_addr, rlp_addr) + // stack: scalar, rlp_addr, rlp_addr, retdest + MSTORE_GENERAL + // stack: rlp_addr, retdest %increment - // stack: pos', retdest + // stack: rlp_addr', 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) + %stack (rlp_addr, scalar) -> (rlp_addr, 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) + %stack (rlp_addr, scalar) -> (rlp_addr, scalar, %%after) %jump(doubly_encode_rlp_scalar) %%after: %endmacro diff --git a/evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm b/evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm index 1c8bec96..60174a94 100644 --- a/evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm +++ b/evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm @@ -1,80 +1,79 @@ // Encodes an arbitrary string, given a pointer and length. -// Pre stack: pos, ADDR: 3, len, retdest -// Post stack: pos' +// Pre stack: rlp_addr, ADDR, len, retdest +// Post stack: rlp_addr' global encode_rlp_string: - // stack: pos, ADDR: 3, len, retdest - DUP5 %eq_const(1) - // stack: len == 1, pos, ADDR: 3, len, retdest - DUP5 DUP5 DUP5 // ADDR: 3 + // stack: rlp_addr, ADDR, len, retdest + DUP3 %eq_const(1) + // stack: len == 1, rlp_addr, ADDR, len, retdest + DUP3 MLOAD_GENERAL - // stack: first_byte, len == 1, pos, ADDR: 3, len, retdest + // stack: first_byte, len == 1, rlp_addr, ADDR, len, retdest %lt_const(128) MUL // cheaper than AND - // stack: single_small_byte, pos, ADDR: 3, len, retdest + // stack: single_small_byte, rlp_addr, ADDR, len, retdest %jumpi(encode_rlp_string_small_single_byte) - // stack: pos, ADDR: 3, len, retdest - DUP5 %gt_const(55) - // stack: len > 55, pos, ADDR: 3, len, retdest + // stack: rlp_addr, ADDR, len, retdest + DUP3 %gt_const(55) + // stack: len > 55, rlp_addr, ADDR, len, retdest %jumpi(encode_rlp_string_large) global encode_rlp_string_small: - // stack: pos, ADDR: 3, len, retdest - DUP5 // len + // stack: rlp_addr, ADDR, len, retdest + DUP1 + DUP4 // len %add_const(0x80) - // stack: first_byte, pos, ADDR: 3, len, retdest - DUP2 - // stack: pos, first_byte, pos, ADDR: 3, len, retdest - %mstore_rlp - // stack: pos, ADDR: 3, len, retdest + // stack: first_byte, rlp_addr, rlp_addr, ADDR, len, retdest + MSTORE_GENERAL + // stack: rlp_addr, ADDR, len, retdest %increment - // stack: pos', ADDR: 3, len, retdest - DUP5 DUP2 ADD // pos'' = pos' + len - // stack: pos'', pos', ADDR: 3, len, retdest - %stack (pos2, pos1, ADDR: 3, len, retdest) - -> (0, @SEGMENT_RLP_RAW, pos1, ADDR, len, retdest, pos2) + // stack: rlp_addr', ADDR, len, retdest + DUP3 DUP2 ADD // rlp_addr'' = rlp_addr' + len + // stack: rlp_addr'', rlp_addr', ADDR, len, retdest + %stack (rlp_addr2, rlp_addr1, ADDR, len, retdest) + -> (rlp_addr1, ADDR, len, retdest, rlp_addr2) %jump(memcpy_bytes) global encode_rlp_string_small_single_byte: - // stack: pos, ADDR: 3, len, retdest - %stack (pos, ADDR: 3, len) -> (ADDR, pos) + // stack: rlp_addr, ADDR, len, retdest + %stack (rlp_addr, ADDR, len) -> (ADDR, rlp_addr) MLOAD_GENERAL - // stack: byte, pos, retdest - DUP2 - %mstore_rlp - // stack: pos, retdest + // stack: byte, rlp_addr, retdest + DUP2 SWAP1 + MSTORE_GENERAL + // stack: rlp_addr, retdest %increment SWAP1 - // stack: retdest, pos' + // stack: retdest, rlp_addr' JUMP global encode_rlp_string_large: - // stack: pos, ADDR: 3, len, retdest - DUP5 %num_bytes - // stack: len_of_len, pos, ADDR: 3, len, retdest + // stack: rlp_addr, ADDR, len, retdest + DUP3 %num_bytes + // stack: len_of_len, rlp_addr, ADDR, len, retdest SWAP1 - DUP2 // len_of_len + DUP1 + // stack: rlp_addr, rlp_addr, len_of_len, ADDR, len, retdest + DUP3 // len_of_len %add_const(0xb7) - // stack: first_byte, pos, len_of_len, ADDR: 3, len, retdest - DUP2 - // stack: pos, first_byte, pos, len_of_len, ADDR: 3, len, retdest - %mstore_rlp - // stack: pos, len_of_len, ADDR: 3, len, retdest + // stack: first_byte, rlp_addr, rlp_addr, len_of_len, ADDR, len, retdest + MSTORE_GENERAL + // stack: rlp_addr, len_of_len, ADDR, len, retdest %increment - // stack: pos', len_of_len, ADDR: 3, len, retdest - %stack (pos, len_of_len, ADDR: 3, len) - -> (pos, len, len_of_len, encode_rlp_string_large_after_writing_len, ADDR, len) - %jump(mstore_unpacking_rlp) + // stack: rlp_addr', len_of_len, ADDR, len, retdest + %stack (rlp_addr, len_of_len, ADDR, len) + -> (rlp_addr, len, len_of_len, encode_rlp_string_large_after_writing_len, ADDR, len) + %jump(mstore_unpacking) global encode_rlp_string_large_after_writing_len: - // stack: pos'', ADDR: 3, len, retdest - DUP5 DUP2 ADD // pos''' = pos'' + len - // stack: pos''', pos'', ADDR: 3, len, retdest - %stack (pos3, pos2, ADDR: 3, len, retdest) - -> (0, @SEGMENT_RLP_RAW, pos2, ADDR, len, retdest, pos3) + // stack: rlp_addr'', ADDR, len, retdest + DUP3 DUP2 ADD // rlp_addr''' = rlp_addr'' + len + // stack: rlp_addr''', rlp_addr'', ADDR, len, retdest + %stack (rlp_addr3, rlp_addr2, ADDR, len, retdest) + -> (rlp_addr2, ADDR, len, retdest, rlp_addr3) %jump(memcpy_bytes) %macro encode_rlp_string - %stack (pos, ADDR: 3, len) -> (pos, ADDR, len, %%after) + %stack (rlp_addr, ADDR, len) -> (rlp_addr, ADDR, len, %%after) %jump(encode_rlp_string) %%after: %endmacro diff --git a/evm/src/cpu/kernel/asm/rlp/read_to_memory.asm b/evm/src/cpu/kernel/asm/rlp/read_to_memory.asm index 85a78175..75935371 100644 --- a/evm/src/cpu/kernel/asm/rlp/read_to_memory.asm +++ b/evm/src/cpu/kernel/asm/rlp/read_to_memory.asm @@ -8,29 +8,34 @@ global read_rlp_to_memory: // stack: retdest PROVER_INPUT(rlp) // Read the RLP blob length from the prover tape. // stack: len, retdest - PUSH 0 // initial position - // stack: pos, len, retdest + PUSH @SEGMENT_RLP_RAW + %build_kernel_address + + PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 + // stack: addr, final_addr, retdest read_rlp_to_memory_loop: - // stack: pos, len, retdest + // stack: addr, final_addr, retdest DUP2 DUP2 EQ - // stack: pos == len, pos, len, retdest + // stack: addr == final_addr, addr, final_addr, retdest %jumpi(read_rlp_to_memory_finish) - // stack: pos, len, retdest + // stack: addr, len, retdest + DUP1 PROVER_INPUT(rlp) - // stack: byte, pos, len, retdest - DUP2 - // stack: pos, byte, pos, len, retdest - %mstore_kernel(@SEGMENT_RLP_RAW) - // stack: pos, len, retdest + // stack: byte, addr, addr, final_addr, retdest + MSTORE_GENERAL + // stack: addr, final_addr, retdest %increment - // stack: pos', len, retdest + // stack: addr', final_addr, retdest %jump(read_rlp_to_memory_loop) read_rlp_to_memory_finish: - // stack: pos, len, retdest - POP - // stack: len, retdest - SWAP1 JUMP + // stack: addr, final_addr, retdest + // we recover the offset here + PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 + DUP2 SUB + // stack: pos, addr, final_addr, retdest + %stack(pos, addr, final_addr, retdest) -> (retdest, pos) + JUMP diff --git a/evm/src/cpu/kernel/asm/shift.asm b/evm/src/cpu/kernel/asm/shift.asm index 9040f195..ee9ccbfa 100644 --- a/evm/src/cpu/kernel/asm/shift.asm +++ b/evm/src/cpu/kernel/asm/shift.asm @@ -2,21 +2,17 @@ /// /// Specifically, set SHIFT_TABLE_SEGMENT[i] = 2^i for i = 0..255. %macro shift_table_init - push 0 // initial offset is zero - push @SEGMENT_SHIFT_TABLE // segment - dup2 // kernel context is 0 + push @SEGMENT_SHIFT_TABLE // segment, ctx == virt == 0 push 1 // 2^0 %rep 255 - // stack: 2^i, context, segment, ost_i - dup4 + // stack: 2^i, addr_i + dup2 %increment - dup4 - dup4 - // stack: context, segment, ost_(i+1), 2^i, context, segment, ost_i - dup4 + // stack: addr_(i+1), 2^i, addr_i + dup2 dup1 add - // stack: 2^(i+1), context, segment, ost_(i+1), 2^i, context, segment, ost_i + // stack: 2^(i+1), addr_(i+1), 2^i, addr_i %endrep %rep 256 mstore_general diff --git a/evm/src/cpu/kernel/asm/transactions/common_decoding.asm b/evm/src/cpu/kernel/asm/transactions/common_decoding.asm index d4df7a6e..4a8fecca 100644 --- a/evm/src/cpu/kernel/asm/transactions/common_decoding.asm +++ b/evm/src/cpu/kernel/asm/transactions/common_decoding.asm @@ -6,207 +6,206 @@ // Decode the chain ID and store it. %macro decode_and_store_chain_id - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, chain_id) -> (chain_id, pos) + %stack (rlp_addr, chain_id) -> (chain_id, rlp_addr) %mstore_txn_field(@TXN_FIELD_CHAIN_ID) - // stack: pos + // stack: rlp_addr %endmacro // Decode the nonce and store it. %macro decode_and_store_nonce - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, nonce) -> (nonce, pos) + %stack (rlp_addr, nonce) -> (nonce, rlp_addr) %mstore_txn_field(@TXN_FIELD_NONCE) - // stack: pos + // stack: rlp_addr %endmacro // Decode the gas price and, since this is for legacy txns, store it as both // TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS and TXN_FIELD_MAX_FEE_PER_GAS. %macro decode_and_store_gas_price_legacy - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, gas_price) -> (gas_price, gas_price, pos) + %stack (rlp_addr, gas_price) -> (gas_price, gas_price, rlp_addr) %mstore_txn_field(@TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS) %mstore_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) - // stack: pos + // stack: rlp_addr %endmacro // Decode the max priority fee and store it. %macro decode_and_store_max_priority_fee - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, gas_price) -> (gas_price, pos) + %stack (rlp_addr, gas_price) -> (gas_price, rlp_addr) %mstore_txn_field(@TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS) - // stack: pos + // stack: rlp_addr %endmacro // Decode the max fee and store it. %macro decode_and_store_max_fee - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, gas_price) -> (gas_price, pos) + %stack (rlp_addr, gas_price) -> (gas_price, rlp_addr) %mstore_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) - // stack: pos + // stack: rlp_addr %endmacro // Decode the gas limit and store it. %macro decode_and_store_gas_limit - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, gas_limit) -> (gas_limit, pos) + %stack (rlp_addr, gas_limit) -> (gas_limit, rlp_addr) %mstore_txn_field(@TXN_FIELD_GAS_LIMIT) - // stack: pos + // stack: rlp_addr %endmacro // Decode the "to" field and store it. // This field is either 160-bit or empty in the case of a contract creation txn. %macro decode_and_store_to - // stack: pos + // stack: rlp_addr %decode_rlp_string_len - // stack: pos, len + // stack: rlp_addr, len SWAP1 - // stack: len, pos + // stack: len, rlp_addr DUP1 ISZERO %jumpi(%%contract_creation) - // stack: len, pos + // stack: len, rlp_addr DUP1 %eq_const(20) ISZERO %jumpi(invalid_txn) // Address is 160-bit - %stack (len, pos) -> (pos, len, %%with_scalar) + %stack (len, rlp_addr) -> (rlp_addr, len, %%with_scalar) %jump(decode_int_given_len) %%with_scalar: - // stack: pos, int + // stack: rlp_addr, int SWAP1 %mstore_txn_field(@TXN_FIELD_TO) - // stack: pos + // stack: rlp_addr %jump(%%end) %%contract_creation: - // stack: len, pos + // stack: len, rlp_addr POP PUSH 1 %mstore_global_metadata(@GLOBAL_METADATA_CONTRACT_CREATION) - // stack: pos + // stack: rlp_addr %%end: %endmacro // Decode the "value" field and store it. %macro decode_and_store_value - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, value) -> (value, pos) + %stack (rlp_addr, value) -> (value, rlp_addr) %mstore_txn_field(@TXN_FIELD_VALUE) - // stack: pos + // stack: rlp_addr %endmacro // Decode the calldata field, store its length in @TXN_FIELD_DATA_LEN, and copy it to @SEGMENT_TXN_DATA. %macro decode_and_store_data - // stack: pos - // Decode the data length, store it, and compute new_pos after any data. + // stack: rlp_addr + // Decode the data length, store it, and compute new_rlp_addr after any data. %decode_rlp_string_len - %stack (pos, data_len) -> (data_len, pos, data_len, pos, data_len) + %stack (rlp_addr, data_len) -> (data_len, rlp_addr, data_len, rlp_addr, data_len) %mstore_txn_field(@TXN_FIELD_DATA_LEN) - // stack: pos, data_len, pos, data_len + // stack: rlp_addr, data_len, rlp_addr, data_len ADD - // stack: new_pos, old_pos, data_len + // stack: new_rlp_addr, old_rlp_addr, data_len // Memcpy the txn data from @SEGMENT_RLP_RAW to @SEGMENT_TXN_DATA. - %stack (new_pos, old_pos, data_len) -> (old_pos, data_len, %%after, new_pos) - PUSH @SEGMENT_RLP_RAW - GET_CONTEXT - PUSH 0 + %stack (new_rlp_addr, old_rlp_addr, data_len) -> (old_rlp_addr, data_len, %%after, new_rlp_addr) + // old_rlp_addr has context 0. We will call GET_CONTEXT and update it. + GET_CONTEXT ADD PUSH @SEGMENT_TXN_DATA - GET_CONTEXT - // stack: DST, SRC, data_len, %%after, new_pos + GET_CONTEXT ADD + // stack: DST, SRC, data_len, %%after, new_rlp_addr %jump(memcpy_bytes) %%after: - // stack: new_pos + // stack: new_rlp_addr %endmacro %macro decode_and_store_access_list - // stack: pos + // stack: rlp_addr DUP1 %mstore_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) %decode_rlp_list_len - %stack (pos, len) -> (len, len, pos, %%after) + %stack (rlp_addr, len) -> (len, len, rlp_addr, %%after) %jumpi(decode_and_store_access_list) - // stack: len, pos, %%after + // stack: len, rlp_addr, %%after POP SWAP1 POP - // stack: pos + // stack: rlp_addr %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) DUP2 SUB %mstore_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) %%after: %endmacro %macro decode_and_store_y_parity - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, y_parity) -> (y_parity, pos) + %stack (rlp_addr, y_parity) -> (y_parity, rlp_addr) %mstore_txn_field(@TXN_FIELD_Y_PARITY) - // stack: pos + // stack: rlp_addr %endmacro %macro decode_and_store_r - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, r) -> (r, pos) + %stack (rlp_addr, r) -> (r, rlp_addr) %mstore_txn_field(@TXN_FIELD_R) - // stack: pos + // stack: rlp_addr %endmacro %macro decode_and_store_s - // stack: pos + // stack: rlp_addr %decode_rlp_scalar - %stack (pos, s) -> (s, pos) + %stack (rlp_addr, s) -> (s, rlp_addr) %mstore_txn_field(@TXN_FIELD_S) - // stack: pos + // stack: rlp_addr %endmacro // The access list is of the form `[[{20 bytes}, [{32 bytes}...]]...]`. global decode_and_store_access_list: - // stack: len, pos + // stack: len, rlp_addr DUP2 ADD - // stack: end_pos, pos + // stack: end_rlp_addr, rlp_addr // Store the RLP length. %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) DUP2 SUB %mstore_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) SWAP1 decode_and_store_access_list_loop: - // stack: pos, end_pos + // stack: rlp_addr, end_rlp_addr DUP2 DUP2 EQ %jumpi(decode_and_store_access_list_finish) - // stack: pos, end_pos + // stack: rlp_addr, end_rlp_addr %decode_rlp_list_len // Should be a list `[{20 bytes}, [{32 bytes}...]]` - // stack: pos, internal_len, end_pos + // stack: rlp_addr, internal_len, end_rlp_addr SWAP1 POP // We don't need the length of this list. - // stack: pos, end_pos + // stack: rlp_addr, end_rlp_addr %decode_rlp_scalar // Address // TODO: Should panic when address is not 20 bytes? - // stack: pos, addr, end_pos + // stack: rlp_addr, addr, end_rlp_addr SWAP1 - // stack: addr, pos, end_pos + // stack: addr, rlp_addr, end_rlp_addr DUP1 %insert_accessed_addresses_no_return - // stack: addr, pos, end_pos + // stack: addr, rlp_addr, end_rlp_addr %add_address_cost - // stack: addr, pos, end_pos + // stack: addr, rlp_addr, end_rlp_addr SWAP1 - // stack: pos, addr, end_pos + // stack: rlp_addr, addr, end_rlp_addr %decode_rlp_list_len // Should be a list of storage keys `[{32 bytes}...]` - // stack: pos, sk_len, addr, end_pos + // stack: rlp_addr, sk_len, addr, end_rlp_addr SWAP1 DUP2 ADD - // stack: sk_end_pos, pos, addr, end_pos + // stack: sk_end_rlp_addr, rlp_addr, addr, end_rlp_addr SWAP1 - // stack: pos, sk_end_pos, addr, end_pos + // stack: rlp_addr, sk_end_rlp_addr, addr, end_rlp_addr sk_loop: DUP2 DUP2 EQ %jumpi(end_sk) - // stack: pos, sk_end_pos, addr, end_pos + // stack: rlp_addr, sk_end_rlp_addr, addr, end_rlp_addr %decode_rlp_scalar // Storage key // TODO: Should panic when key is not 32 bytes? - %stack (pos, key, sk_end_pos, addr, end_pos) -> - (addr, key, sk_loop_contd, pos, sk_end_pos, addr, end_pos) + %stack (rlp_addr, key, sk_end_rlp_addr, addr, end_rlp_addr) -> + (addr, key, sk_loop_contd, rlp_addr, sk_end_rlp_addr, addr, end_rlp_addr) %jump(insert_accessed_storage_keys_with_original_value) sk_loop_contd: - // stack: pos, sk_end_pos, addr, end_pos + // stack: rlp_addr, sk_end_rlp_addr, addr, end_rlp_addr %add_storage_key_cost %jump(sk_loop) end_sk: - %stack (pos, sk_end_pos, addr, end_pos) -> (pos, end_pos) + %stack (rlp_addr, sk_end_rlp_addr, addr, end_rlp_addr) -> (rlp_addr, end_rlp_addr) %jump(decode_and_store_access_list_loop) decode_and_store_access_list_finish: - %stack (pos, end_pos, retdest) -> (retdest, pos) + %stack (rlp_addr, end_rlp_addr, retdest) -> (retdest, rlp_addr) JUMP %macro add_address_cost diff --git a/evm/src/cpu/kernel/asm/transactions/router.asm b/evm/src/cpu/kernel/asm/transactions/router.asm index 109334dd..2ecccfe0 100644 --- a/evm/src/cpu/kernel/asm/transactions/router.asm +++ b/evm/src/cpu/kernel/asm/transactions/router.asm @@ -20,15 +20,15 @@ read_txn_from_memory: // Type 0 (legacy) transactions have no such prefix, but their RLP will have a // first byte >= 0xc0, so there is no overlap. - PUSH 0 - %mload_kernel(@SEGMENT_RLP_RAW) + PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 + MLOAD_GENERAL %eq_const(1) // stack: first_byte == 1, retdest %jumpi(process_type_1_txn) // stack: retdest - PUSH 0 - %mload_kernel(@SEGMENT_RLP_RAW) + PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 + MLOAD_GENERAL %eq_const(2) // stack: first_byte == 2, retdest %jumpi(process_type_2_txn) @@ -53,10 +53,12 @@ global update_txn_trie: // and now copy txn_rlp to the new block %stack (rlp_start, txn_rlp_len, value_ptr, txn_counter, num_nibbles) -> ( - 0, @SEGMENT_TRIE_DATA, rlp_start, // dest addr - 0, @SEGMENT_RLP_RAW, 0, // src addr. Kernel has context 0 + @SEGMENT_RLP_RAW, // src addr. ctx == virt == 0 + rlp_start, @SEGMENT_TRIE_DATA, // swapped dest addr, ctx == 0 txn_rlp_len, // mcpy len txn_rlp_len, rlp_start, txn_counter, num_nibbles, value_ptr) + SWAP2 %build_kernel_address + // stack: DST, SRC, txn_rlp_len, txn_rlp_len, rlp_start, txn_counter, num_nibbles, value_ptr %memcpy_bytes ADD %set_trie_data_size diff --git a/evm/src/cpu/kernel/asm/transactions/type_0.asm b/evm/src/cpu/kernel/asm/transactions/type_0.asm index edd01e51..0cd22c10 100644 --- a/evm/src/cpu/kernel/asm/transactions/type_0.asm +++ b/evm/src/cpu/kernel/asm/transactions/type_0.asm @@ -13,68 +13,68 @@ global process_type_0_txn: // stack: retdest - PUSH 0 // initial pos - // stack: pos, retdest + PUSH @SEGMENT_RLP_RAW // ctx == virt == 0 + // stack: rlp_addr, retdest %decode_rlp_list_len // We don't actually need the length. - %stack (pos, len) -> (pos) + %stack (rlp_addr, len) -> (rlp_addr) - // stack: pos, retdest + // stack: rlp_addr, retdest %decode_and_store_nonce %decode_and_store_gas_price_legacy %decode_and_store_gas_limit %decode_and_store_to %decode_and_store_value %decode_and_store_data - // stack: pos, retdest + // stack: rlp_addr, retdest // Parse the "v" field. - // stack: pos, retdest + // stack: rlp_addr, retdest %decode_rlp_scalar - // stack: pos, v, retdest + // stack: rlp_addr, v, retdest SWAP1 - // stack: v, pos, retdest + // stack: v, rlp_addr, retdest DUP1 %gt_const(28) - // stack: v > 28, v, pos, retdest + // stack: v > 28, v, rlp_addr, retdest %jumpi(process_v_new_style) // We have an old style v, so y_parity = v - 27. // No chain ID is present, so we can leave TXN_FIELD_CHAIN_ID_PRESENT and // TXN_FIELD_CHAIN_ID with their default values of zero. - // stack: v, pos, retdest + // stack: v, rlp_addr, retdest %sub_const(27) - %stack (y_parity, pos) -> (y_parity, pos) + %stack (y_parity, rlp_addr) -> (y_parity, rlp_addr) %mstore_txn_field(@TXN_FIELD_Y_PARITY) - // stack: pos, retdest + // stack: rlp_addr, retdest %jump(decode_r_and_s) process_v_new_style: - // stack: v, pos, retdest + // stack: v, rlp_addr, retdest // We have a new style v, so chain_id_present = 1, // chain_id = (v - 35) / 2, and y_parity = (v - 35) % 2. - %stack (v, pos) -> (1, v, pos) + %stack (v, rlp_addr) -> (1, v, rlp_addr) %mstore_txn_field(@TXN_FIELD_CHAIN_ID_PRESENT) - // stack: v, pos, retdest + // stack: v, rlp_addr, retdest %sub_const(35) DUP1 - // stack: v - 35, v - 35, pos, retdest + // stack: v - 35, v - 35, rlp_addr, retdest %div_const(2) - // stack: chain_id, v - 35, pos, retdest + // stack: chain_id, v - 35, rlp_addr, retdest %mstore_txn_field(@TXN_FIELD_CHAIN_ID) - // stack: v - 35, pos, retdest + // stack: v - 35, rlp_addr, retdest %mod_const(2) - // stack: y_parity, pos, retdest + // stack: y_parity, rlp_addr, retdest %mstore_txn_field(@TXN_FIELD_Y_PARITY) decode_r_and_s: - // stack: pos, retdest + // stack: rlp_addr, retdest %decode_and_store_r %decode_and_store_s - // stack: pos, retdest + // stack: rlp_addr, retdest POP // stack: retdest @@ -85,73 +85,68 @@ type_0_compute_signed_data: // keccak256(rlp([nonce, gas_price, gas_limit, to, value, data])) %alloc_rlp_block - // stack: rlp_start, retdest + // stack: rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_NONCE) - // stack: nonce, rlp_start, retdest + // stack: nonce, rlp_addr_start, retdest DUP2 - // stack: rlp_pos, nonce, rlp_start, retdest + // stack: rlp_addr, nonce, rlp_addr_start, retdest %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_GAS_LIMIT) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_TO) %mload_global_metadata(@GLOBAL_METADATA_CONTRACT_CREATION) %jumpi(zero_to) - // stack: to, rlp_pos, rlp_start, retdest + // stack: to, rlp_addr, rlp_addr_start, retdest SWAP1 %encode_rlp_160 %jump(after_to) zero_to: - // stack: to, rlp_pos, rlp_start, retdest + // stack: to, rlp_addr, rlp_addr_start, retdest SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest after_to: %mload_txn_field(@TXN_FIELD_VALUE) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest // Encode txn data. %mload_txn_field(@TXN_FIELD_DATA_LEN) - PUSH 0 // ADDR.virt PUSH @SEGMENT_TXN_DATA - PUSH 0 // ADDR.context - // stack: ADDR: 3, len, rlp_pos, rlp_start, retdest + // stack: ADDR, len, rlp_addr, rlp_addr_start, retdest PUSH after_serializing_txn_data - // stack: after_serializing_txn_data, ADDR: 3, len, rlp_pos, rlp_start, retdest - SWAP5 - // stack: rlp_pos, ADDR: 3, len, after_serializing_txn_data, rlp_start, retdest + // stack: after_serializing_txn_data, ADDR, len, rlp_addr, rlp_addr_start, retdest + SWAP3 + // stack: rlp_addr, ADDR, len, after_serializing_txn_data, rlp_addr_start, retdest %jump(encode_rlp_string) after_serializing_txn_data: - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_CHAIN_ID_PRESENT) ISZERO %jumpi(finish_rlp_list) - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_CHAIN_ID) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest PUSH 0 SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest PUSH 0 SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest finish_rlp_list: %prepend_rlp_list_prefix - // stack: prefix_start_pos, rlp_len, retdest - PUSH @SEGMENT_RLP_RAW - PUSH 0 // context - // stack: ADDR: 3, rlp_len, retdest + // stack: ADDR, rlp_len, retdest KECCAK_GENERAL // stack: hash, retdest diff --git a/evm/src/cpu/kernel/asm/transactions/type_1.asm b/evm/src/cpu/kernel/asm/transactions/type_1.asm index c9298b66..f9142bd4 100644 --- a/evm/src/cpu/kernel/asm/transactions/type_1.asm +++ b/evm/src/cpu/kernel/asm/transactions/type_1.asm @@ -8,11 +8,14 @@ global process_type_1_txn: // stack: retdest - PUSH 1 // initial pos, skipping over the 0x01 byte - // stack: pos, retdest + // Initial rlp address offset of 1 (skipping over the 0x01 byte) + PUSH 1 + PUSH @SEGMENT_RLP_RAW + %build_kernel_address + // stack: rlp_addr, retdest %decode_rlp_list_len // We don't actually need the length. - %stack (pos, len) -> (pos) + %stack (rlp_addr, len) -> (rlp_addr) %store_chain_id_present_true %decode_and_store_chain_id @@ -27,7 +30,7 @@ global process_type_1_txn: %decode_and_store_r %decode_and_store_s - // stack: pos, retdest + // stack: rlp_addr, retdest POP // stack: retdest @@ -36,83 +39,79 @@ global process_type_1_txn: // over keccak256(0x01 || rlp([chainId, nonce, gasPrice, gasLimit, to, value, data, accessList])). type_1_compute_signed_data: %alloc_rlp_block - // stack: rlp_start, retdest + // stack: rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_CHAIN_ID) - // stack: chain_id, rlp_start, retdest + // stack: chain_id, rlp_addr_start, retdest DUP2 - // stack: rlp_pos, chain_id, rlp_start, retdest + // stack: rlp_addr, chain_id, rlp_addr_start, retdest %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_NONCE) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_GAS_LIMIT) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_TO) %mload_global_metadata(@GLOBAL_METADATA_CONTRACT_CREATION) %jumpi(zero_to) - // stack: to, rlp_pos, rlp_start, retdest + // stack: to, rlp_addr, rlp_addr_start, retdest SWAP1 %encode_rlp_160 %jump(after_to) zero_to: - // stack: to, rlp_pos, rlp_start, retdest + // stack: to, rlp_addr, rlp_addr_start, retdest SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest after_to: %mload_txn_field(@TXN_FIELD_VALUE) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest // Encode txn data. %mload_txn_field(@TXN_FIELD_DATA_LEN) - PUSH 0 // ADDR.virt - PUSH @SEGMENT_TXN_DATA - PUSH 0 // ADDR.context - // stack: ADDR: 3, len, rlp_pos, rlp_start, retdest + PUSH @SEGMENT_TXN_DATA // ctx == virt == 0 + // stack: ADDR, len, rlp_addr, rlp_addr_start, retdest PUSH after_serializing_txn_data - // stack: after_serializing_txn_data, ADDR: 3, len, rlp_pos, rlp_start, retdest - SWAP5 - // stack: rlp_pos, ADDR: 3, len, after_serializing_txn_data, rlp_start, retdest + // stack: after_serializing_txn_data, ADDR, len, rlp_addr, rlp_addr_start, retdest + SWAP3 + // stack: rlp_addr, ADDR, len, after_serializing_txn_data, rlp_addr_start, retdest %jump(encode_rlp_string) after_serializing_txn_data: // Instead of manually encoding the access list, we just copy the raw RLP from the transaction. %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) - %stack (al_len, al_start, rlp_pos, rlp_start, retdest) -> + %stack (al_len, al_start, rlp_addr, rlp_addr_start, retdest) -> ( - 0, @SEGMENT_RLP_RAW, rlp_pos, - 0, @SEGMENT_RLP_RAW, al_start, + rlp_addr, + al_start, al_len, after_serializing_access_list, - rlp_pos, rlp_start, retdest) + rlp_addr, rlp_addr_start, retdest) %jump(memcpy_bytes) after_serializing_access_list: - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) ADD - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_addr_start, retdest %prepend_rlp_list_prefix - // stack: prefix_start_pos, rlp_len, retdest + // stack: prefix_start_rlp_addr, rlp_len, retdest // Store a `1` in front of the RLP %decrement - %stack (pos) -> (1, 0, @SEGMENT_RLP_RAW, pos, pos) + %stack (rlp_addr) -> (1, rlp_addr, rlp_addr) MSTORE_GENERAL - // stack: pos, rlp_len, retdest + // stack: rlp_addr, rlp_len, retdest // Hash the RLP + the leading `1` SWAP1 %increment SWAP1 - PUSH @SEGMENT_RLP_RAW - PUSH 0 // context - // stack: ADDR: 3, len, retdest + // stack: ADDR, len, retdest KECCAK_GENERAL // stack: hash, retdest diff --git a/evm/src/cpu/kernel/asm/transactions/type_2.asm b/evm/src/cpu/kernel/asm/transactions/type_2.asm index b7f6e6c7..cd4f85e6 100644 --- a/evm/src/cpu/kernel/asm/transactions/type_2.asm +++ b/evm/src/cpu/kernel/asm/transactions/type_2.asm @@ -9,13 +9,16 @@ global process_type_2_txn: // stack: retdest - PUSH 1 // initial pos, skipping over the 0x02 byte - // stack: pos, retdest + // Initial rlp address offset of 1 (skipping over the 0x02 byte) + PUSH 1 + PUSH @SEGMENT_RLP_RAW + %build_kernel_address + // stack: rlp_addr, retdest %decode_rlp_list_len // We don't actually need the length. - %stack (pos, len) -> (pos) + %stack (rlp_addr, len) -> (rlp_addr) - // stack: pos, retdest + // stack: rlp_addr, retdest %store_chain_id_present_true %decode_and_store_chain_id %decode_and_store_nonce @@ -30,7 +33,7 @@ global process_type_2_txn: %decode_and_store_r %decode_and_store_s - // stack: pos, retdest + // stack: rlp_addr, retdest POP // stack: retdest @@ -39,87 +42,83 @@ global process_type_2_txn: // keccak256(0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list])) type_2_compute_signed_data: %alloc_rlp_block - // stack: rlp_start, retdest + // stack: rlp_addr_start, retdest %mload_txn_field(@TXN_FIELD_CHAIN_ID) // stack: chain_id, rlp_start, retdest DUP2 - // stack: rlp_pos, chain_id, rlp_start, retdest + // stack: rlp_addr, chain_id, rlp_start, retdest %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest %mload_txn_field(@TXN_FIELD_NONCE) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest %mload_txn_field(@TXN_FIELD_MAX_PRIORITY_FEE_PER_GAS) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest %mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest %mload_txn_field(@TXN_FIELD_GAS_LIMIT) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest %mload_txn_field(@TXN_FIELD_TO) %mload_global_metadata(@GLOBAL_METADATA_CONTRACT_CREATION) %jumpi(zero_to) - // stack: to, rlp_pos, rlp_start, retdest + // stack: to, rlp_addr, rlp_start, retdest SWAP1 %encode_rlp_160 %jump(after_to) zero_to: - // stack: to, rlp_pos, rlp_start, retdest + // stack: to, rlp_addr, rlp_start, retdest SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest after_to: %mload_txn_field(@TXN_FIELD_VALUE) SWAP1 %encode_rlp_scalar - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest // Encode txn data. %mload_txn_field(@TXN_FIELD_DATA_LEN) - PUSH 0 // ADDR.virt - PUSH @SEGMENT_TXN_DATA - PUSH 0 // ADDR.context - // stack: ADDR: 3, len, rlp_pos, rlp_start, retdest + PUSH @SEGMENT_TXN_DATA // ctx == virt == 0 + // stack: ADDR, len, rlp_addr, rlp_start, retdest PUSH after_serializing_txn_data - // stack: after_serializing_txn_data, ADDR: 3, len, rlp_pos, rlp_start, retdest - SWAP5 - // stack: rlp_pos, ADDR: 3, len, after_serializing_txn_data, rlp_start, retdest + // stack: after_serializing_txn_data, ADDR, len, rlp_addr, rlp_start, retdest + SWAP3 + // stack: rlp_addr, ADDR, len, after_serializing_txn_data, rlp_start, retdest %jump(encode_rlp_string) after_serializing_txn_data: // Instead of manually encoding the access list, we just copy the raw RLP from the transaction. %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_START) %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) - %stack (al_len, al_start, rlp_pos, rlp_start, retdest) -> + %stack (al_len, al_start, rlp_addr, rlp_start, retdest) -> ( - 0, @SEGMENT_RLP_RAW, rlp_pos, - 0, @SEGMENT_RLP_RAW, al_start, + rlp_addr, + al_start, al_len, after_serializing_access_list, - rlp_pos, rlp_start, retdest) + rlp_addr, rlp_start, retdest) %jump(memcpy_bytes) after_serializing_access_list: - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest %mload_global_metadata(@GLOBAL_METADATA_ACCESS_LIST_RLP_LEN) ADD - // stack: rlp_pos, rlp_start, retdest + // stack: rlp_addr, rlp_start, retdest %prepend_rlp_list_prefix // stack: prefix_start_pos, rlp_len, retdest // Store a `2` in front of the RLP %decrement - %stack (pos) -> (2, 0, @SEGMENT_RLP_RAW, pos, pos) + %stack (rlp_addr) -> (2, rlp_addr, rlp_addr) MSTORE_GENERAL - // stack: pos, rlp_len, retdest + // stack: rlp_addr, rlp_len, retdest // Hash the RLP + the leading `2` SWAP1 %increment SWAP1 - PUSH @SEGMENT_RLP_RAW - PUSH 0 // context - // stack: ADDR: 3, len, retdest + // stack: ADDR, len, retdest KECCAK_GENERAL // stack: hash, retdest diff --git a/evm/src/cpu/kernel/asm/util/basic_macros.asm b/evm/src/cpu/kernel/asm/util/basic_macros.asm index fc2472b3..c331acb4 100644 --- a/evm/src/cpu/kernel/asm/util/basic_macros.asm +++ b/evm/src/cpu/kernel/asm/util/basic_macros.asm @@ -410,3 +410,36 @@ ISZERO // stack: not b %endmacro + +%macro build_address + // stack: ctx, seg, off + ADD + ADD + // stack: addr +%endmacro + +%macro build_address_no_offset + // stack: ctx, seg + ADD + // stack: addr +%endmacro + +%macro build_kernel_address + // stack: seg, off + ADD + // stack: addr (ctx == 0) +%endmacro + +%macro build_address_with_ctx_no_offset(seg) + // stack: ctx + PUSH $seg + ADD + // stack: addr +%endmacro + +%macro build_address_with_ctx_no_segment(off) + // stack: ctx + PUSH $off + ADD + // stack: addr +%endmacro diff --git a/evm/src/cpu/kernel/asm/util/keccak.asm b/evm/src/cpu/kernel/asm/util/keccak.asm index 1a1f4372..8385ee59 100644 --- a/evm/src/cpu/kernel/asm/util/keccak.asm +++ b/evm/src/cpu/kernel/asm/util/keccak.asm @@ -18,7 +18,8 @@ global sys_keccak256: %stack (kexit_info, offset, len) -> (offset, len, kexit_info) PUSH @SEGMENT_MAIN_MEMORY GET_CONTEXT - // stack: ADDR: 3, len, kexit_info + %build_address + // stack: ADDR, len, kexit_info KECCAK_GENERAL // stack: hash, kexit_info SWAP1 @@ -37,11 +38,12 @@ sys_keccak256_empty: %macro keccak256_word(num_bytes) // Since KECCAK_GENERAL takes its input from memory, we will first write // input_word's bytes to @SEGMENT_KERNEL_GENERAL[0..$num_bytes]. - %stack (word) -> (0, @SEGMENT_KERNEL_GENERAL, 0, word, $num_bytes, %%after_mstore) + %stack (word) -> (@SEGMENT_KERNEL_GENERAL, word, $num_bytes, %%after_mstore) %jump(mstore_unpacking) %%after_mstore: - // stack: offset - %stack (offset) -> (0, @SEGMENT_KERNEL_GENERAL, 0, $num_bytes) // context, segment, offset, len + // stack: addr + %stack(addr) -> (addr, $num_bytes, $num_bytes) + SUB KECCAK_GENERAL %endmacro @@ -53,12 +55,13 @@ sys_keccak256_empty: // Since KECCAK_GENERAL takes its input from memory, we will first write // a's bytes to @SEGMENT_KERNEL_GENERAL[0..32], then b's bytes to // @SEGMENT_KERNEL_GENERAL[32..64]. - %stack (a) -> (0, @SEGMENT_KERNEL_GENERAL, 0, a, 32, %%after_mstore_a) + %stack (a) -> (@SEGMENT_KERNEL_GENERAL, a, 32, %%after_mstore_a) %jump(mstore_unpacking) %%after_mstore_a: - %stack (offset, b) -> (0, @SEGMENT_KERNEL_GENERAL, 32, b, 32, %%after_mstore_b) + %stack (addr, b) -> (addr, b, 32, %%after_mstore_b) %jump(mstore_unpacking) %%after_mstore_b: - %stack (offset) -> (0, @SEGMENT_KERNEL_GENERAL, 0, 64) // context, segment, offset, len + %stack (addr) -> (addr, 64, 64) // reset the address offset + SUB KECCAK_GENERAL %endmacro diff --git a/evm/src/cpu/kernel/constants/context_metadata.rs b/evm/src/cpu/kernel/constants/context_metadata.rs index 664ce6e9..ffcc6538 100644 --- a/evm/src/cpu/kernel/constants/context_metadata.rs +++ b/evm/src/cpu/kernel/constants/context_metadata.rs @@ -1,39 +1,51 @@ +use crate::memory::segments::Segment; + /// These metadata fields contain VM state specific to a particular context. +/// +/// Each value is directly scaled by the corresponding `Segment::ContextMetadata` value for faster +/// memory access in the kernel. +#[allow(clippy::enum_clike_unportable_variant)] +#[repr(usize)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)] pub(crate) enum ContextMetadata { /// The ID of the context which created this one. - ParentContext = 0, + ParentContext = Segment::ContextMetadata as usize, /// The program counter to return to when we return to the parent context. - ParentProgramCounter = 1, - CalldataSize = 2, - ReturndataSize = 3, + ParentProgramCounter, + CalldataSize, + ReturndataSize, /// The address of the account associated with this context. - Address = 4, + Address, /// The size of the code under the account associated with this context. /// While this information could be obtained from the state trie, it is best to cache it since /// the `CODESIZE` instruction is very cheap. - CodeSize = 5, + CodeSize, /// The address of the caller who spawned this context. - Caller = 6, + Caller, /// The value (in wei) deposited by the caller. - CallValue = 7, + CallValue, /// Whether this context was created by `STATICCALL`, in which case state changes are /// prohibited. - Static = 8, + Static, /// Pointer to the initial version of the state trie, at the creation of this context. Used when /// we need to revert a context. - StateTrieCheckpointPointer = 9, + StateTrieCheckpointPointer, /// Size of the active main memory, in (32 byte) words. - MemWords = 10, - StackSize = 11, + MemWords, + StackSize, /// The gas limit for this call (not the entire transaction). - GasLimit = 12, - ContextCheckpointsLen = 13, + GasLimit, + ContextCheckpointsLen, } impl ContextMetadata { pub(crate) const COUNT: usize = 14; + /// Unscales this virtual offset by their respective `Segment` value. + pub(crate) const fn unscale(&self) -> usize { + *self as usize - Segment::ContextMetadata as usize + } + pub(crate) const fn all() -> [Self; Self::COUNT] { [ Self::ParentContext, diff --git a/evm/src/cpu/kernel/constants/global_metadata.rs b/evm/src/cpu/kernel/constants/global_metadata.rs index 8b85c0b5..4669674e 100644 --- a/evm/src/cpu/kernel/constants/global_metadata.rs +++ b/evm/src/cpu/kernel/constants/global_metadata.rs @@ -1,98 +1,110 @@ +use crate::memory::segments::Segment; + /// These metadata fields contain global VM state, stored in the `Segment::Metadata` segment of the /// kernel's context (which is zero). +/// +/// Each value is directly scaled by the corresponding `Segment::GlobalMetadata` value for faster +/// memory access in the kernel. +#[allow(clippy::enum_clike_unportable_variant)] +#[repr(usize)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)] pub(crate) enum GlobalMetadata { /// The largest context ID that has been used so far in this execution. Tracking this allows us /// give each new context a unique ID, so that its memory will be zero-initialized. - LargestContext = 0, + LargestContext = Segment::GlobalMetadata as usize, /// The size of active memory, in bytes. - MemorySize = 1, + MemorySize, /// The size of the `TrieData` segment, in bytes. In other words, the next address available for /// appending additional trie data. - TrieDataSize = 2, - /// The size of the `TrieData` segment, in bytes. In other words, the next address available for - /// appending additional trie data. - RlpDataSize = 3, + TrieDataSize, + /// The size of the `TrieData` segment, in bytes, represented as a whole address. + /// In other words, the next address available for appending additional trie data. + RlpDataSize, /// A pointer to the root of the state trie within the `TrieData` buffer. - StateTrieRoot = 4, + StateTrieRoot, /// A pointer to the root of the transaction trie within the `TrieData` buffer. - TransactionTrieRoot = 5, + TransactionTrieRoot, /// A pointer to the root of the receipt trie within the `TrieData` buffer. - ReceiptTrieRoot = 6, + ReceiptTrieRoot, // The root digests of each Merkle trie before these transactions. - StateTrieRootDigestBefore = 7, - TransactionTrieRootDigestBefore = 8, - ReceiptTrieRootDigestBefore = 9, + StateTrieRootDigestBefore, + TransactionTrieRootDigestBefore, + ReceiptTrieRootDigestBefore, // The root digests of each Merkle trie after these transactions. - StateTrieRootDigestAfter = 10, - TransactionTrieRootDigestAfter = 11, - ReceiptTrieRootDigestAfter = 12, + StateTrieRootDigestAfter, + TransactionTrieRootDigestAfter, + ReceiptTrieRootDigestAfter, /// The sizes of the `TrieEncodedChild` and `TrieEncodedChildLen` buffers. In other words, the /// next available offset in these buffers. - TrieEncodedChildSize = 13, + TrieEncodedChildSize, // Block metadata. - BlockBeneficiary = 14, - BlockTimestamp = 15, - BlockNumber = 16, - BlockDifficulty = 17, - BlockRandom = 18, - BlockGasLimit = 19, - BlockChainId = 20, - BlockBaseFee = 21, - BlockGasUsed = 22, + BlockBeneficiary, + BlockTimestamp, + BlockNumber, + BlockDifficulty, + BlockRandom, + BlockGasLimit, + BlockChainId, + BlockBaseFee, + BlockGasUsed, /// Before current transactions block values. - BlockGasUsedBefore = 23, + BlockGasUsedBefore, /// After current transactions block values. - BlockGasUsedAfter = 24, + BlockGasUsedAfter, /// Current block header hash - BlockCurrentHash = 25, + BlockCurrentHash, /// Gas to refund at the end of the transaction. - RefundCounter = 26, + RefundCounter, /// Length of the addresses access list. - AccessedAddressesLen = 27, + AccessedAddressesLen, /// Length of the storage keys access list. - AccessedStorageKeysLen = 28, + AccessedStorageKeysLen, /// Length of the self-destruct list. - SelfDestructListLen = 29, + SelfDestructListLen, /// Length of the bloom entry buffer. - BloomEntryLen = 30, + BloomEntryLen, /// Length of the journal. - JournalLen = 31, + JournalLen, /// Length of the `JournalData` segment. - JournalDataLen = 32, + JournalDataLen, /// Current checkpoint. - CurrentCheckpoint = 33, - TouchedAddressesLen = 34, + CurrentCheckpoint, + TouchedAddressesLen, // Gas cost for the access list in type-1 txns. See EIP-2930. - AccessListDataCost = 35, + AccessListDataCost, // Start of the access list in the RLP for type-1 txns. - AccessListRlpStart = 36, + AccessListRlpStart, // Length of the access list in the RLP for type-1 txns. - AccessListRlpLen = 37, + AccessListRlpLen, // Boolean flag indicating if the txn is a contract creation txn. - ContractCreation = 38, - IsPrecompileFromEoa = 39, - CallStackDepth = 40, + ContractCreation, + IsPrecompileFromEoa, + CallStackDepth, /// Transaction logs list length - LogsLen = 41, - LogsDataLen = 42, - LogsPayloadLen = 43, - TxnNumberBefore = 44, - TxnNumberAfter = 45, + LogsLen, + LogsDataLen, + LogsPayloadLen, + TxnNumberBefore, + TxnNumberAfter, - KernelHash = 46, - KernelLen = 47, + KernelHash, + KernelLen, } impl GlobalMetadata { pub(crate) const COUNT: usize = 48; + /// Unscales this virtual offset by their respective `Segment` value. + pub(crate) const fn unscale(&self) -> usize { + *self as usize - Segment::GlobalMetadata as usize + } + pub(crate) const fn all() -> [Self; Self::COUNT] { [ Self::LargestContext, diff --git a/evm/src/cpu/kernel/constants/mod.rs b/evm/src/cpu/kernel/constants/mod.rs index 6e2a0015..451d7beb 100644 --- a/evm/src/cpu/kernel/constants/mod.rs +++ b/evm/src/cpu/kernel/constants/mod.rs @@ -58,16 +58,19 @@ pub(crate) fn evm_constants() -> HashMap { c.insert(CALL_STACK_LIMIT.0.into(), U256::from(CALL_STACK_LIMIT.1)); for segment in Segment::all() { - c.insert(segment.var_name().into(), (segment as u32).into()); + c.insert(segment.var_name().into(), (segment as usize).into()); } for txn_field in NormalizedTxnField::all() { - c.insert(txn_field.var_name().into(), (txn_field as u32).into()); + // These offsets are already scaled by their respective segment. + c.insert(txn_field.var_name().into(), (txn_field as usize).into()); } for txn_field in GlobalMetadata::all() { - c.insert(txn_field.var_name().into(), (txn_field as u32).into()); + // These offsets are already scaled by their respective segment. + c.insert(txn_field.var_name().into(), (txn_field as usize).into()); } for txn_field in ContextMetadata::all() { - c.insert(txn_field.var_name().into(), (txn_field as u32).into()); + // These offsets are already scaled by their respective segment. + c.insert(txn_field.var_name().into(), (txn_field as usize).into()); } for trie_type in PartialTrieType::all() { c.insert(trie_type.var_name().into(), (trie_type as u32).into()); diff --git a/evm/src/cpu/kernel/constants/txn_fields.rs b/evm/src/cpu/kernel/constants/txn_fields.rs index d62159a2..0b74409b 100644 --- a/evm/src/cpu/kernel/constants/txn_fields.rs +++ b/evm/src/cpu/kernel/constants/txn_fields.rs @@ -1,33 +1,46 @@ +use crate::memory::segments::Segment; + /// These are normalized transaction fields, i.e. not specific to any transaction type. +/// +/// Each value is directly scaled by the corresponding `Segment::TxnFields` value for faster +/// memory access in the kernel. +#[allow(dead_code)] +#[allow(clippy::enum_clike_unportable_variant)] +#[repr(usize)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)] pub(crate) enum NormalizedTxnField { /// Whether a chain ID was present in the txn data. Type 0 transaction with v=27 or v=28 have /// no chain ID. This affects what fields get signed. - ChainIdPresent = 0, - ChainId = 1, - Nonce = 2, - MaxPriorityFeePerGas = 3, - MaxFeePerGas = 4, - GasLimit = 6, - IntrinsicGas = 7, - To = 8, - Value = 9, + ChainIdPresent = Segment::TxnFields as usize, + ChainId, + Nonce, + MaxPriorityFeePerGas, + MaxFeePerGas, + GasLimit, + IntrinsicGas, + To, + Value, /// The length of the data field. The data itself is stored in another segment. - DataLen = 10, - YParity = 11, - R = 12, - S = 13, - Origin = 14, + DataLen, + YParity, + R, + S, + Origin, /// The actual computed gas price for this transaction in the block. /// This is not technically a transaction field, as it depends on the block's base fee. - ComputedFeePerGas = 15, - ComputedPriorityFeePerGas = 16, + ComputedFeePerGas, + ComputedPriorityFeePerGas, } impl NormalizedTxnField { pub(crate) const COUNT: usize = 16; + /// Unscales this virtual offset by their respective `Segment` value. + pub(crate) const fn unscale(&self) -> usize { + *self as usize - Segment::TxnFields as usize + } + pub(crate) const fn all() -> [Self; Self::COUNT] { [ Self::ChainIdPresent, diff --git a/evm/src/cpu/kernel/interpreter.rs b/evm/src/cpu/kernel/interpreter.rs index c4376721..4dc887de 100644 --- a/evm/src/cpu/kernel/interpreter.rs +++ b/evm/src/cpu/kernel/interpreter.rs @@ -19,12 +19,12 @@ use crate::extension_tower::BN_BASE; use crate::generation::prover_input::ProverInputFn; use crate::generation::state::GenerationState; use crate::generation::GenerationInputs; -use crate::memory::segments::Segment; +use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; use crate::util::u256_to_usize; use crate::witness::errors::{ProgramError, ProverInputError}; use crate::witness::gas::gas_to_charge; use crate::witness::memory::{MemoryAddress, MemoryContextState, MemorySegmentState, MemoryState}; -use crate::witness::operation::Operation; +use crate::witness::operation::{Operation, CONTEXT_SCALING_FACTOR}; use crate::witness::state::RegistersState; use crate::witness::transition::decode; use crate::witness::util::stack_peek; @@ -199,19 +199,20 @@ impl<'a> Interpreter<'a> { match op { InterpreterMemOpKind::Push(context) => { self.generation_state.memory.contexts[context].segments - [Segment::Stack as usize] - .content - .pop(); + [Segment::Stack.unscale()] + .content + .pop(); } InterpreterMemOpKind::Pop(value, context) => { self.generation_state.memory.contexts[context].segments - [Segment::Stack as usize] - .content - .push(value) + [Segment::Stack.unscale()] + .content + .push(value) } InterpreterMemOpKind::Write(value, context, segment, offset) => { - self.generation_state.memory.contexts[context].segments[segment].content - [offset] = value + self.generation_state.memory.contexts[context].segments + [segment >> SEGMENT_SCALING_FACTOR] // we need to unscale the segment value + .content[offset] = value } } } @@ -267,8 +268,8 @@ impl<'a> Interpreter<'a> { offset_name, self.stack(), self.generation_state.memory.contexts[0].segments - [Segment::KernelGeneral as usize] - .content, + [Segment::KernelGeneral.unscale()] + .content, ); } self.rollback(checkpoint); @@ -289,7 +290,7 @@ impl<'a> Interpreter<'a> { fn code(&self) -> &MemorySegmentState { // The context is 0 if we are in kernel mode. &self.generation_state.memory.contexts[(1 - self.is_kernel() as usize) * self.context()] - .segments[Segment::Code as usize] + .segments[Segment::Code.unscale()] } fn code_slice(&self, n: usize) -> Vec { @@ -301,52 +302,76 @@ impl<'a> Interpreter<'a> { } pub(crate) fn get_txn_field(&self, field: NormalizedTxnField) -> U256 { - self.generation_state.memory.contexts[0].segments[Segment::TxnFields as usize] - .get(field as usize) + // These fields are already scaled by their respective segment. + self.generation_state.memory.contexts[0].segments[Segment::TxnFields.unscale()] + .get(field.unscale()) } pub(crate) fn set_txn_field(&mut self, field: NormalizedTxnField, value: U256) { - self.generation_state.memory.contexts[0].segments[Segment::TxnFields as usize] - .set(field as usize, value); + // These fields are already scaled by their respective segment. + self.generation_state.memory.contexts[0].segments[Segment::TxnFields.unscale()] + .set(field.unscale(), value); } pub(crate) fn get_txn_data(&self) -> &[U256] { - &self.generation_state.memory.contexts[0].segments[Segment::TxnData as usize].content + &self.generation_state.memory.contexts[0].segments[Segment::TxnData.unscale()].content + } + + pub(crate) fn get_context_metadata_field(&self, ctx: usize, field: ContextMetadata) -> U256 { + // These fields are already scaled by their respective segment. + self.generation_state.memory.contexts[ctx].segments[Segment::ContextMetadata.unscale()] + .get(field.unscale()) + } + + pub(crate) fn set_context_metadata_field( + &mut self, + ctx: usize, + field: ContextMetadata, + value: U256, + ) { + // These fields are already scaled by their respective segment. + self.generation_state.memory.contexts[ctx].segments[Segment::ContextMetadata.unscale()] + .set(field.unscale(), value) } pub(crate) fn get_global_metadata_field(&self, field: GlobalMetadata) -> U256 { - self.generation_state.memory.contexts[0].segments[Segment::GlobalMetadata as usize] - .get(field as usize) + // These fields are already scaled by their respective segment. + let field = field.unscale(); + self.generation_state.memory.contexts[0].segments[Segment::GlobalMetadata.unscale()] + .get(field) } pub(crate) fn set_global_metadata_field(&mut self, field: GlobalMetadata, value: U256) { - self.generation_state.memory.contexts[0].segments[Segment::GlobalMetadata as usize] - .set(field as usize, value) + // These fields are already scaled by their respective segment. + let field = field.unscale(); + self.generation_state.memory.contexts[0].segments[Segment::GlobalMetadata.unscale()] + .set(field, value) } pub(crate) fn set_global_metadata_multi_fields(&mut self, metadata: &[(GlobalMetadata, U256)]) { for &(field, value) in metadata { - self.generation_state.memory.contexts[0].segments[Segment::GlobalMetadata as usize] - .set(field as usize, value); + let field = field.unscale(); + self.generation_state.memory.contexts[0].segments[Segment::GlobalMetadata.unscale()] + .set(field, value); } } pub(crate) fn get_trie_data(&self) -> &[U256] { - &self.generation_state.memory.contexts[0].segments[Segment::TrieData as usize].content + &self.generation_state.memory.contexts[0].segments[Segment::TrieData.unscale()].content } pub(crate) fn get_trie_data_mut(&mut self) -> &mut Vec { - &mut self.generation_state.memory.contexts[0].segments[Segment::TrieData as usize].content + &mut self.generation_state.memory.contexts[0].segments[Segment::TrieData.unscale()].content } pub(crate) fn get_memory_segment(&self, segment: Segment) -> Vec { - self.generation_state.memory.contexts[0].segments[segment as usize] + self.generation_state.memory.contexts[0].segments[segment.unscale()] .content .clone() } pub(crate) fn get_memory_segment_bytes(&self, segment: Segment) -> Vec { - self.generation_state.memory.contexts[0].segments[segment as usize] + self.generation_state.memory.contexts[0].segments[segment.unscale()] .content .iter() .map(|x| x.low_u32() as u8) @@ -355,9 +380,9 @@ impl<'a> Interpreter<'a> { pub(crate) fn get_current_general_memory(&self) -> Vec { self.generation_state.memory.contexts[self.context()].segments - [Segment::KernelGeneral as usize] - .content - .clone() + [Segment::KernelGeneral.unscale()] + .content + .clone() } pub(crate) fn get_kernel_general_memory(&self) -> Vec { @@ -370,16 +395,16 @@ impl<'a> Interpreter<'a> { pub(crate) fn set_current_general_memory(&mut self, memory: Vec) { let context = self.context(); - self.generation_state.memory.contexts[context].segments[Segment::KernelGeneral as usize] + self.generation_state.memory.contexts[context].segments[Segment::KernelGeneral.unscale()] .content = memory; } pub(crate) fn set_memory_segment(&mut self, segment: Segment, memory: Vec) { - self.generation_state.memory.contexts[0].segments[segment as usize].content = memory; + self.generation_state.memory.contexts[0].segments[segment.unscale()].content = memory; } pub(crate) fn set_memory_segment_bytes(&mut self, segment: Segment, memory: Vec) { - self.generation_state.memory.contexts[0].segments[segment as usize].content = + self.generation_state.memory.contexts[0].segments[segment.unscale()].content = memory.into_iter().map(U256::from).collect(); } @@ -395,7 +420,7 @@ impl<'a> Interpreter<'a> { .contexts .push(MemoryContextState::default()); } - self.generation_state.memory.contexts[context].segments[Segment::Code as usize].content = + self.generation_state.memory.contexts[context].segments[Segment::Code.unscale()].content = code.into_iter().map(U256::from).collect(); } @@ -406,7 +431,7 @@ impl<'a> Interpreter<'a> { } pub(crate) fn get_jumpdest_bits(&self, context: usize) -> Vec { - self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits as usize] + self.generation_state.memory.contexts[context].segments[Segment::JumpdestBits.unscale()] .content .iter() .map(|x| x.bit(0)) @@ -421,9 +446,9 @@ impl<'a> Interpreter<'a> { match self.stack_len().cmp(&1) { Ordering::Greater => { let mut stack = self.generation_state.memory.contexts[self.context()].segments - [Segment::Stack as usize] - .content - .clone(); + [Segment::Stack.unscale()] + .content + .clone(); stack.truncate(self.stack_len() - 1); stack.push( self.stack_top() @@ -443,7 +468,7 @@ impl<'a> Interpreter<'a> { } fn stack_segment_mut(&mut self) -> &mut Vec { let context = self.context(); - &mut self.generation_state.memory.contexts[context].segments[Segment::Stack as usize] + &mut self.generation_state.memory.contexts[context].segments[Segment::Stack.unscale()] .content } @@ -642,8 +667,8 @@ impl<'a> Interpreter<'a> { if !self.is_kernel() { let gas_limit_address = MemoryAddress { context: self.context(), - segment: Segment::ContextMetadata as usize, - virt: ContextMetadata::GasLimit as usize, + segment: Segment::ContextMetadata.unscale(), + virt: ContextMetadata::GasLimit.unscale(), }; let gas_limit = u256_to_usize(self.generation_state.memory.get(gas_limit_address))? as u64; @@ -828,11 +853,11 @@ impl<'a> Interpreter<'a> { } fn run_keccak_general(&mut self) -> anyhow::Result<(), ProgramError> { - let context = self.pop()?.as_usize(); - let segment = Segment::all()[self.pop()?.as_usize()]; + let addr = self.pop()?; + let (context, segment, offset) = unpack_address!(addr); + // Not strictly needed but here to avoid surprises with MSIZE. assert_ne!(segment, Segment::MainMemory, "Call KECCAK256 instead."); - let offset = self.pop()?.as_usize(); let size = self.pop()?.as_usize(); let bytes = (offset..offset + size) .map(|i| { @@ -983,7 +1008,7 @@ impl<'a> Interpreter<'a> { let mem_write_op = InterpreterMemOpKind::Write( old_value, self.context(), - Segment::Stack as usize, + Segment::Stack.unscale(), len - n as usize - 1, ); self.memops.push(mem_write_op); @@ -992,16 +1017,17 @@ impl<'a> Interpreter<'a> { } fn run_get_context(&mut self) -> anyhow::Result<(), ProgramError> { - self.push(self.context().into()) + self.push(U256::from(self.context()) << CONTEXT_SCALING_FACTOR) } fn run_set_context(&mut self) -> anyhow::Result<(), ProgramError> { - let new_ctx = self.pop()?.as_usize(); + let x = self.pop()?; + let new_ctx = (x >> CONTEXT_SCALING_FACTOR).as_usize(); let sp_to_save = self.stack_len().into(); let old_ctx = self.context(); - let sp_field = ContextMetadata::StackSize as usize; + let sp_field = ContextMetadata::StackSize.unscale(); let old_sp_addr = MemoryAddress::new(old_ctx, Segment::ContextMetadata, sp_field); let new_sp_addr = MemoryAddress::new(new_ctx, Segment::ContextMetadata, sp_field); @@ -1011,8 +1037,8 @@ impl<'a> Interpreter<'a> { if new_sp > 0 { let new_stack_top = self.generation_state.memory.contexts[new_ctx].segments - [Segment::Stack as usize] - .content[new_sp - 1]; + [Segment::Stack.unscale()] + .content[new_sp - 1]; self.generation_state.registers.stack_top = new_stack_top; } self.set_context(new_ctx); @@ -1021,9 +1047,8 @@ impl<'a> Interpreter<'a> { } fn run_mload_general(&mut self) -> anyhow::Result<(), ProgramError> { - let context = self.pop()?.as_usize(); - let segment = Segment::all()[self.pop()?.as_usize()]; - let offset = self.pop()?.as_usize(); + let addr = self.pop()?; + let (context, segment, offset) = unpack_address!(addr); let value = self .generation_state .memory @@ -1033,9 +1058,8 @@ impl<'a> Interpreter<'a> { } fn run_mload_32bytes(&mut self) -> anyhow::Result<(), ProgramError> { - let context = self.pop()?.as_usize(); - let segment = Segment::all()[self.pop()?.as_usize()]; - let offset = self.pop()?.as_usize(); + let addr = self.pop()?; + let (context, segment, offset) = unpack_address!(addr); let len = self.pop()?.as_usize(); if len > 32 { return Err(ProgramError::IntegerTooLarge); @@ -1054,9 +1078,8 @@ impl<'a> Interpreter<'a> { fn run_mstore_general(&mut self) -> anyhow::Result<(), ProgramError> { let value = self.pop()?; - let context = self.pop()?.as_usize(); - let segment = Segment::all()[self.pop()?.as_usize()]; - let offset = self.pop()?.as_usize(); + let addr = self.pop()?; + let (context, segment, offset) = unpack_address!(addr); let memop = self .generation_state .memory @@ -1066,9 +1089,8 @@ impl<'a> Interpreter<'a> { } fn run_mstore_32bytes(&mut self, n: u8) -> anyhow::Result<(), ProgramError> { - let context = self.pop()?.as_usize(); - let segment = Segment::all()[self.pop()?.as_usize()]; - let offset = self.pop()?.as_usize(); + let addr = self.pop()?; + let (context, segment, offset) = unpack_address!(addr); let value = self.pop()?; let mut bytes = vec![0; 32]; @@ -1086,7 +1108,7 @@ impl<'a> Interpreter<'a> { self.memops.push(memop); } - self.push(U256::from(offset + n as usize)) + self.push(addr + U256::from(n)) } fn run_exit_kernel(&mut self) -> anyhow::Result<(), ProgramError> { @@ -1447,14 +1469,28 @@ fn get_mnemonic(opcode: u8) -> &'static str { } } +#[macro_use] +macro_rules! unpack_address { + ($addr:ident) => {{ + let offset = $addr.low_u32() as usize; + let segment = Segment::all()[($addr >> SEGMENT_SCALING_FACTOR).low_u32() as usize]; + let context = ($addr >> CONTEXT_SCALING_FACTOR).low_u32() as usize; + (context, segment, offset) + }}; +} +pub(crate) use unpack_address; + #[cfg(test)] mod tests { use std::collections::HashMap; + use ethereum_types::U256; + use crate::cpu::kernel::constants::context_metadata::ContextMetadata; use crate::cpu::kernel::interpreter::{run, Interpreter}; use crate::memory::segments::Segment; use crate::witness::memory::MemoryAddress; + use crate::witness::operation::CONTEXT_SCALING_FACTOR; #[test] fn test_run() -> anyhow::Result<()> { @@ -1491,8 +1527,9 @@ mod tests { interpreter.set_code(1, code.to_vec()); - interpreter.generation_state.memory.contexts[1].segments[Segment::ContextMetadata as usize] - .set(ContextMetadata::GasLimit as usize, 100_000.into()); + interpreter.generation_state.memory.contexts[1].segments + [Segment::ContextMetadata.unscale()] + .set(ContextMetadata::GasLimit.unscale(), 100_000.into()); // Set context and kernel mode. interpreter.set_context(1); interpreter.set_is_kernel(false); @@ -1501,7 +1538,7 @@ mod tests { MemoryAddress::new( 1, Segment::ContextMetadata, - ContextMetadata::ParentProgramCounter as usize, + ContextMetadata::ParentProgramCounter.unscale(), ), 0xdeadbeefu32.into(), ); @@ -1509,9 +1546,9 @@ mod tests { MemoryAddress::new( 1, Segment::ContextMetadata, - ContextMetadata::ParentContext as usize, + ContextMetadata::ParentContext.unscale(), ), - 1.into(), + U256::one() << CONTEXT_SCALING_FACTOR, ); interpreter.run()?; @@ -1522,12 +1559,12 @@ mod tests { assert_eq!(interpreter.stack(), &[0xff.into(), 0xff00.into()]); assert_eq!( - interpreter.generation_state.memory.contexts[1].segments[Segment::MainMemory as usize] + interpreter.generation_state.memory.contexts[1].segments[Segment::MainMemory.unscale()] .get(0x27), 0x42.into() ); assert_eq!( - interpreter.generation_state.memory.contexts[1].segments[Segment::MainMemory as usize] + interpreter.generation_state.memory.contexts[1].segments[Segment::MainMemory.unscale()] .get(0x1f), 0xff.into() ); diff --git a/evm/src/cpu/kernel/tests/account_code.rs b/evm/src/cpu/kernel/tests/account_code.rs index 20c98bf9..28b9ae7d 100644 --- a/evm/src/cpu/kernel/tests/account_code.rs +++ b/evm/src/cpu/kernel/tests/account_code.rs @@ -17,6 +17,7 @@ use crate::generation::mpt::{load_all_mpts, AccountRlp}; use crate::generation::TrieInputs; use crate::memory::segments::Segment; use crate::witness::memory::MemoryAddress; +use crate::witness::operation::CONTEXT_SCALING_FACTOR; use crate::Node; pub(crate) fn initialize_mpts(interpreter: &mut Interpreter, trie_inputs: &TrieInputs) { @@ -24,27 +25,14 @@ pub(crate) fn initialize_mpts(interpreter: &mut Interpreter, trie_inputs: &TrieI let (trie_root_ptrs, trie_data) = load_all_mpts(trie_inputs).expect("Invalid MPT data for preinitialization"); - let state_addr = MemoryAddress::new( - 0, - Segment::GlobalMetadata, - GlobalMetadata::StateTrieRoot as usize, - ); - - let txn_addr = MemoryAddress::new( - 0, - Segment::GlobalMetadata, - GlobalMetadata::TransactionTrieRoot as usize, - ); - let receipts_addr = MemoryAddress::new( - 0, - Segment::GlobalMetadata, - GlobalMetadata::ReceiptTrieRoot as usize, - ); - let len_addr = MemoryAddress::new( - 0, - Segment::GlobalMetadata, - GlobalMetadata::TrieDataSize as usize, - ); + let state_addr = + MemoryAddress::new_bundle((GlobalMetadata::StateTrieRoot as usize).into()).unwrap(); + let txn_addr = + MemoryAddress::new_bundle((GlobalMetadata::TransactionTrieRoot as usize).into()).unwrap(); + let receipts_addr = + MemoryAddress::new_bundle((GlobalMetadata::ReceiptTrieRoot as usize).into()).unwrap(); + let len_addr = + MemoryAddress::new_bundle((GlobalMetadata::TrieDataSize as usize).into()).unwrap(); let to_set = [ (state_addr, trie_root_ptrs.state_root_ptr.into()), @@ -202,8 +190,8 @@ fn test_extcodecopy() -> Result<()> { let context = interpreter.context(); interpreter.generation_state.memory.contexts[context].segments - [Segment::ContextMetadata as usize] - .set(GasLimit as usize, U256::from(1000000000000u64)); + [Segment::ContextMetadata.unscale()] + .set(GasLimit.unscale(), U256::from(1000000000000u64)); let extcodecopy = KERNEL.global_labels["sys_extcodecopy"]; @@ -211,11 +199,11 @@ fn test_extcodecopy() -> Result<()> { let mut rng = thread_rng(); for i in 0..2000 { interpreter.generation_state.memory.contexts[context].segments - [Segment::MainMemory as usize] - .set(i, U256::from(rng.gen::())); + [Segment::MainMemory.unscale()] + .set(i, U256::from(rng.gen::())); interpreter.generation_state.memory.contexts[context].segments - [Segment::KernelAccountCode as usize] - .set(i, U256::from(rng.gen::())); + [Segment::KernelAccountCode.unscale()] + .set(i, U256::from(rng.gen::())); } // Random inputs @@ -251,8 +239,8 @@ fn test_extcodecopy() -> Result<()> { // Check that the code was correctly copied to memory. for i in 0..size { let memory = interpreter.generation_state.memory.contexts[context].segments - [Segment::MainMemory as usize] - .get(dest_offset + i); + [Segment::MainMemory.unscale()] + .get(dest_offset + i); assert_eq!( memory, code.get(offset + i).copied().unwrap_or_default().into() @@ -277,30 +265,23 @@ fn prepare_interpreter_all_accounts( // Switch context and initialize memory with the data we need for the tests. interpreter.generation_state.registers.program_counter = 0; interpreter.set_code(1, code.to_vec()); - interpreter.generation_state.memory.contexts[1].segments[Segment::ContextMetadata as usize] - .set( - ContextMetadata::Address as usize, - U256::from_big_endian(&addr), - ); - interpreter.generation_state.memory.contexts[1].segments[Segment::ContextMetadata as usize] - .set(ContextMetadata::GasLimit as usize, 100_000.into()); + interpreter.set_context_metadata_field( + 1, + ContextMetadata::Address, + U256::from_big_endian(&addr), + ); + interpreter.set_context_metadata_field(1, ContextMetadata::GasLimit, 100_000.into()); interpreter.set_context(1); interpreter.set_is_kernel(false); - interpreter.generation_state.memory.set( - MemoryAddress::new( - 1, - Segment::ContextMetadata, - ContextMetadata::ParentProgramCounter as usize, - ), + interpreter.set_context_metadata_field( + 1, + ContextMetadata::ParentProgramCounter, 0xdeadbeefu32.into(), ); - interpreter.generation_state.memory.set( - MemoryAddress::new( - 1, - Segment::ContextMetadata, - ContextMetadata::ParentContext as usize, - ), - 1.into(), + interpreter.set_context_metadata_field( + 1, + ContextMetadata::ParentContext, + U256::one() << CONTEXT_SCALING_FACTOR, // ctx = 1 ); Ok(()) diff --git a/evm/src/cpu/kernel/tests/add11.rs b/evm/src/cpu/kernel/tests/add11.rs index 9ba65db2..1e71d60d 100644 --- a/evm/src/cpu/kernel/tests/add11.rs +++ b/evm/src/cpu/kernel/tests/add11.rs @@ -16,7 +16,7 @@ use crate::cpu::kernel::tests::account_code::initialize_mpts; use crate::generation::mpt::{AccountRlp, LegacyReceiptRlp}; use crate::generation::rlp::all_rlp_prover_inputs_reversed; use crate::generation::TrieInputs; -use crate::memory::segments::Segment; +use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; use crate::proof::TrieRoots; use crate::util::h2u; @@ -199,8 +199,7 @@ fn test_add11_yml() { let route_txn_label = KERNEL.global_labels["hash_initial_tries"]; // Switch context and initialize memory with the data we need for the tests. interpreter.generation_state.registers.program_counter = route_txn_label; - interpreter.generation_state.memory.contexts[0].segments[Segment::ContextMetadata as usize] - .set(ContextMetadata::GasLimit as usize, 1_000_000.into()); + interpreter.set_context_metadata_field(0, ContextMetadata::GasLimit, 1_000_000.into()); interpreter.set_is_kernel(true); interpreter.run().expect("Proving add11 failed."); } @@ -331,8 +330,7 @@ fn test_add11_yml_with_exception() { let route_txn_label = KERNEL.global_labels["hash_initial_tries"]; // Switch context and initialize memory with the data we need for the tests. interpreter.generation_state.registers.program_counter = route_txn_label; - interpreter.generation_state.memory.contexts[0].segments[Segment::ContextMetadata as usize] - .set(ContextMetadata::GasLimit as usize, 1_000_000.into()); + interpreter.set_context_metadata_field(0, ContextMetadata::GasLimit, 1_000_000.into()); interpreter.set_is_kernel(true); interpreter .run() diff --git a/evm/src/cpu/kernel/tests/core/access_lists.rs b/evm/src/cpu/kernel/tests/core/access_lists.rs index c62d4865..69dd2d27 100644 --- a/evm/src/cpu/kernel/tests/core/access_lists.rs +++ b/evm/src/cpu/kernel/tests/core/access_lists.rs @@ -9,7 +9,7 @@ use crate::cpu::kernel::constants::global_metadata::GlobalMetadata::{ AccessedAddressesLen, AccessedStorageKeysLen, }; use crate::cpu::kernel::interpreter::Interpreter; -use crate::memory::segments::Segment::{AccessedAddresses, AccessedStorageKeys, GlobalMetadata}; +use crate::memory::segments::Segment::{AccessedAddresses, AccessedStorageKeys}; use crate::witness::memory::MemoryAddress; #[test] @@ -42,17 +42,16 @@ fn test_insert_accessed_addresses() -> Result<()> { .set(MemoryAddress::new(0, AccessedAddresses, i), addr); } interpreter.generation_state.memory.set( - MemoryAddress::new(0, GlobalMetadata, AccessedAddressesLen as usize), + MemoryAddress::new_bundle(U256::from(AccessedAddressesLen as usize)).unwrap(), U256::from(n), ); interpreter.run()?; assert_eq!(interpreter.stack(), &[U256::zero()]); assert_eq!( - interpreter.generation_state.memory.get(MemoryAddress::new( - 0, - GlobalMetadata, - AccessedAddressesLen as usize - )), + interpreter + .generation_state + .memory + .get(MemoryAddress::new_bundle(U256::from(AccessedAddressesLen as usize)).unwrap()), U256::from(n) ); @@ -67,17 +66,16 @@ fn test_insert_accessed_addresses() -> Result<()> { .set(MemoryAddress::new(0, AccessedAddresses, i), addr); } interpreter.generation_state.memory.set( - MemoryAddress::new(0, GlobalMetadata, AccessedAddressesLen as usize), + MemoryAddress::new_bundle(U256::from(AccessedAddressesLen as usize)).unwrap(), U256::from(n), ); interpreter.run()?; assert_eq!(interpreter.stack(), &[U256::one()]); assert_eq!( - interpreter.generation_state.memory.get(MemoryAddress::new( - 0, - GlobalMetadata, - AccessedAddressesLen as usize - )), + interpreter + .generation_state + .memory + .get(MemoryAddress::new_bundle(U256::from(AccessedAddressesLen as usize)).unwrap()), U256::from(n + 1) ); assert_eq!( @@ -134,17 +132,16 @@ fn test_insert_accessed_storage_keys() -> Result<()> { ); } interpreter.generation_state.memory.set( - MemoryAddress::new(0, GlobalMetadata, AccessedStorageKeysLen as usize), + MemoryAddress::new_bundle(U256::from(AccessedStorageKeysLen as usize)).unwrap(), U256::from(3 * n), ); interpreter.run()?; assert_eq!(interpreter.stack(), &[storage_key_in_list.2, U256::zero()]); assert_eq!( - interpreter.generation_state.memory.get(MemoryAddress::new( - 0, - GlobalMetadata, - AccessedStorageKeysLen as usize - )), + interpreter + .generation_state + .memory + .get(MemoryAddress::new_bundle(U256::from(AccessedStorageKeysLen as usize)).unwrap()), U256::from(3 * n) ); @@ -172,7 +169,7 @@ fn test_insert_accessed_storage_keys() -> Result<()> { ); } interpreter.generation_state.memory.set( - MemoryAddress::new(0, GlobalMetadata, AccessedStorageKeysLen as usize), + MemoryAddress::new_bundle(U256::from(AccessedStorageKeysLen as usize)).unwrap(), U256::from(3 * n), ); interpreter.run()?; @@ -181,11 +178,10 @@ fn test_insert_accessed_storage_keys() -> Result<()> { &[storage_key_not_in_list.2, U256::one()] ); assert_eq!( - interpreter.generation_state.memory.get(MemoryAddress::new( - 0, - GlobalMetadata, - AccessedStorageKeysLen as usize - )), + interpreter + .generation_state + .memory + .get(MemoryAddress::new_bundle(U256::from(AccessedStorageKeysLen as usize)).unwrap()), U256::from(3 * (n + 1)) ); assert_eq!( diff --git a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs index 022a18d7..1d686d62 100644 --- a/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs +++ b/evm/src/cpu/kernel/tests/core/jumpdest_analysis.rs @@ -1,8 +1,10 @@ use anyhow::Result; +use ethereum_types::U256; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; use crate::cpu::kernel::opcodes::{get_opcode, get_push_opcode}; +use crate::witness::operation::CONTEXT_SCALING_FACTOR; #[test] fn test_jumpdest_analysis() -> Result<()> { @@ -28,7 +30,11 @@ fn test_jumpdest_analysis() -> Result<()> { let expected_jumpdest_bits = vec![false, true, false, false, false, true, false, true]; // Contract creation transaction. - let initial_stack = vec![0xDEADBEEFu32.into(), code.len().into(), CONTEXT.into()]; + let initial_stack = vec![ + 0xDEADBEEFu32.into(), + code.len().into(), + U256::from(CONTEXT) << CONTEXT_SCALING_FACTOR, + ]; let mut interpreter = Interpreter::new_with_kernel(jumpdest_analysis, initial_stack); interpreter.set_code(CONTEXT, code); interpreter.run()?; diff --git a/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs b/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs index c13b8122..e51e60ab 100644 --- a/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs +++ b/evm/src/cpu/kernel/tests/mpt/hex_prefix.rs @@ -1,7 +1,9 @@ use anyhow::Result; +use ethereum_types::U256; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; +use crate::memory::segments::Segment; #[test] fn hex_prefix_even_nonterminated() -> Result<()> { @@ -11,11 +13,11 @@ fn hex_prefix_even_nonterminated() -> Result<()> { let terminated = 0.into(); let packed_nibbles = 0xABCDEF.into(); let num_nibbles = 6.into(); - let rlp_pos = 0.into(); + let rlp_pos = U256::from(Segment::RlpRaw as usize); 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![5.into()]); + assert_eq!(interpreter.stack(), vec![rlp_pos + U256::from(5)]); assert_eq!( interpreter.get_rlp_memory(), @@ -39,11 +41,11 @@ fn hex_prefix_odd_terminated() -> Result<()> { let terminated = 1.into(); let packed_nibbles = 0xABCDE.into(); let num_nibbles = 5.into(); - let rlp_pos = 0.into(); + let rlp_pos = U256::from(Segment::RlpRaw as usize); 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![rlp_pos + U256::from(4)]); assert_eq!( interpreter.get_rlp_memory(), @@ -66,11 +68,14 @@ fn hex_prefix_odd_terminated_tiny() -> Result<()> { let terminated = 1.into(); let packed_nibbles = 0xA.into(); let num_nibbles = 1.into(); - let rlp_pos = 2.into(); + let rlp_pos = U256::from(Segment::RlpRaw as usize + 2); 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![U256::from(Segment::RlpRaw as usize + 3)] + ); assert_eq!( interpreter.get_rlp_memory(), diff --git a/evm/src/cpu/kernel/tests/packing.rs b/evm/src/cpu/kernel/tests/packing.rs index 43ca9b5f..5517001f 100644 --- a/evm/src/cpu/kernel/tests/packing.rs +++ b/evm/src/cpu/kernel/tests/packing.rs @@ -11,10 +11,8 @@ fn test_mload_packing_1_byte() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let len = 1.into(); - let offset = 2.into(); - let segment = (Segment::RlpRaw as u32).into(); - let context = 0.into(); - let initial_stack = vec![retdest, len, offset, segment, context]; + let addr = (Segment::RlpRaw as u64 + 2).into(); + let initial_stack = vec![retdest, len, addr]; let mut interpreter = Interpreter::new_with_kernel(mload_packing, initial_stack); interpreter.set_rlp_memory(vec![0, 0, 0xAB]); @@ -31,10 +29,8 @@ fn test_mload_packing_3_bytes() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let len = 3.into(); - let offset = 2.into(); - let segment = (Segment::RlpRaw as u32).into(); - let context = 0.into(); - let initial_stack = vec![retdest, len, offset, segment, context]; + let addr = (Segment::RlpRaw as u64 + 2).into(); + let initial_stack = vec![retdest, len, addr]; let mut interpreter = Interpreter::new_with_kernel(mload_packing, initial_stack); interpreter.set_rlp_memory(vec![0, 0, 0xAB, 0xCD, 0xEF]); @@ -51,10 +47,8 @@ fn test_mload_packing_32_bytes() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let len = 32.into(); - let offset = 0.into(); - let segment = (Segment::RlpRaw as u32).into(); - let context = 0.into(); - let initial_stack = vec![retdest, len, offset, segment, context]; + let addr = (Segment::RlpRaw as u64).into(); + let initial_stack = vec![retdest, len, addr]; let mut interpreter = Interpreter::new_with_kernel(mload_packing, initial_stack); interpreter.set_rlp_memory(vec![0xFF; 32]); @@ -72,15 +66,13 @@ fn test_mstore_unpacking() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let len = 4.into(); let value = 0xABCD1234u32.into(); - let offset = 0.into(); - let segment = (Segment::TxnData as u32).into(); - let context = 0.into(); - let initial_stack = vec![retdest, len, value, offset, segment, context]; + let addr = (Segment::TxnData as u64).into(); + let initial_stack = vec![retdest, len, value, addr]; let mut interpreter = Interpreter::new_with_kernel(mstore_unpacking, initial_stack); interpreter.run()?; - assert_eq!(interpreter.stack(), vec![4.into()]); + assert_eq!(interpreter.stack(), vec![addr + U256::from(4)]); assert_eq!( &interpreter.get_txn_data(), &[0xAB.into(), 0xCD.into(), 0x12.into(), 0x34.into()] diff --git a/evm/src/cpu/kernel/tests/rlp/decode.rs b/evm/src/cpu/kernel/tests/rlp/decode.rs index a1ca3609..1f3260e5 100644 --- a/evm/src/cpu/kernel/tests/rlp/decode.rs +++ b/evm/src/cpu/kernel/tests/rlp/decode.rs @@ -1,20 +1,25 @@ use anyhow::Result; +use ethereum_types::U256; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; +use crate::memory::segments::Segment; #[test] fn test_decode_rlp_string_len_short() -> Result<()> { let decode_rlp_string_len = KERNEL.global_labels["decode_rlp_string_len"]; - let initial_stack = vec![0xDEADBEEFu32.into(), 2.into()]; + let initial_stack = vec![ + 0xDEADBEEFu32.into(), + U256::from(Segment::RlpRaw as usize + 2), + ]; let mut interpreter = Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); // A couple dummy bytes, followed by "0x70" which is its own encoding. interpreter.set_rlp_memory(vec![123, 234, 0x70]); interpreter.run()?; - let expected_stack = vec![1.into(), 2.into()]; // len, pos + let expected_stack = vec![1.into(), U256::from(Segment::RlpRaw as usize + 2)]; // len, pos assert_eq!(interpreter.stack(), expected_stack); Ok(()) @@ -24,14 +29,17 @@ fn test_decode_rlp_string_len_short() -> Result<()> { fn test_decode_rlp_string_len_medium() -> Result<()> { let decode_rlp_string_len = KERNEL.global_labels["decode_rlp_string_len"]; - let initial_stack = vec![0xDEADBEEFu32.into(), 2.into()]; + let initial_stack = vec![ + 0xDEADBEEFu32.into(), + U256::from(Segment::RlpRaw as usize + 2), + ]; let mut interpreter = Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); // A couple dummy bytes, followed by the RLP encoding of "1 2 3 4 5". interpreter.set_rlp_memory(vec![123, 234, 0x85, 1, 2, 3, 4, 5]); interpreter.run()?; - let expected_stack = vec![5.into(), 3.into()]; // len, pos + let expected_stack = vec![5.into(), U256::from(Segment::RlpRaw as usize + 3)]; // len, pos assert_eq!(interpreter.stack(), expected_stack); Ok(()) @@ -41,7 +49,10 @@ fn test_decode_rlp_string_len_medium() -> Result<()> { fn test_decode_rlp_string_len_long() -> Result<()> { let decode_rlp_string_len = KERNEL.global_labels["decode_rlp_string_len"]; - let initial_stack = vec![0xDEADBEEFu32.into(), 2.into()]; + let initial_stack = vec![ + 0xDEADBEEFu32.into(), + U256::from(Segment::RlpRaw as usize + 2), + ]; let mut interpreter = Interpreter::new_with_kernel(decode_rlp_string_len, initial_stack); // The RLP encoding of the string "1 2 3 ... 56". @@ -52,7 +63,7 @@ fn test_decode_rlp_string_len_long() -> Result<()> { ]); interpreter.run()?; - let expected_stack = vec![56.into(), 4.into()]; // len, pos + let expected_stack = vec![56.into(), U256::from(Segment::RlpRaw as usize + 4)]; // len, pos assert_eq!(interpreter.stack(), expected_stack); Ok(()) @@ -62,14 +73,14 @@ fn test_decode_rlp_string_len_long() -> Result<()> { fn test_decode_rlp_list_len_short() -> Result<()> { let decode_rlp_list_len = KERNEL.global_labels["decode_rlp_list_len"]; - let initial_stack = vec![0xDEADBEEFu32.into(), 0.into()]; + let initial_stack = vec![0xDEADBEEFu32.into(), U256::from(Segment::RlpRaw as usize)]; let mut interpreter = Interpreter::new_with_kernel(decode_rlp_list_len, initial_stack); // The RLP encoding of [1, 2, [3, 4]]. interpreter.set_rlp_memory(vec![0xc5, 1, 2, 0xc2, 3, 4]); interpreter.run()?; - let expected_stack = vec![5.into(), 1.into()]; // len, pos + let expected_stack = vec![5.into(), U256::from(Segment::RlpRaw as usize + 1)]; // len, pos assert_eq!(interpreter.stack(), expected_stack); Ok(()) @@ -79,7 +90,7 @@ fn test_decode_rlp_list_len_short() -> Result<()> { fn test_decode_rlp_list_len_long() -> Result<()> { let decode_rlp_list_len = KERNEL.global_labels["decode_rlp_list_len"]; - let initial_stack = vec![0xDEADBEEFu32.into(), 0.into()]; + let initial_stack = vec![0xDEADBEEFu32.into(), U256::from(Segment::RlpRaw as usize)]; let mut interpreter = Interpreter::new_with_kernel(decode_rlp_list_len, initial_stack); // The RLP encoding of [1, ..., 56]. @@ -90,7 +101,7 @@ fn test_decode_rlp_list_len_long() -> Result<()> { ]); interpreter.run()?; - let expected_stack = vec![56.into(), 2.into()]; // len, pos + let expected_stack = vec![56.into(), U256::from(Segment::RlpRaw as usize + 2)]; // len, pos assert_eq!(interpreter.stack(), expected_stack); Ok(()) @@ -100,14 +111,14 @@ fn test_decode_rlp_list_len_long() -> Result<()> { fn test_decode_rlp_scalar() -> Result<()> { let decode_rlp_scalar = KERNEL.global_labels["decode_rlp_scalar"]; - let initial_stack = vec![0xDEADBEEFu32.into(), 0.into()]; + let initial_stack = vec![0xDEADBEEFu32.into(), U256::from(Segment::RlpRaw as usize)]; let mut interpreter = Interpreter::new_with_kernel(decode_rlp_scalar, initial_stack); // The RLP encoding of "12 34 56". interpreter.set_rlp_memory(vec![0x83, 0x12, 0x34, 0x56]); interpreter.run()?; - let expected_stack = vec![0x123456.into(), 4.into()]; // scalar, pos + let expected_stack = vec![0x123456.into(), U256::from(Segment::RlpRaw as usize + 4)]; // scalar, pos assert_eq!(interpreter.stack(), expected_stack); Ok(()) diff --git a/evm/src/cpu/kernel/tests/rlp/encode.rs b/evm/src/cpu/kernel/tests/rlp/encode.rs index 2771dea0..505c99df 100644 --- a/evm/src/cpu/kernel/tests/rlp/encode.rs +++ b/evm/src/cpu/kernel/tests/rlp/encode.rs @@ -1,7 +1,9 @@ use anyhow::Result; +use ethereum_types::U256; use crate::cpu::kernel::aggregator::KERNEL; use crate::cpu::kernel::interpreter::Interpreter; +use crate::memory::segments::Segment; #[test] fn test_encode_rlp_scalar_small() -> Result<()> { @@ -9,12 +11,12 @@ fn test_encode_rlp_scalar_small() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let scalar = 42.into(); - let pos = 2.into(); + let pos = U256::from(Segment::RlpRaw as usize + 2); 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_stack = vec![pos + U256::from(1)]; // 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); @@ -28,12 +30,12 @@ fn test_encode_rlp_scalar_medium() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let scalar = 0x12345.into(); - let pos = 2.into(); + let pos = U256::from(Segment::RlpRaw as usize + 2); 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_stack = vec![pos + U256::from(4)]; // 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); @@ -47,12 +49,12 @@ fn test_encode_rlp_160() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let string = 0x12345.into(); - let pos = 0.into(); + let pos = U256::from(Segment::RlpRaw as usize); 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' + let expected_stack = vec![pos + U256::from(1 + 20)]; // 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); @@ -67,12 +69,12 @@ fn test_encode_rlp_256() -> Result<()> { let retdest = 0xDEADBEEFu32.into(); let string = 0x12345.into(); - let pos = 0.into(); + let pos = U256::from(Segment::RlpRaw as usize); 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' + let expected_stack = vec![pos + U256::from(1 + 32)]; // 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); @@ -86,8 +88,8 @@ 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 start_pos = 9.into(); - let end_pos = (9 + 5).into(); + let start_pos = U256::from(Segment::RlpRaw as usize + 9); + let end_pos = U256::from(Segment::RlpRaw as usize + 9 + 5); let initial_stack = vec![retdest, start_pos, end_pos]; let mut interpreter = Interpreter::new_with_kernel(prepend_rlp_list_prefix, initial_stack); interpreter.set_rlp_memory(vec![ @@ -100,7 +102,7 @@ fn test_prepend_rlp_list_prefix_small() -> Result<()> { interpreter.run()?; let expected_rlp_len = 6.into(); - let expected_start_pos = 8.into(); + let expected_start_pos = U256::from(Segment::RlpRaw as usize + 8); 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]; @@ -115,8 +117,8 @@ fn test_prepend_rlp_list_prefix_large() -> Result<()> { let prepend_rlp_list_prefix = KERNEL.global_labels["prepend_rlp_list_prefix"]; let retdest = 0xDEADBEEFu32.into(); - let start_pos = 9.into(); - let end_pos = (9 + 60).into(); + let start_pos = U256::from(Segment::RlpRaw as usize + 9); + let end_pos = U256::from(Segment::RlpRaw as usize + 9 + 60); let initial_stack = vec![retdest, start_pos, end_pos]; let mut interpreter = Interpreter::new_with_kernel(prepend_rlp_list_prefix, initial_stack); @@ -136,7 +138,7 @@ fn test_prepend_rlp_list_prefix_large() -> Result<()> { interpreter.run()?; let expected_rlp_len = 62.into(); - let expected_start_pos = 7.into(); + let expected_start_pos = U256::from(Segment::RlpRaw as usize + 7); let expected_stack = vec![expected_rlp_len, expected_start_pos]; #[rustfmt::skip] diff --git a/evm/src/cpu/memio.rs b/evm/src/cpu/memio.rs index 304bb3de..2073e182 100644 --- a/evm/src/cpu/memio.rs +++ b/evm/src/cpu/memio.rs @@ -5,23 +5,17 @@ use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use super::cpu_stark::get_addr; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; -use crate::cpu::membus::NUM_GP_CHANNELS; use crate::cpu::stack; -use crate::memory::segments::Segment; +use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; const fn get_addr_load(lv: &CpuColumnsView) -> (T, T, T) { - let addr_context = lv.mem_channels[0].value[0]; - let addr_segment = lv.mem_channels[1].value[0]; - let addr_virtual = lv.mem_channels[2].value[0]; - (addr_context, addr_segment, addr_virtual) + get_addr(lv, 0) } const fn get_addr_store(lv: &CpuColumnsView) -> (T, T, T) { - let addr_context = lv.mem_channels[1].value[0]; - let addr_segment = lv.mem_channels[2].value[0]; - let addr_virtual = lv.mem_channels[3].value[0]; - (addr_context, addr_segment, addr_virtual) + get_addr(lv, 1) } /// Evaluates constraints for MLOAD_GENERAL. @@ -36,7 +30,7 @@ fn eval_packed_load( let (addr_context, addr_segment, addr_virtual) = get_addr_load(lv); // Check that we are loading the correct value from the correct address. - let load_channel = lv.mem_channels[3]; + let load_channel = lv.mem_channels[1]; yield_constr.constraint(filter * (load_channel.used - P::ONES)); yield_constr.constraint(filter * (load_channel.is_read - P::ONES)); yield_constr.constraint(filter * (load_channel.addr_context - addr_context)); @@ -53,7 +47,7 @@ fn eval_packed_load( } // Disable remaining memory channels, if any. - for &channel in &lv.mem_channels[4..NUM_GP_CHANNELS] { + for &channel in &lv.mem_channels[2..] { yield_constr.constraint(filter * channel.used); } yield_constr.constraint(filter * lv.partial_channel.used); @@ -83,7 +77,7 @@ fn eval_ext_circuit_load, const D: usize>( let (addr_context, addr_segment, addr_virtual) = get_addr_load(lv); // Check that we are loading the correct value from the correct channel. - let load_channel = lv.mem_channels[3]; + let load_channel = lv.mem_channels[1]; { let constr = builder.mul_sub_extension(filter, load_channel.used, filter); yield_constr.constraint(builder, constr); @@ -117,7 +111,7 @@ fn eval_ext_circuit_load, const D: usize>( } // Disable remaining memory channels, if any. - for &channel in &lv.mem_channels[4..] { + for &channel in &lv.mem_channels[2..] { let constr = builder.mul_extension(filter, channel.used); yield_constr.constraint(builder, constr); } @@ -157,13 +151,13 @@ fn eval_packed_store( yield_constr.constraint(filter * (store_channel.addr_virtual - addr_virtual)); // Disable remaining memory channels, if any. - for &channel in &lv.mem_channels[4..] { + for &channel in &lv.mem_channels[2..] { yield_constr.constraint(filter * channel.used); } // Stack constraints. // Pops. - for i in 1..4 { + for i in 1..2 { let channel = lv.mem_channels[i]; yield_constr.constraint(filter * (channel.used - P::ONES)); @@ -171,19 +165,21 @@ fn eval_packed_store( yield_constr.constraint(filter * (channel.addr_context - lv.context)); yield_constr.constraint( - filter * (channel.addr_segment - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + filter + * (channel.addr_segment + - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); // Remember that the first read (`i == 1`) is for the second stack element at `stack[stack_len - 1]`. let addr_virtual = lv.stack_len - P::Scalar::from_canonical_usize(i + 1); yield_constr.constraint(filter * (channel.addr_virtual - addr_virtual)); } // Constrain `stack_inv_aux`. - let len_diff = lv.stack_len - P::Scalar::from_canonical_usize(4); + let len_diff = lv.stack_len - P::Scalar::from_canonical_usize(2); yield_constr.constraint( lv.op.m_op_general * (len_diff * lv.general.stack().stack_inv - lv.general.stack().stack_inv_aux), ); - // If stack_len != 4 and MSTORE, read new top of the stack in nv.mem_channels[0]. + // If stack_len != 2 and MSTORE, read new top of the stack in nv.mem_channels[0]. let top_read_channel = nv.mem_channels[0]; let is_top_read = lv.general.stack().stack_inv_aux * (P::ONES - lv.opcode_bits[0]); // Constrain `stack_inv_aux_2`. It contains `stack_inv_aux * opcode_bits[0]`. @@ -196,12 +192,11 @@ fn eval_packed_store( yield_constr.constraint_transition( new_filter * (top_read_channel.addr_segment - - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); let addr_virtual = nv.stack_len - P::ONES; yield_constr.constraint_transition(new_filter * (top_read_channel.addr_virtual - addr_virtual)); - - // If stack_len == 4 or MLOAD, disable the channel. + // If stack_len == 2 or MLOAD, disable the channel. yield_constr.constraint( lv.op.m_op_general * (lv.general.stack().stack_inv_aux - P::ONES) * top_read_channel.used, ); @@ -245,14 +240,14 @@ fn eval_ext_circuit_store, const D: usize>( } // Disable remaining memory channels, if any. - for &channel in &lv.mem_channels[4..] { + for &channel in &lv.mem_channels[2..] { let constr = builder.mul_extension(filter, channel.used); yield_constr.constraint(builder, constr); } // Stack constraints // Pops. - for i in 1..4 { + for i in 1..2 { let channel = lv.mem_channels[i]; { @@ -271,7 +266,7 @@ fn eval_ext_circuit_store, const D: usize>( { let diff = builder.add_const_extension( channel.addr_segment, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), ); let constr = builder.mul_extension(filter, diff); yield_constr.constraint(builder, constr); @@ -285,7 +280,7 @@ fn eval_ext_circuit_store, const D: usize>( } // Constrain `stack_inv_aux`. { - let len_diff = builder.add_const_extension(lv.stack_len, -F::from_canonical_usize(4)); + let len_diff = builder.add_const_extension(lv.stack_len, -F::from_canonical_usize(2)); let diff = builder.mul_sub_extension( len_diff, lv.general.stack().stack_inv, @@ -294,7 +289,7 @@ fn eval_ext_circuit_store, const D: usize>( let constr = builder.mul_extension(lv.op.m_op_general, diff); yield_constr.constraint(builder, constr); } - // If stack_len != 4 and MSTORE, read new top of the stack in nv.mem_channels[0]. + // If stack_len != 2 and MSTORE, read new top of the stack in nv.mem_channels[0]. let top_read_channel = nv.mem_channels[0]; let is_top_read = builder.mul_extension(lv.general.stack().stack_inv_aux, lv.opcode_bits[0]); let is_top_read = builder.sub_extension(lv.general.stack().stack_inv_aux, is_top_read); @@ -321,7 +316,7 @@ fn eval_ext_circuit_store, const D: usize>( { let diff = builder.add_const_extension( top_read_channel.addr_segment, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), ); let constr = builder.mul_extension(new_filter, diff); yield_constr.constraint_transition(builder, constr); @@ -332,7 +327,7 @@ fn eval_ext_circuit_store, const D: usize>( let constr = builder.mul_extension(new_filter, diff); yield_constr.constraint_transition(builder, constr); } - // If stack_len == 4 or MLOAD, disable the channel. + // If stack_len == 2 or MLOAD, disable the channel. { let diff = builder.mul_sub_extension( lv.op.m_op_general, diff --git a/evm/src/cpu/shift.rs b/evm/src/cpu/shift.rs index 3d97c2f1..29baa5ea 100644 --- a/evm/src/cpu/shift.rs +++ b/evm/src/cpu/shift.rs @@ -24,7 +24,7 @@ pub(crate) fn eval_packed( // let val = lv.mem_channels[0]; // let output = lv.mem_channels[NUM_GP_CHANNELS - 1]; - let shift_table_segment = P::Scalar::from_canonical_u64(Segment::ShiftTable as u64); + let shift_table_segment = P::Scalar::from_canonical_usize(Segment::ShiftTable.unscale()); // Only lookup the shifting factor when displacement is < 2^32. // two_exp.used is true (1) if the high limbs of the displacement are @@ -73,7 +73,7 @@ pub(crate) fn eval_ext_circuit, const D: usize>( let displacement = lv.mem_channels[0]; let two_exp = lv.mem_channels[2]; - let shift_table_segment = F::from_canonical_u64(Segment::ShiftTable as u64); + let shift_table_segment = F::from_canonical_usize(Segment::ShiftTable.unscale()); // Only lookup the shifting factor when displacement is < 2^32. // two_exp.used is true (1) if the high limbs of the displacement are diff --git a/evm/src/cpu/stack.rs b/evm/src/cpu/stack.rs index 0497b228..9acf1f3a 100644 --- a/evm/src/cpu/stack.rs +++ b/evm/src/cpu/stack.rs @@ -83,13 +83,13 @@ pub(crate) const JUMPI_OP: Option = Some(StackBehavior { }); /// `StackBehavior` for MLOAD_GENERAL. pub(crate) const MLOAD_GENERAL_OP: Option = Some(StackBehavior { - num_pops: 3, + num_pops: 1, pushes: true, disable_other_channels: false, }); pub(crate) const KECCAK_GENERAL_OP: StackBehavior = StackBehavior { - num_pops: 4, + num_pops: 2, pushes: true, disable_other_channels: true, }; @@ -132,7 +132,7 @@ pub(crate) const STACK_BEHAVIORS: OpsColumnsView> = OpsCol dup_swap: None, context_op: None, m_op_32bytes: Some(StackBehavior { - num_pops: 4, + num_pops: 2, pushes: true, disable_other_channels: false, }), @@ -186,7 +186,8 @@ pub(crate) fn eval_packed_one( yield_constr.constraint(filter * (channel.addr_context - lv.context)); yield_constr.constraint( filter - * (channel.addr_segment - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + * (channel.addr_segment + - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); // Remember that the first read (`i == 1`) is for the second stack element at `stack[stack_len - 1]`. let addr_virtual = lv.stack_len - P::Scalar::from_canonical_usize(i + 1); @@ -212,7 +213,8 @@ pub(crate) fn eval_packed_one( yield_constr.constraint_transition(new_filter * (channel.addr_context - nv.context)); yield_constr.constraint_transition( new_filter - * (channel.addr_segment - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + * (channel.addr_segment + - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); let addr_virtual = nv.stack_len - P::ONES; yield_constr.constraint_transition(new_filter * (channel.addr_virtual - addr_virtual)); @@ -238,7 +240,8 @@ pub(crate) fn eval_packed_one( yield_constr.constraint(new_filter * (channel.addr_context - lv.context)); yield_constr.constraint( new_filter - * (channel.addr_segment - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + * (channel.addr_segment + - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); let addr_virtual = lv.stack_len - P::ONES; yield_constr.constraint(new_filter * (channel.addr_virtual - addr_virtual)); @@ -343,7 +346,7 @@ pub(crate) fn eval_packed( yield_constr.constraint_transition( new_filter * (top_read_channel.addr_segment - - P::Scalar::from_canonical_u64(Segment::Stack as u64)), + - P::Scalar::from_canonical_usize(Segment::Stack.unscale())), ); let addr_virtual = nv.stack_len - P::ONES; yield_constr.constraint_transition(new_filter * (top_read_channel.addr_virtual - addr_virtual)); @@ -397,7 +400,7 @@ pub(crate) fn eval_ext_circuit_one, const D: usize> { let constr = builder.arithmetic_extension( F::ONE, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), filter, channel.addr_segment, filter, @@ -454,7 +457,7 @@ pub(crate) fn eval_ext_circuit_one, const D: usize> { let constr = builder.arithmetic_extension( F::ONE, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), new_filter, channel.addr_segment, new_filter, @@ -507,7 +510,7 @@ pub(crate) fn eval_ext_circuit_one, const D: usize> { let constr = builder.arithmetic_extension( F::ONE, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), new_filter, channel.addr_segment, new_filter, @@ -674,7 +677,7 @@ pub(crate) fn eval_ext_circuit, const D: usize>( { let diff = builder.add_const_extension( top_read_channel.addr_segment, - -F::from_canonical_u64(Segment::Stack as u64), + -F::from_canonical_usize(Segment::Stack.unscale()), ); let constr = builder.mul_extension(new_filter, diff); yield_constr.constraint_transition(builder, constr); diff --git a/evm/src/cpu/syscalls_exceptions.rs b/evm/src/cpu/syscalls_exceptions.rs index 45302b9e..501b114f 100644 --- a/evm/src/cpu/syscalls_exceptions.rs +++ b/evm/src/cpu/syscalls_exceptions.rs @@ -45,7 +45,7 @@ pub(crate) fn eval_packed( } // Look up the handler in memory - let code_segment = P::Scalar::from_canonical_usize(Segment::Code as usize); + let code_segment = P::Scalar::from_canonical_usize(Segment::Code.unscale()); let opcode: P = lv .opcode_bits @@ -153,7 +153,7 @@ pub(crate) fn eval_ext_circuit, const D: usize>( } // Look up the handler in memory - let code_segment = F::from_canonical_usize(Segment::Code as usize); + let code_segment = F::from_canonical_usize(Segment::Code.unscale()); let opcode = lv .opcode_bits diff --git a/evm/src/generation/mod.rs b/evm/src/generation/mod.rs index d691d34e..8ae487b0 100644 --- a/evm/src/generation/mod.rs +++ b/evm/src/generation/mod.rs @@ -155,7 +155,8 @@ fn apply_metadata_and_tries_memops, const D: usize> .map(|(field, val)| { mem_write_log( channel, - MemoryAddress::new(0, Segment::GlobalMetadata, field as usize), + // These fields are already scaled by their segment, and are in context 0 (kernel). + MemoryAddress::new_bundle(U256::from(field as usize)).unwrap(), state, val, ) diff --git a/evm/src/generation/prover_input.rs b/evm/src/generation/prover_input.rs index b2a8f0ce..b60233d9 100644 --- a/evm/src/generation/prover_input.rs +++ b/evm/src/generation/prover_input.rs @@ -20,6 +20,7 @@ use crate::util::{biguint_to_mem_vec, mem_vec_to_biguint, u256_to_usize}; use crate::witness::errors::ProgramError; use crate::witness::errors::ProverInputError::*; use crate::witness::memory::MemoryAddress; +use crate::witness::operation::CONTEXT_SCALING_FACTOR; use crate::witness::util::{current_context_peek, stack_peek}; /// Prover input function represented as a scoped function name. @@ -138,7 +139,7 @@ impl GenerationState { fn run_account_code(&mut self) -> Result { // stack: codehash, ctx, ... let codehash = stack_peek(self, 0)?; - let context = stack_peek(self, 1)?; + let context = stack_peek(self, 1)? >> CONTEXT_SCALING_FACTOR; let context = u256_to_usize(context)?; let mut address = MemoryAddress::new(context, Segment::Code, 0); let code = self @@ -189,11 +190,11 @@ impl GenerationState { m_start_loc: usize, ) -> (Vec, Vec) { let n = self.memory.contexts.len(); - let a = &self.memory.contexts[n - 1].segments[Segment::KernelGeneral as usize].content + let a = &self.memory.contexts[n - 1].segments[Segment::KernelGeneral.unscale()].content [a_start_loc..a_start_loc + len]; - let b = &self.memory.contexts[n - 1].segments[Segment::KernelGeneral as usize].content + let b = &self.memory.contexts[n - 1].segments[Segment::KernelGeneral.unscale()].content [b_start_loc..b_start_loc + len]; - let m = &self.memory.contexts[n - 1].segments[Segment::KernelGeneral as usize].content + let m = &self.memory.contexts[n - 1].segments[Segment::KernelGeneral.unscale()].content [m_start_loc..m_start_loc + len]; let a_biguint = mem_vec_to_biguint(a); diff --git a/evm/src/generation/state.rs b/evm/src/generation/state.rs index 89ff0c5a..fec2e11c 100644 --- a/evm/src/generation/state.rs +++ b/evm/src/generation/state.rs @@ -57,7 +57,7 @@ impl GenerationState { let (trie_roots_ptrs, trie_data) = load_all_mpts(trie_inputs).expect("Invalid MPT data for preinitialization"); - self.memory.contexts[0].segments[Segment::TrieData as usize].content = trie_data; + self.memory.contexts[0].segments[Segment::TrieData.unscale()].content = trie_data; trie_roots_ptrs } @@ -131,13 +131,11 @@ impl GenerationState { } let ctx = self.registers.context; - let returndata_size_addr = MemoryAddress::new( - ctx, - Segment::ContextMetadata, - ContextMetadata::ReturndataSize as usize, - ); + let returndata_offset = ContextMetadata::ReturndataSize.unscale(); + let returndata_size_addr = + MemoryAddress::new(ctx, Segment::ContextMetadata, returndata_offset); let returndata_size = u256_to_usize(self.memory.get(returndata_size_addr))?; - let code = self.memory.contexts[ctx].segments[Segment::Returndata as usize].content + let code = self.memory.contexts[ctx].segments[Segment::Returndata.unscale()].content [..returndata_size] .iter() .map(|x| x.low_u32() as u8) diff --git a/evm/src/generation/trie_extractor.rs b/evm/src/generation/trie_extractor.rs index a7c97e10..d55a1fbf 100644 --- a/evm/src/generation/trie_extractor.rs +++ b/evm/src/generation/trie_extractor.rs @@ -58,7 +58,7 @@ pub(crate) fn read_trie_helper( ) -> Result<(), ProgramError> { let load = |offset| memory.get(MemoryAddress::new(0, Segment::TrieData, offset)); let load_slice_from = |init_offset| { - &memory.contexts[0].segments[Segment::TrieData as usize].content[init_offset..] + &memory.contexts[0].segments[Segment::TrieData.unscale()].content[init_offset..] }; let trie_type = PartialTrieType::all()[u256_to_usize(load(ptr))?]; diff --git a/evm/src/keccak_sponge/keccak_sponge_stark.rs b/evm/src/keccak_sponge/keccak_sponge_stark.rs index 2cfc3409..46ab7179 100644 --- a/evm/src/keccak_sponge/keccak_sponge_stark.rs +++ b/evm/src/keccak_sponge/keccak_sponge_stark.rs @@ -859,11 +859,7 @@ mod tests { let expected_output = keccak(&input); let op = KeccakSpongeOp { - base_address: MemoryAddress { - context: 0, - segment: Segment::Code as usize, - virt: 0, - }, + base_address: MemoryAddress::new(0, Segment::Code, 0), timestamp: 0, input, }; diff --git a/evm/src/memory/memory_stark.rs b/evm/src/memory/memory_stark.rs index c2af69b5..e596f421 100644 --- a/evm/src/memory/memory_stark.rs +++ b/evm/src/memory/memory_stark.rs @@ -368,7 +368,7 @@ impl, const D: usize> Stark for MemoryStark, const D: usize> Stark for MemoryStark usize { + *self as usize >> SEGMENT_SCALING_FACTOR + } + pub(crate) const fn all() -> [Self; Self::COUNT] { [ Self::Code, diff --git a/evm/src/recursive_verifier.rs b/evm/src/recursive_verifier.rs index 633a8d33..5c10e3b3 100644 --- a/evm/src/recursive_verifier.rs +++ b/evm/src/recursive_verifier.rs @@ -431,78 +431,94 @@ pub(crate) fn get_memory_extra_looking_sum_circuit, // Add metadata writes. let block_fields_scalars = [ ( - GlobalMetadata::BlockTimestamp as usize, + GlobalMetadata::BlockTimestamp, public_values.block_metadata.block_timestamp, ), ( - GlobalMetadata::BlockNumber as usize, + GlobalMetadata::BlockNumber, public_values.block_metadata.block_number, ), ( - GlobalMetadata::BlockDifficulty as usize, + GlobalMetadata::BlockDifficulty, public_values.block_metadata.block_difficulty, ), ( - GlobalMetadata::BlockGasLimit as usize, + GlobalMetadata::BlockGasLimit, public_values.block_metadata.block_gaslimit, ), ( - GlobalMetadata::BlockChainId as usize, + GlobalMetadata::BlockChainId, public_values.block_metadata.block_chain_id, ), ( - GlobalMetadata::BlockGasUsed as usize, + GlobalMetadata::BlockGasUsed, public_values.block_metadata.block_gas_used, ), ( - GlobalMetadata::BlockGasUsedBefore as usize, + GlobalMetadata::BlockGasUsedBefore, public_values.extra_block_data.gas_used_before, ), ( - GlobalMetadata::BlockGasUsedAfter as usize, + GlobalMetadata::BlockGasUsedAfter, public_values.extra_block_data.gas_used_after, ), ( - GlobalMetadata::TxnNumberBefore as usize, + GlobalMetadata::TxnNumberBefore, public_values.extra_block_data.txn_number_before, ), ( - GlobalMetadata::TxnNumberAfter as usize, + GlobalMetadata::TxnNumberAfter, public_values.extra_block_data.txn_number_after, ), ]; - let beneficiary_random_base_fee_cur_hash_fields: [(usize, &[Target]); 4] = [ + let beneficiary_random_base_fee_cur_hash_fields: [(GlobalMetadata, &[Target]); 4] = [ ( - GlobalMetadata::BlockBeneficiary as usize, + GlobalMetadata::BlockBeneficiary, &public_values.block_metadata.block_beneficiary, ), ( - GlobalMetadata::BlockRandom as usize, + GlobalMetadata::BlockRandom, &public_values.block_metadata.block_random, ), ( - GlobalMetadata::BlockBaseFee as usize, + GlobalMetadata::BlockBaseFee, &public_values.block_metadata.block_base_fee, ), ( - GlobalMetadata::BlockCurrentHash as usize, + GlobalMetadata::BlockCurrentHash, &public_values.block_hashes.cur_hash, ), ]; - let metadata_segment = builder.constant(F::from_canonical_u32(Segment::GlobalMetadata as u32)); + let metadata_segment = + builder.constant(F::from_canonical_usize(Segment::GlobalMetadata.unscale())); block_fields_scalars.map(|(field, target)| { // Each of those fields fit in 32 bits, hence in a single Target. - sum = add_data_write(builder, challenge, sum, metadata_segment, field, &[target]); + sum = add_data_write( + builder, + challenge, + sum, + metadata_segment, + field.unscale(), + &[target], + ); }); beneficiary_random_base_fee_cur_hash_fields.map(|(field, targets)| { - sum = add_data_write(builder, challenge, sum, metadata_segment, field, targets); + sum = add_data_write( + builder, + challenge, + sum, + metadata_segment, + field.unscale(), + targets, + ); }); // Add block hashes writes. - let block_hashes_segment = builder.constant(F::from_canonical_u32(Segment::BlockHashes as u32)); + let block_hashes_segment = + builder.constant(F::from_canonical_usize(Segment::BlockHashes.unscale())); for i in 0..256 { sum = add_data_write( builder, @@ -515,7 +531,8 @@ pub(crate) fn get_memory_extra_looking_sum_circuit, } // Add block bloom filters writes. - let bloom_segment = builder.constant(F::from_canonical_u32(Segment::GlobalBlockBloom as u32)); + let bloom_segment = + builder.constant(F::from_canonical_usize(Segment::GlobalBlockBloom.unscale())); for i in 0..8 { sum = add_data_write( builder, @@ -530,33 +547,40 @@ pub(crate) fn get_memory_extra_looking_sum_circuit, // Add trie roots writes. let trie_fields = [ ( - GlobalMetadata::StateTrieRootDigestBefore as usize, + GlobalMetadata::StateTrieRootDigestBefore, public_values.trie_roots_before.state_root, ), ( - GlobalMetadata::TransactionTrieRootDigestBefore as usize, + GlobalMetadata::TransactionTrieRootDigestBefore, public_values.trie_roots_before.transactions_root, ), ( - GlobalMetadata::ReceiptTrieRootDigestBefore as usize, + GlobalMetadata::ReceiptTrieRootDigestBefore, public_values.trie_roots_before.receipts_root, ), ( - GlobalMetadata::StateTrieRootDigestAfter as usize, + GlobalMetadata::StateTrieRootDigestAfter, public_values.trie_roots_after.state_root, ), ( - GlobalMetadata::TransactionTrieRootDigestAfter as usize, + GlobalMetadata::TransactionTrieRootDigestAfter, public_values.trie_roots_after.transactions_root, ), ( - GlobalMetadata::ReceiptTrieRootDigestAfter as usize, + GlobalMetadata::ReceiptTrieRootDigestAfter, public_values.trie_roots_after.receipts_root, ), ]; trie_fields.map(|(field, targets)| { - sum = add_data_write(builder, challenge, sum, metadata_segment, field, &targets); + sum = add_data_write( + builder, + challenge, + sum, + metadata_segment, + field.unscale(), + &targets, + ); }); // Add kernel hash and kernel length. @@ -567,7 +591,7 @@ pub(crate) fn get_memory_extra_looking_sum_circuit, challenge, sum, metadata_segment, - GlobalMetadata::KernelHash as usize, + GlobalMetadata::KernelHash.unscale(), &kernel_hash_targets, ); let kernel_len_target = builder.constant(F::from_canonical_usize(KERNEL.code.len())); @@ -576,7 +600,7 @@ pub(crate) fn get_memory_extra_looking_sum_circuit, challenge, sum, metadata_segment, - GlobalMetadata::KernelLen as usize, + GlobalMetadata::KernelLen.unscale(), &[kernel_len_target], ); diff --git a/evm/src/verifier.rs b/evm/src/verifier.rs index 7ca564f2..b69cd927 100644 --- a/evm/src/verifier.rs +++ b/evm/src/verifier.rs @@ -239,19 +239,22 @@ where (GlobalMetadata::KernelLen, KERNEL.code.len().into()), ]; - let segment = F::from_canonical_u32(Segment::GlobalMetadata as u32); + let segment = F::from_canonical_usize(Segment::GlobalMetadata.unscale()); - fields.map(|(field, val)| sum = add_data_write(challenge, segment, sum, field as usize, val)); + fields.map(|(field, val)| { + // These fields are already scaled by their segment, and are in context 0 (kernel). + sum = add_data_write(challenge, segment, sum, field.unscale(), val) + }); // Add block bloom writes. - let bloom_segment = F::from_canonical_u32(Segment::GlobalBlockBloom as u32); + let bloom_segment = F::from_canonical_usize(Segment::GlobalBlockBloom.unscale()); for index in 0..8 { let val = public_values.block_metadata.block_bloom[index]; sum = add_data_write(challenge, bloom_segment, sum, index, val); } // Add Blockhashes writes. - let block_hashes_segment = F::from_canonical_u32(Segment::BlockHashes as u32); + let block_hashes_segment = F::from_canonical_usize(Segment::BlockHashes.unscale()); for index in 0..256 { let val = h2u(public_values.block_hashes.prev_hashes[index]); sum = add_data_write(challenge, block_hashes_segment, sum, index, val); @@ -547,22 +550,22 @@ pub(crate) mod testutils { (GlobalMetadata::KernelLen, KERNEL.code.len().into()), ]; - let segment = F::from_canonical_u32(Segment::GlobalMetadata as u32); + let segment = F::from_canonical_usize(Segment::GlobalMetadata.unscale()); let mut extra_looking_rows = Vec::new(); fields.map(|(field, val)| { - extra_looking_rows.push(add_extra_looking_row(segment, field as usize, val)) + extra_looking_rows.push(add_extra_looking_row(segment, field.unscale(), val)) }); // Add block bloom writes. - let bloom_segment = F::from_canonical_u32(Segment::GlobalBlockBloom as u32); + let bloom_segment = F::from_canonical_usize(Segment::GlobalBlockBloom.unscale()); for index in 0..8 { let val = public_values.block_metadata.block_bloom[index]; extra_looking_rows.push(add_extra_looking_row(bloom_segment, index, val)); } // Add Blockhashes writes. - let block_hashes_segment = F::from_canonical_u32(Segment::BlockHashes as u32); + let block_hashes_segment = F::from_canonical_usize(Segment::BlockHashes.unscale()); for index in 0..256 { let val = h2u(public_values.block_hashes.prev_hashes[index]); extra_looking_rows.push(add_extra_looking_row(block_hashes_segment, index, val)); diff --git a/evm/src/witness/memory.rs b/evm/src/witness/memory.rs index 8cb7daf7..ff4c9614 100644 --- a/evm/src/witness/memory.rs +++ b/evm/src/witness/memory.rs @@ -11,8 +11,9 @@ pub(crate) enum MemoryChannel { use MemoryChannel::{Code, GeneralPurpose, PartialChannel}; +use super::operation::CONTEXT_SCALING_FACTOR; use crate::cpu::kernel::constants::global_metadata::GlobalMetadata; -use crate::memory::segments::Segment; +use crate::memory::segments::{Segment, SEGMENT_SCALING_FACTOR}; use crate::witness::errors::MemoryError::{ContextTooLarge, SegmentTooLarge, VirtTooLarge}; use crate::witness::errors::ProgramError; use crate::witness::errors::ProgramError::MemoryError; @@ -41,7 +42,8 @@ impl MemoryAddress { pub(crate) const fn new(context: usize, segment: Segment, virt: usize) -> Self { Self { context, - segment: segment as usize, + // segment is scaled + segment: segment.unscale(), virt, } } @@ -69,6 +71,17 @@ impl MemoryAddress { }) } + /// Creates a new `MemoryAddress` from a bundled address fitting a `U256`. + /// It will recover the virtual offset as the lowest 32-bit limb, the segment + /// as the next limb, and the context as the next one. + pub(crate) fn new_bundle(addr: U256) -> Result { + let virt = addr.low_u32().into(); + let segment = (addr >> SEGMENT_SCALING_FACTOR).low_u32().into(); + let context = (addr >> CONTEXT_SCALING_FACTOR).low_u32().into(); + + Self::new_u256s(context, segment, virt) + } + pub(crate) fn increment(&mut self) { self.virt = self.virt.saturating_add(1); } @@ -153,7 +166,7 @@ impl MemoryState { pub(crate) fn new(kernel_code: &[u8]) -> Self { let code_u256s = kernel_code.iter().map(|&x| x.into()).collect(); let mut result = Self::default(); - result.contexts[0].segments[Segment::Code as usize].content = code_u256s; + result.contexts[0].segments[Segment::Code.unscale()].content = code_u256s; result } @@ -204,12 +217,9 @@ impl MemoryState { self.contexts[address.context].segments[address.segment].set(address.virt, val); } + // These fields are already scaled by their respective segment. pub(crate) fn read_global_metadata(&self, field: GlobalMetadata) -> U256 { - self.get(MemoryAddress::new( - 0, - Segment::GlobalMetadata, - field as usize, - )) + self.get(MemoryAddress::new_bundle(U256::from(field as usize)).unwrap()) } } diff --git a/evm/src/witness/operation.rs b/evm/src/witness/operation.rs index 848dae85..c9dea230 100644 --- a/evm/src/witness/operation.rs +++ b/evm/src/witness/operation.rs @@ -19,9 +19,8 @@ use crate::extension_tower::BN_BASE; use crate::generation::state::GenerationState; use crate::memory::segments::Segment; use crate::util::u256_to_usize; -use crate::witness::errors::MemoryError::{ContextTooLarge, SegmentTooLarge, VirtTooLarge}; +use crate::witness::errors::MemoryError::VirtTooLarge; use crate::witness::errors::ProgramError; -use crate::witness::errors::ProgramError::MemoryError; use crate::witness::memory::{MemoryAddress, MemoryChannel, MemoryOp, MemoryOpKind}; use crate::witness::operation::MemoryChannel::GeneralPurpose; use crate::witness::transition::fill_stack_fields; @@ -59,6 +58,10 @@ pub(crate) enum Operation { MstoreGeneral, } +// Contexts in the kernel are shifted by 2^64, so that they can be combined with +// the segment and virtual address components in a single U256 word. +pub(crate) const CONTEXT_SCALING_FACTOR: usize = 64; + /// Adds a CPU row filled with the two inputs and the output of a logic operation. /// Generates a new logic operation and adds it to the vector of operation in `LogicStark`. /// Adds three memory read operations to `MemoryStark`: for the two inputs and the output. @@ -129,11 +132,10 @@ pub(crate) fn generate_keccak_general( state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { - let [(context, _), (segment, log_in1), (base_virt, log_in2), (len, log_in3)] = - stack_pop_with_log_and_fill::<4, _>(state, &mut row)?; + let [(addr, _), (len, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; let len = u256_to_usize(len)?; - let base_address = MemoryAddress::new_u256s(context, segment, base_virt)?; + let base_address = MemoryAddress::new_bundle(addr)?; let input = (0..len) .map(|i| { let address = MemoryAddress { @@ -152,8 +154,6 @@ pub(crate) fn generate_keccak_general( keccak_sponge_log(state, base_address, input); state.traces.push_memory(log_in1); - state.traces.push_memory(log_in2); - state.traces.push_memory(log_in3); state.traces.push_cpu(row); Ok(()) } @@ -191,7 +191,7 @@ pub(crate) fn generate_pop( ) -> Result<(), ProgramError> { let [(_, _)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?; - let diff = row.stack_len - F::from_canonical_usize(1); + let diff = row.stack_len - F::ONE; if let Some(inv) = diff.try_inverse() { row.general.stack_mut().stack_inv = inv; row.general.stack_mut().stack_inv_aux = F::ONE; @@ -352,7 +352,11 @@ pub(crate) fn generate_get_context( let res = mem_write_gp_log_and_fill(3, address, state, &mut row, state.registers.stack_top); Some(res) }; - push_no_write(state, state.registers.context.into()); + push_no_write( + state, + // The fetched value needs to be scaled before being pushed. + U256::from(state.registers.context) << CONTEXT_SCALING_FACTOR, + ); if let Some(log) = write { state.traces.push_memory(log); } @@ -369,9 +373,10 @@ pub(crate) fn generate_set_context( let sp_to_save = state.registers.stack_len.into(); let old_ctx = state.registers.context; - let new_ctx = u256_to_usize(ctx)?; + // The popped value needs to be scaled down. + let new_ctx = u256_to_usize(ctx >> CONTEXT_SCALING_FACTOR)?; - let sp_field = ContextMetadata::StackSize as usize; + let sp_field = ContextMetadata::StackSize.unscale(); let old_sp_addr = MemoryAddress::new(old_ctx, Segment::ContextMetadata, sp_field); let new_sp_addr = MemoryAddress::new(new_ctx, Segment::ContextMetadata, sp_field); @@ -390,7 +395,7 @@ pub(crate) fn generate_set_context( channel.used = F::ONE; channel.is_read = F::ONE; channel.addr_context = F::from_canonical_usize(new_ctx); - channel.addr_segment = F::from_canonical_usize(Segment::ContextMetadata as usize); + channel.addr_segment = F::from_canonical_usize(Segment::ContextMetadata.unscale()); channel.addr_virtual = F::from_canonical_usize(new_sp_addr.virt); let val_limbs: [u64; 4] = sp_to_save.0; for (i, limb) in val_limbs.into_iter().enumerate() { @@ -433,6 +438,7 @@ pub(crate) fn generate_set_context( state.traces.push_memory(log_write_old_sp); state.traces.push_memory(log_read_new_sp); state.traces.push_cpu(row); + Ok(()) } @@ -575,7 +581,7 @@ pub(crate) fn generate_not( // This is necessary for the stack constraints for POP, // since the two flags are combined. - let diff = row.stack_len - F::from_canonical_usize(1); + let diff = row.stack_len - F::ONE; if let Some(inv) = diff.try_inverse() { row.general.stack_mut().stack_inv = inv; row.general.stack_mut().stack_inv_aux = F::ONE; @@ -808,18 +814,16 @@ pub(crate) fn generate_mload_general( state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { - let [(context, _), (segment, log_in1), (virt, log_in2)] = - stack_pop_with_log_and_fill::<3, _>(state, &mut row)?; + let [(addr, _)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?; - let (val, log_read) = mem_read_gp_with_log_and_fill( - 3, - MemoryAddress::new_u256s(context, segment, virt)?, - state, - &mut row, - ); + let (val, log_read) = + mem_read_gp_with_log_and_fill(1, MemoryAddress::new_bundle(addr)?, state, &mut row); push_no_write(state, val); - let diff = row.stack_len - F::from_canonical_usize(4); + // Because MLOAD_GENERAL performs 1 pop and 1 push, it does not make use of the `stack_inv_aux` general columns. + // We hence can set the diff to 2 (instead of 1) so that the stack constraint for MSTORE_GENERAL applies to both + // operations, which are combined into a single CPU flag. + let diff = row.stack_len - F::TWO; if let Some(inv) = diff.try_inverse() { row.general.stack_mut().stack_inv = inv; row.general.stack_mut().stack_inv_aux = F::ONE; @@ -828,8 +832,6 @@ pub(crate) fn generate_mload_general( row.general.stack_mut().stack_inv_aux = F::ZERO; } - state.traces.push_memory(log_in1); - state.traces.push_memory(log_in2); state.traces.push_memory(log_read); state.traces.push_cpu(row); Ok(()) @@ -839,15 +841,14 @@ pub(crate) fn generate_mload_32bytes( state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { - let [(context, _), (segment, log_in1), (base_virt, log_in2), (len, log_in3)] = - stack_pop_with_log_and_fill::<4, _>(state, &mut row)?; + let [(addr, _), (len, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; let len = u256_to_usize(len)?; if len > 32 { // The call to `U256::from_big_endian()` would panic. return Err(ProgramError::IntegerTooLarge); } - let base_address = MemoryAddress::new_u256s(context, segment, base_virt)?; + let base_address = MemoryAddress::new_bundle(addr)?; if usize::MAX - base_address.virt < len { return Err(ProgramError::MemoryError(VirtTooLarge { virt: base_address.virt.into(), @@ -870,8 +871,6 @@ pub(crate) fn generate_mload_32bytes( byte_packing_log(state, base_address, bytes); state.traces.push_memory(log_in1); - state.traces.push_memory(log_in2); - state.traces.push_memory(log_in3); state.traces.push_cpu(row); Ok(()) } @@ -880,23 +879,12 @@ pub(crate) fn generate_mstore_general( state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { - let [(val, _), (context, log_in1), (segment, log_in2), (virt, log_in3)] = - stack_pop_with_log_and_fill::<4, _>(state, &mut row)?; + let [(val, _), (addr, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; - let address = MemoryAddress { - context: context - .try_into() - .map_err(|_| MemoryError(ContextTooLarge { context }))?, - segment: segment - .try_into() - .map_err(|_| MemoryError(SegmentTooLarge { segment }))?, - virt: virt - .try_into() - .map_err(|_| MemoryError(VirtTooLarge { virt }))?, - }; + let address = MemoryAddress::new_bundle(addr)?; let log_write = mem_write_partial_log_and_fill(address, state, &mut row, val); - let diff = row.stack_len - F::from_canonical_usize(4); + let diff = row.stack_len - F::TWO; if let Some(inv) = diff.try_inverse() { row.general.stack_mut().stack_inv = inv; row.general.stack_mut().stack_inv_aux = F::ONE; @@ -908,8 +896,6 @@ pub(crate) fn generate_mstore_general( } state.traces.push_memory(log_in1); - state.traces.push_memory(log_in2); - state.traces.push_memory(log_in3); state.traces.push_memory(log_write); state.traces.push_cpu(row); @@ -922,19 +908,16 @@ pub(crate) fn generate_mstore_32bytes( state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { - let [(context, _), (segment, log_in1), (base_virt, log_in2), (val, log_in3)] = - stack_pop_with_log_and_fill::<4, _>(state, &mut row)?; + let [(addr, _), (val, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?; - let base_address = MemoryAddress::new_u256s(context, segment, base_virt)?; + let base_address = MemoryAddress::new_bundle(addr)?; byte_unpacking_log(state, base_address, val, n as usize); - let new_offset = base_virt + n; - push_no_write(state, new_offset); + let new_addr = addr + n; + push_no_write(state, new_addr); state.traces.push_memory(log_in1); - state.traces.push_memory(log_in2); - state.traces.push_memory(log_in3); state.traces.push_cpu(row); Ok(()) } diff --git a/evm/src/witness/transition.rs b/evm/src/witness/transition.rs index cf2e3bbe..835ff593 100644 --- a/evm/src/witness/transition.rs +++ b/evm/src/witness/transition.rs @@ -299,11 +299,11 @@ fn perform_op( state.registers.gas_used += gas_to_charge(op); - let gas_limit_address = MemoryAddress { - context: state.registers.context, - segment: Segment::ContextMetadata as usize, - virt: ContextMetadata::GasLimit as usize, - }; + let gas_limit_address = MemoryAddress::new( + state.registers.context, + Segment::ContextMetadata, + ContextMetadata::GasLimit.unscale(), // context offsets are already scaled + ); if !state.registers.is_kernel { let gas_limit = TryInto::::try_into(state.memory.get(gas_limit_address)); match gas_limit { @@ -345,14 +345,14 @@ pub(crate) fn fill_stack_fields( channel.used = F::ONE; channel.is_read = F::ONE; channel.addr_context = F::from_canonical_usize(state.registers.context); - channel.addr_segment = F::from_canonical_usize(Segment::Stack as usize); + channel.addr_segment = F::from_canonical_usize(Segment::Stack.unscale()); channel.addr_virtual = F::from_canonical_usize(state.registers.stack_len - 1); - let address = MemoryAddress { - context: state.registers.context, - segment: Segment::Stack as usize, - virt: state.registers.stack_len - 1, - }; + let address = MemoryAddress::new( + state.registers.context, + Segment::Stack, + state.registers.stack_len - 1, + ); let mem_op = MemoryOp::new( GeneralPurpose(0), @@ -494,7 +494,7 @@ pub(crate) fn transition(state: &mut GenerationState) -> anyhow::Re e, offset_name, state.stack(), - state.memory.contexts[0].segments[Segment::KernelGeneral as usize].content, + state.memory.contexts[0].segments[Segment::KernelGeneral.unscale()].content, ); } state.rollback(checkpoint); diff --git a/evm/tests/log_opcode.rs b/evm/tests/log_opcode.rs index b23b4402..8d56c6bd 100644 --- a/evm/tests/log_opcode.rs +++ b/evm/tests/log_opcode.rs @@ -442,7 +442,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> { // Preprocess all circuits. let all_circuits = AllRecursiveCircuits::::new( &all_stark, - &[16..17, 13..16, 15..18, 14..15, 9..10, 12..13, 17..20], + &[16..17, 13..16, 15..18, 14..15, 10..11, 12..13, 17..20], &config, );