Merge branch 'main' into plonky2_doc

This commit is contained in:
Robin Salen 2024-01-10 09:01:30 +01:00
commit 33def0841d
No known key found for this signature in database
GPG Key ID: F98FD38F65687358
98 changed files with 2554 additions and 1860 deletions

View File

@ -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:}

Binary file not shown.

View File

@ -11,7 +11,7 @@ use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::util::transpose;
use static_assertions::const_assert;
use super::columns::NUM_ARITH_COLUMNS;
use super::columns::{op_flags, NUM_ARITH_COLUMNS};
use super::shift;
use crate::all_stark::Table;
use crate::arithmetic::columns::{NUM_SHARED_COLS, RANGE_COUNTER, RC_FREQUENCIES, SHARED_COLS};
@ -208,6 +208,12 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for ArithmeticSta
let lv: &[P; NUM_ARITH_COLUMNS] = vars.get_local_values().try_into().unwrap();
let nv: &[P; NUM_ARITH_COLUMNS] = vars.get_next_values().try_into().unwrap();
// Flags must be boolean.
for flag_idx in op_flags() {
let flag = lv[flag_idx];
yield_constr.constraint(flag * (flag - P::ONES));
}
// Check that `OPCODE_COL` holds 0 if the operation is not a range_check.
let opcode_constraint = (P::ONES - lv[columns::IS_RANGE_CHECK]) * lv[columns::OPCODE_COL];
yield_constr.constraint(opcode_constraint);
@ -248,6 +254,13 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for ArithmeticSta
let nv: &[ExtensionTarget<D>; NUM_ARITH_COLUMNS] =
vars.get_next_values().try_into().unwrap();
// Flags must be boolean.
for flag_idx in op_flags() {
let flag = lv[flag_idx];
let constraint = builder.mul_sub_extension(flag, flag, flag);
yield_constr.constraint(builder, constraint);
}
// Check that `OPCODE_COL` holds 0 if the operation is not a range_check.
let opcode_constraint = builder.arithmetic_extension(
F::NEG_ONE,

View File

@ -43,6 +43,10 @@ pub(crate) const IS_RANGE_CHECK: usize = IS_SHR + 1;
pub(crate) const OPCODE_COL: usize = IS_RANGE_CHECK + 1;
pub(crate) const START_SHARED_COLS: usize = OPCODE_COL + 1;
pub(crate) const fn op_flags() -> Range<usize> {
IS_ADD..IS_RANGE_CHECK + 1
}
/// Within the Arithmetic Unit, there are shared columns which can be
/// used by any arithmetic circuit, depending on which one is active
/// this cycle.

View File

@ -1,16 +1,25 @@
use plonky2::fri::reduction_strategies::FriReductionStrategy;
use plonky2::fri::{FriConfig, FriParams};
/// A configuration containing the different parameters to be used by the STARK prover.
pub struct StarkConfig {
/// The targeted security level for the proofs generated with this configuration.
pub security_bits: usize,
/// The number of challenge points to generate, for IOPs that have soundness errors of (roughly)
/// `degree / |F|`.
pub num_challenges: usize,
/// The configuration of the FRI sub-protocol.
pub fri_config: FriConfig,
}
impl Default for StarkConfig {
fn default() -> Self {
Self::standard_fast_config()
}
}
impl StarkConfig {
/// A typical configuration with a rate of 2, resulting in fast but large proofs.
/// Targets ~100 bit conjectured security.

View File

@ -16,8 +16,13 @@ pub(crate) fn eval_packed<P: PackedField>(
// 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<P: PackedField>(
.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<F: RichField + Extendable<D>, 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<F: RichField + Extendable<D>, 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);
}

View File

@ -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<P: PackedField>(
// 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<F: RichField + Extendable<D>, 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<P: PackedField>(
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<F: RichField + Extendable<D>, 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<P: PackedField>(
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<F: RichField + Extendable<D>, 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);

View File

@ -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<F: Field>() -> Vec<Column<F>> {
// 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<F: Field>() -> Vec<Column<F>> {
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<T: Copy>(lv: &CpuColumnsView<T>, 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<F: Field>(channel: usize) -> Column<F> {
let scalar = F::from_canonical_usize(NUM_CHANNELS);
@ -234,10 +250,10 @@ fn mem_time_and_channel<F: Field>(channel: usize) -> Column<F> {
/// Creates the vector of `Columns` corresponding to the contents of the code channel when reading code values.
pub(crate) fn ctl_data_code_memory<F: Field>() -> Vec<Column<F>> {
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

View File

@ -54,7 +54,7 @@ fn constrain_channel_packed<P: PackedField>(
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<F: RichField + Extendable<D>, 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,

View File

@ -87,7 +87,8 @@ pub(crate) fn eval_packed_jump_jumpi<P: PackedField>(
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<P: PackedField>(
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<F: RichField + Extendable<D>, 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<F: RichField + Extendable<D>, 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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -6,10 +6,6 @@ global sys_stop:
// Set the parent context's return data size to 0.
%mstore_parent_context_metadata(@CTX_METADATA_RETURNDATA_SIZE, 0)
// This makes sure the gas used hasn't overflowed the gaslimit.
// This could happen when executing a native instruction (i.e. not a syscall).
%charge_gas_const(0)
%leftover_gas
// stack: leftover_gas
PUSH 1 // success
@ -33,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:
@ -133,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:

View File

@ -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

View File

@ -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)

View File

@ -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,13 @@ 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)
// Encode constant nodes
%initialize_rlp_segment
// Initialize the state, transaction and receipt trie root pointers.
PROVER_INPUT(trie_ptr::state)

View File

@ -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,14 @@
// 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)
%macro swap_mstore
// stack: addr, value
SWAP1
MSTORE_GENERAL
// stack: (empty)
%endmacro

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -37,18 +37,10 @@ after_mpt_delete_extension_branch:
// stack: child_type, updated_child_node_ptr, node_payload_ptr, node_len, node_key, retdest
POP
// stack: updated_child_node_ptr, node_payload_ptr, node_len, node_key, retdest
SWAP1
// stack: extension_ptr, updated_child_node_ptr, node_len, node_key, retdest
PUSH @MPT_NODE_EXTENSION DUP2 %mstore_trie_data
// stack: extension_ptr, updated_child_node_ptr, node_len, node_key, retdest
DUP3 DUP2 %mstore_trie_data // Append node_len to our node
// stack: extension_ptr, updated_child_node_ptr, node_len, node_key, retdest
DUP4 DUP2 %mstore_trie_data // Append node_key to our node
// stack: extension_ptr, updated_child_node_ptr, node_len, node_key, retdest
SWAP1 DUP2 %mstore_trie_data // Append updated_child_node_ptr to our node
// stack: extension_ptr, node_len, node_key, retdest
DUP2 %add_const(2) %mstore_trie_data
// stack: node_payload_ptr, node_len, node_key, retdest
%decrement
%stack (extension_ptr, node_len, node_key, retdest) -> (retdest, extension_ptr)
// stack: extension_ptr, retdest
JUMP
after_mpt_delete_extension_extension:

View File

@ -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)
@ -49,10 +47,9 @@ mpt_hash_hash_rlp_after_unpacking:
// The result is given as a (value, length) pair, where the length is given
// in bytes.
//
// Pre stack: node_ptr, encode_value, retdest
// Post stack: result, result_len
// Pre stack: node_ptr, encode_value, cur_len, retdest
// Post stack: result, result_len, cur_len
global encode_or_hash_node:
// stack: node_ptr, encode_value, cur_len, retdest
DUP1 %mload_trie_data
// Check if we're dealing with a concrete node, i.e. not a hash node.
@ -80,23 +77,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)
@ -124,19 +117,8 @@ encode_node:
global encode_node_empty:
// stack: node_type, node_payload_ptr, encode_value, cur_len, retdest
// Then length of `TrieData` is unchanged here.
%pop3
// stack: cur_len, retdest
// An empty node is encoded as a single byte, 0x80, which is the RLP encoding of the empty string.
// TODO: Write this byte just once to RLP memory, then we can always return (0, 1).
%alloc_rlp_block
// stack: rlp_pos, cur_len, retdest
PUSH 0x80
// stack: 0x80, rlp_pos, cur_len, retdest
DUP2
// stack: rlp_pos, 0x80, rlp_pos, cur_len, retdest
%mstore_rlp
%stack (rlp_pos, cur_len, retdest) -> (retdest, rlp_pos, 1, cur_len)
%stack (cur_len, retdest) -> (retdest, @ENCODED_EMPTY_NODE_POS, 1, cur_len)
JUMP
global encode_node_branch:
@ -147,33 +129,19 @@ global encode_node_branch:
SWAP2 %add_const(18) SWAP2
// stack: node_payload_ptr, encode_value, cur_len, retdest
// Get the next unused offset within the encoded child buffers.
// Then immediately increment the next unused offset by 16, so any
// recursive calls will use nonoverlapping offsets.
// TODO: Allocate a block of RLP memory instead?
%mload_global_metadata(@GLOBAL_METADATA_TRIE_ENCODED_CHILD_SIZE)
DUP1 %add_const(16)
%mstore_global_metadata(@GLOBAL_METADATA_TRIE_ENCODED_CHILD_SIZE)
// stack: base_offset, node_payload_ptr, encode_value, cur_len, retdest
// We will call encode_or_hash_node on each child. For the i'th child, we
// will store the result in SEGMENT_TRIE_ENCODED_CHILD[base + i], and its length in
// SEGMENT_TRIE_ENCODED_CHILD_LEN[base + i].
// Allocate a block of RLP memory
%alloc_rlp_block DUP1
// stack: rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len retdest
// Call encode_or_hash_node on each child
%encode_child(0) %encode_child(1) %encode_child(2) %encode_child(3)
%encode_child(4) %encode_child(5) %encode_child(6) %encode_child(7)
%encode_child(8) %encode_child(9) %encode_child(10) %encode_child(11)
%encode_child(12) %encode_child(13) %encode_child(14) %encode_child(15)
// stack: base_offset, node_payload_ptr, encode_value, cur_len, retdest
// Now, append each child to our RLP tape.
%alloc_rlp_block DUP1
// stack: rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest
%append_child(0) %append_child(1) %append_child(2) %append_child(3)
%append_child(4) %append_child(5) %append_child(6) %append_child(7)
%append_child(8) %append_child(9) %append_child(10) %append_child(11)
%append_child(12) %append_child(13) %append_child(14) %append_child(15)
// stack: rlp_pos', rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest
// stack: rlp_pos', rlp_start, node_payload_ptr, encode_value, cur_len, retdest
%stack (rlp_pos, rlp_start, base_offset, node_payload_ptr)
%stack (rlp_pos, rlp_start, node_payload_ptr)
-> (node_payload_ptr, rlp_pos, rlp_start)
%add_const(16)
// stack: value_ptr_ptr, rlp_pos', rlp_start, encode_value, cur_len, retdest
@ -183,8 +151,8 @@ global encode_node_branch:
// No value; append the empty string (0x80).
// stack: value_ptr, rlp_pos', rlp_start, encode_value, cur_len, retdest
%stack (value_ptr, rlp_pos, rlp_start, encode_value) -> (rlp_pos, 0x80, rlp_pos, rlp_start)
%mstore_rlp
%stack (value_ptr, rlp_pos, rlp_start, encode_value) -> (0x80, rlp_pos, rlp_pos, rlp_start)
MSTORE_GENERAL
// stack: rlp_pos', rlp_start, cur_len, retdest
%increment
// stack: rlp_pos'', rlp_start, cur_len, retdest
@ -205,48 +173,36 @@ encode_node_branch_prepend_prefix:
-> (retdest, rlp_prefix_start, rlp_len, cur_len)
JUMP
// Part of the encode_node_branch function. Encodes the i'th child.
// Stores the result in SEGMENT_TRIE_ENCODED_CHILD[base + i], and its length in
// SEGMENT_TRIE_ENCODED_CHILD_LEN[base + i].
%macro encode_child(i)
// stack: base_offset, node_payload_ptr, encode_value, cur_len, retdest
// stack: rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest
PUSH %%after_encode
DUP4 DUP4
// stack: node_payload_ptr, encode_value, %%after_encode, base_offset, node_payload_ptr, encode_value, cur_len, retdest
DUP6 DUP6 DUP6
// stack: node_payload_ptr, encode_value, cur_len, %%after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest
%add_const($i) %mload_trie_data
// stack: child_i_ptr, encode_value, %%after_encode, base_offset, node_payload_ptr, encode_value, cur_len, retdest
%stack(child_i_ptr, encode_value, after_encode, base_offset, node_payload_ptr, encode_value, cur_len) -> (child_i_ptr, encode_value, cur_len, after_encode, base_offset, node_payload_ptr, encode_value)
// stack: child_i_ptr, encode_value, cur_len, %%after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest
%stack
(child_i_ptr, encode_value, cur_len, after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, cur_len, retdest) ->
(child_i_ptr, encode_value, cur_len, after_encode, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest)
%jump(encode_or_hash_node)
%%after_encode:
// stack: result, result_len, cur_len, base_offset, node_payload_ptr, encode_value, retdest
%stack(result, result_len, cur_len, base_offset, node_payload_ptr, encode_value) -> (result, result_len, base_offset, node_payload_ptr, encode_value, cur_len)
DUP3 %add_const($i) %mstore_kernel(@SEGMENT_TRIE_ENCODED_CHILD)
// stack: result_len, base_offset, node_payload_ptr, encode_value, cur_len, retdest
DUP2 %add_const($i) %mstore_kernel(@SEGMENT_TRIE_ENCODED_CHILD_LEN)
// stack: base_offset, node_payload_ptr, encode_value, cur_len, retdest
%endmacro
// Part of the encode_node_branch function. Appends the i'th child's RLP.
%macro append_child(i)
// stack: rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest
DUP3 %add_const($i) %mload_kernel(@SEGMENT_TRIE_ENCODED_CHILD) // load result
DUP4 %add_const($i) %mload_kernel(@SEGMENT_TRIE_ENCODED_CHILD_LEN) // load result_len
// stack: result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest
// stack: result, result_len, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest
// If result_len != 32, result is raw RLP, with an appropriate RLP prefix already.
DUP1 %sub_const(32) %jumpi(%%unpack)
SWAP1 DUP1 %sub_const(32) %jumpi(%%unpack)
// Otherwise, result is a hash, and we need to add the prefix 0x80 + 32 = 160.
// stack: result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest
PUSH 160
// stack: result_len, result, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest
DUP4 // rlp_pos
%mstore_rlp
SWAP2 %increment SWAP2 // rlp_pos += 1
PUSH 160
MSTORE_GENERAL
SWAP3 %increment SWAP3 // rlp_pos += 1
%%unpack:
%stack (result_len, result, rlp_pos, rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest)
%stack (result_len, result, cur_len, rlp_pos, rlp_start, node_payload_ptr, encode_value, retdest)
-> (rlp_pos, result, result_len, %%after_unpacking,
rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest)
%jump(mstore_unpacking_rlp)
rlp_start, node_payload_ptr, encode_value, cur_len, retdest)
%jump(mstore_unpacking)
%%after_unpacking:
// stack: rlp_pos', rlp_start, base_offset, node_payload_ptr, encode_value, cur_len, retdest
// stack: rlp_pos', rlp_start, node_payload_ptr, encode_value, cur_len, retdest
%endmacro
global encode_node_extension:
@ -277,14 +233,14 @@ encode_node_extension_after_hex_prefix:
// If result_len != 32, result is raw RLP, with an appropriate RLP prefix already.
DUP4 %sub_const(32) %jumpi(encode_node_extension_unpack)
// Otherwise, result is a hash, and we need to add the prefix 0x80 + 32 = 160.
DUP1 // rlp_pos
PUSH 160
DUP2 // rlp_pos
%mstore_rlp
MSTORE_GENERAL
%increment // rlp_pos += 1
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

View File

@ -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
@ -101,6 +101,10 @@ global encode_account:
DUP3 %add_const(2) %mload_trie_data // storage_root_ptr = value[2]
// stack: storage_root_ptr, cur_len, rlp_pos_5, value_ptr, cur_len, retdest
PUSH debug_after_hash_storage_trie
POP
// Hash storage trie.
%mpt_hash_storage_trie
// stack: storage_root_digest, new_len, rlp_pos_5, value_ptr, cur_len, retdest
@ -115,232 +119,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
DUP2 DUP2
%mstore_rlp
// stack: rlp_addr, txn_type, old_rlp_addr, value_ptr, retdest
DUP1 DUP3
MSTORE_GENERAL
%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

View File

@ -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_nibbles, 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
%swap_mstore
%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
%mstore_rlp
// stack: rlp_pos, num_nibbles, packed_nibbles, terminated, retdest
// rlp_pos += 1
DUP2
%swap_mstore
// 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.
DUP2 // rlp_addr
PUSH 0xb8 // value = 0xb7 + len_of_len = 0xb8
DUP3 // offset = rlp_pos
%mstore_rlp
MSTORE_GENERAL
// stack: rlp_addr, value, hp_len, i, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest
// stack: hp_len, rlp_pos, num_nibbles, packed_nibbles, terminated, retdest
DUP2 %increment // offset = rlp_pos + 1
%mstore_rlp
// stack: hp_len, rlp_addr, num_nibbles, packed_nibbles, terminated, retdest
DUP2 %increment
%swap_mstore
// 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)

View File

@ -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

View File

@ -10,6 +10,12 @@
// stack: (empty)
%endmacro
%macro initialize_rlp_segment
PUSH @ENCODED_EMPTY_NODE_POS
PUSH 0x80
MSTORE_GENERAL
%endmacro
%macro alloc_rlp_block
// stack: (empty)
%mload_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE)
@ -17,7 +23,7 @@
// In our model it's fine to use memory in a sparse way, as long as the gaps aren't larger than
// 2^16 or so. So instead of the caller specifying the size of the block they need, we'll just
// allocate 0x10000 = 2^16 bytes, much larger than any RLP blob the EVM could possibly create.
DUP1 %add_const(0x10000)
DUP1 %add_const(@MAX_RLP_BLOB_SIZE)
// stack: block_end, block_start
%mstore_global_metadata(@GLOBAL_METADATA_RLP_DATA_SIZE)
// stack: block_start

View File

@ -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

View File

@ -1,76 +1,73 @@
// 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
DUP1
// stack: len, rlp_addr, string, retdest
DUP2
DUP2
%add_const(0x80)
// stack: first_byte, len, pos, string, retdest
DUP3
// stack: pos, first_byte, len, pos, string, retdest
%mstore_rlp
// stack: len, pos, string, retdest
// stack: first_byte, rlp_addr, len, rlp_addr, string, retdest
MSTORE_GENERAL
// 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
DUP1
// stack: len, rlp_addr, string, retdest
DUP2
DUP2
%add_const(0x81)
// stack: first_byte, len, pos, string, retdest
DUP3
// stack: pos, first_byte, len, pos, string, retdest
%mstore_rlp
// stack: len, pos, string, retdest
DUP1
// stack: first_byte, rlp_addr, len, rlp_addr, string, retdest
MSTORE_GENERAL
// stack: len, rlp_addr, string, retdest
DUP2 %increment
DUP2
%add_const(0x80)
// stack: second_byte, len, original_pos, string, retdest
DUP3 %increment
// stack: pos', second_byte, len, pos, string, retdest
%mstore_rlp
// stack: len, pos, string, retdest
// stack: second_byte, rlp_addr', len, original_rlp_addr, string, retdest
MSTORE_GENERAL
// 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 +75,153 @@ 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
SWAP1 %add_const(0x80)
// stack: prefix, pos, retdest
DUP2
// stack: pos, prefix, pos, retdest
%mstore_rlp
// stack: pos, retdest
// stack: rlp_addr, str_len, retdest
DUP1
SWAP2 %add_const(0x80)
// stack: prefix, rlp_addr, rlp_addr, retdest
MSTORE_GENERAL
// 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
DUP1 // rlp_addr
DUP3 // len_of_len
%add_const(0xb7)
// stack: first_byte, pos, len_of_len, str_len, retdest
DUP2
// stack: pos, first_byte, pos, len_of_len, str_len, retdest
%mstore_rlp
// stack: pos, len_of_len, str_len, retdest
// stack: first_byte, rlp_addr, rlp_addr, len_of_len, str_len, retdest
MSTORE_GENERAL
// 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
SWAP1
// stack: rlp_addr, payload_len, retdest
DUP1
SWAP2
%add_const(0xc0)
// stack: prefix, pos, retdest
DUP2
// stack: pos, prefix, pos, retdest
%mstore_rlp
// stack: pos, retdest
// stack: prefix, rlp_addr, rlp_addr, retdest
MSTORE_GENERAL
// 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
DUP1 %add_const(0xf7)
// stack: first_byte, len_of_len, pos, payload_len, retdest
DUP3 // pos
%mstore_rlp
// stack: len_of_len, pos, payload_len, retdest
// stack: len_of_len, rlp_addr, payload_len, retdest
DUP2
DUP2 %add_const(0xf7)
// stack: first_byte, rlp_addr, len_of_len, rlp_addr, payload_len, retdest
MSTORE_GENERAL
// 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
DUP1 %add_const(0xc0)
// stack: prefix_byte, payload_len, end_pos, start_pos, retdest
DUP4 %decrement // offset of prefix
%mstore_rlp
// 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
DUP3 %decrement // offset of prefix
DUP2 %add_const(0xc0)
// stack: prefix_byte, start_rlp_addr-1, payload_len, end_rlp_addr, start_rlp_addr, retdest
MSTORE_GENERAL
// 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 %swap_mstore // 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 +268,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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -1,97 +1,105 @@
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,
/// The sizes of the `TrieEncodedChild` and `TrieEncodedChildLen` buffers. In other words, the
/// next available offset in these buffers.
TrieEncodedChildSize = 13,
StateTrieRootDigestAfter,
TransactionTrieRootDigestAfter,
ReceiptTrieRootDigestAfter,
// 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;
pub(crate) const COUNT: usize = 47;
/// 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] {
[
@ -108,7 +116,6 @@ impl GlobalMetadata {
Self::StateTrieRootDigestAfter,
Self::TransactionTrieRootDigestAfter,
Self::ReceiptTrieRootDigestAfter,
Self::TrieEncodedChildSize,
Self::BlockBeneficiary,
Self::BlockTimestamp,
Self::BlockNumber,
@ -162,7 +169,6 @@ impl GlobalMetadata {
Self::StateTrieRootDigestAfter => "GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER",
Self::TransactionTrieRootDigestAfter => "GLOBAL_METADATA_TXN_TRIE_DIGEST_AFTER",
Self::ReceiptTrieRootDigestAfter => "GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_AFTER",
Self::TrieEncodedChildSize => "GLOBAL_METADATA_TRIE_ENCODED_CHILD_SIZE",
Self::BlockBeneficiary => "GLOBAL_METADATA_BLOCK_BENEFICIARY",
Self::BlockTimestamp => "GLOBAL_METADATA_BLOCK_TIMESTAMP",
Self::BlockNumber => "GLOBAL_METADATA_BLOCK_NUMBER",

View File

@ -2,6 +2,7 @@ use std::collections::HashMap;
use ethereum_types::U256;
use hex_literal::hex;
use static_assertions::const_assert;
use crate::cpu::kernel::constants::context_metadata::ContextMetadata;
use crate::cpu::kernel::constants::global_metadata::GlobalMetadata;
@ -58,16 +59,19 @@ pub(crate) fn evm_constants() -> HashMap<String, U256> {
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());
@ -86,12 +90,23 @@ pub(crate) fn evm_constants() -> HashMap<String, U256> {
c
}
const MISC_CONSTANTS: [(&str, [u8; 32]); 1] = [
const MISC_CONSTANTS: [(&str, [u8; 32]); 3] = [
// Base for limbs used in bignum arithmetic.
(
"BIGNUM_LIMB_BASE",
hex!("0000000000000000000000000000000100000000000000000000000000000000"),
),
// Position in SEGMENT_RLP_RAW where the empty node encoding is stored. It is
// equal to u32::MAX + @SEGMENT_RLP_RAW so that all rlp pointers are much smaller than that.
(
"ENCODED_EMPTY_NODE_POS",
hex!("0000000000000000000000000000000000000000000000000000000CFFFFFFFF"),
),
// 0x10000 = 2^16 bytes, much larger than any RLP blob the EVM could possibly create.
(
"MAX_RLP_BLOB_SIZE",
hex!("0000000000000000000000000000000000000000000000000000000000010000"),
),
];
const HASH_CONSTANTS: [(&str, [u8; 32]); 2] = [

View File

@ -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,

View File

@ -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;
@ -140,12 +140,14 @@ enum InterpreterMemOpKind {
impl<'a> Interpreter<'a> {
pub(crate) fn new_with_kernel(initial_offset: usize, initial_stack: Vec<U256>) -> Self {
Self::new(
let mut result = Self::new(
&KERNEL.code,
initial_offset,
initial_stack,
&KERNEL.prover_inputs,
)
);
result.initialize_rlp_segment();
result
}
pub(crate) fn new(
@ -199,19 +201,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 +270,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 +292,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<u8> {
@ -301,52 +304,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<U256> {
&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<U256> {
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<u8> {
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 +382,9 @@ impl<'a> Interpreter<'a> {
pub(crate) fn get_current_general_memory(&self) -> Vec<U256> {
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<U256> {
@ -370,16 +397,16 @@ impl<'a> Interpreter<'a> {
pub(crate) fn set_current_general_memory(&mut self, memory: Vec<U256>) {
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<U256>) {
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<u8>) {
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 +422,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 +433,7 @@ impl<'a> Interpreter<'a> {
}
pub(crate) fn get_jumpdest_bits(&self, context: usize) -> Vec<bool> {
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 +448,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 +470,7 @@ impl<'a> Interpreter<'a> {
}
fn stack_segment_mut(&mut self) -> &mut Vec<U256> {
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 +669,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 +855,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 +1010,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 +1019,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 +1039,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 +1049,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 +1060,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 +1080,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 +1091,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 +1110,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> {
@ -1171,6 +1195,14 @@ impl<'a> Interpreter<'a> {
}
self.generation_state.registers.context = context;
}
/// Writes the encoding of 0 to position @ENCODED_EMPTY_NODE_POS.
pub(crate) fn initialize_rlp_segment(&mut self) {
self.generation_state.memory.set(
MemoryAddress::new(0, Segment::RlpRaw, 0xFFFFFFFF),
128.into(),
)
}
}
// Computes the two's complement of the given integer.
@ -1447,14 +1479,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 +1537,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 +1548,7 @@ mod tests {
MemoryAddress::new(
1,
Segment::ContextMetadata,
ContextMetadata::ParentProgramCounter as usize,
ContextMetadata::ParentProgramCounter.unscale(),
),
0xdeadbeefu32.into(),
);
@ -1509,9 +1556,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 +1569,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()
);

View File

@ -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::<u8>()));
[Segment::MainMemory.unscale()]
.set(i, U256::from(rng.gen::<u8>()));
interpreter.generation_state.memory.contexts[context].segments
[Segment::KernelAccountCode as usize]
.set(i, U256::from(rng.gen::<u8>()));
[Segment::KernelAccountCode.unscale()]
.set(i, U256::from(rng.gen::<u8>()));
}
// 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(())

View File

@ -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()

View File

@ -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!(

View File

@ -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()?;

View File

@ -1,7 +1,8 @@
use anyhow::{anyhow, Result};
use eth_trie_utils::nibbles::Nibbles;
use eth_trie_utils::partial_trie::{HashedPartialTrie, PartialTrie};
use ethereum_types::{BigEndianHash, H256};
use ethereum_types::{BigEndianHash, H256, U512};
use rand::random;
use crate::cpu::kernel::aggregator::KERNEL;
use crate::cpu::kernel::constants::global_metadata::GlobalMetadata;
@ -48,6 +49,32 @@ fn mpt_delete_branch_into_hash() -> Result<()> {
test_state_trie(state_trie, nibbles_64(0xADE), test_account_2())
}
#[test]
fn test_after_mpt_delete_extension_branch() -> Result<()> {
let hash = Node::Hash(H256::random());
let branch = Node::Branch {
children: std::array::from_fn(|i| {
if i == 0 {
Node::Empty.into()
} else {
hash.clone().into()
}
}),
value: vec![],
};
let nibbles = Nibbles::from_bytes_be(&random::<[u8; 5]>()).unwrap();
let state_trie = Node::Extension {
nibbles,
child: branch.into(),
}
.into();
let key = nibbles.merge_nibbles(&Nibbles {
packed: U512::zero(),
count: 64 - nibbles.count,
});
test_state_trie(state_trie, key, test_account_2())
}
/// Note: The account's storage_root is ignored, as we can't insert a new storage_root without the
/// accompanying trie data. An empty trie's storage_root is used instead.
fn test_state_trie(

View File

@ -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(),

View File

@ -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()]

View File

@ -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(())

View File

@ -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]

View File

@ -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<T: Copy>(lv: &CpuColumnsView<T>) -> (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<T: Copy>(lv: &CpuColumnsView<T>) -> (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<P: PackedField>(
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<P: PackedField>(
}
// 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<F: RichField + Extendable<D>, 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<F: RichField + Extendable<D>, 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<P: PackedField>(
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<P: PackedField>(
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<P: PackedField>(
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<F: RichField + Extendable<D>, 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<F: RichField + Extendable<D>, 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<F: RichField + Extendable<D>, 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<F: RichField + Extendable<D>, 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<F: RichField + Extendable<D>, 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<F: RichField + Extendable<D>, 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,

View File

@ -24,7 +24,7 @@ pub(crate) fn eval_packed<P: PackedField>(
// 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<F: RichField + Extendable<D>, 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

View File

@ -83,13 +83,13 @@ pub(crate) const JUMPI_OP: Option<StackBehavior> = Some(StackBehavior {
});
/// `StackBehavior` for MLOAD_GENERAL.
pub(crate) const MLOAD_GENERAL_OP: Option<StackBehavior> = 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<Option<StackBehavior>> = 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<P: PackedField>(
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<P: PackedField>(
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<P: PackedField>(
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<P: PackedField>(
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<F: RichField + Extendable<D>, 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<F: RichField + Extendable<D>, 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<F: RichField + Extendable<D>, 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<F: RichField + Extendable<D>, 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);

View File

@ -45,7 +45,7 @@ pub(crate) fn eval_packed<P: PackedField>(
}
// 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<F: RichField + Extendable<D>, 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

View File

@ -1068,7 +1068,7 @@ where
/// (Prod_{i=1}^11 x_i) / phi
/// The 6th Frob map is nontrivial but leaves Fp6 fixed and hence must be the conjugate:
/// x_6 = (a + bz)_6 = a - bz = x.conj()
/// Letting prod_17 = x_1 * x_7, the remaining factors in the numerator can be expresed as:
/// Letting prod_17 = x_1 * x_7, the remaining factors in the numerator can be expressed as:
/// [(prod_17) * (prod_17)_2] * (prod_17)_4 * [(prod_17) * (prod_17)_2]_1
/// By Galois theory, both the following are in Fp2 and are complex conjugates
/// prod_odds, prod_evens

View File

@ -69,8 +69,10 @@ where
{
/// The EVM root circuit, which aggregates the (shrunk) per-table recursive proofs.
pub root: RootCircuitData<F, C, D>,
/// The aggregation circuit, which verifies two proofs that can either be root or
/// aggregation proofs.
pub aggregation: AggregationCircuitData<F, C, D>,
/// The block circuit, which verifies an aggregation root proof and a previous block proof.
/// The block circuit, which verifies an aggregation root proof and an optional previous block proof.
pub block: BlockCircuitData<F, C, D>,
/// Holds chains of circuits for each table and for each initial `degree_bits`.
pub by_table: [RecursiveCircuitsForTable<F, C, D>; NUM_TABLES],
@ -236,6 +238,8 @@ impl<const D: usize> AggregationChildTarget<D> {
}
}
/// Data for the block circuit, which is used to generate a final block proof,
/// and compress it with an optional parent proof if present.
#[derive(Eq, PartialEq, Debug)]
pub struct BlockCircuitData<F, C, const D: usize>
where
@ -298,6 +302,16 @@ where
C: GenericConfig<D, F = F> + 'static,
C::Hasher: AlgebraicHasher<F>,
{
/// Serializes all these preprocessed circuits into a sequence of bytes.
///
/// # Arguments
///
/// - `skip_tables`: a boolean indicating whether to serialize only the upper circuits
/// or the entire prover state, including recursive circuits to shrink STARK proofs.
/// - `gate_serializer`: a custom gate serializer needed to serialize recursive circuits
/// common data.
/// - `generator_serializer`: a custom generator serializer needed to serialize recursive
/// circuits proving data.
pub fn to_bytes(
&self,
skip_tables: bool,
@ -320,6 +334,17 @@ where
Ok(buffer)
}
/// Deserializes a sequence of bytes into an entire prover state containing all recursive circuits.
///
/// # Arguments
///
/// - `bytes`: a slice of bytes to deserialize this prover state from.
/// - `skip_tables`: a boolean indicating whether to deserialize only the upper circuits
/// or the entire prover state, including recursive circuits to shrink STARK proofs.
/// - `gate_serializer`: a custom gate serializer needed to serialize recursive circuits
/// common data.
/// - `generator_serializer`: a custom generator serializer needed to serialize recursive
/// circuits proving data.
pub fn from_bytes(
bytes: &[u8],
skip_tables: bool,
@ -373,6 +398,19 @@ where
}
/// Preprocess all recursive circuits used by the system.
///
/// # Arguments
///
/// - `all_stark`: a structure defining the logic of all STARK modules and their associated
/// cross-table lookups.
/// - `degree_bits_ranges`: the logarithmic ranges to be supported for the recursive tables.
/// Transactions may yield arbitrary trace lengths for each STARK module (within some bounds),
/// unknown prior generating the witness to create a proof. Thus, for each STARK module, we
/// construct a map from `2^{degree_bits} = length` to a chain of shrinking recursion circuits,
/// starting from that length, for each `degree_bits` in the range specified for this STARK module.
/// Specifying a wide enough range allows a prover to cover all possible scenarios.
/// - `stark_config`: the configuration to be used for the STARK prover. It will usually be a fast
/// one yielding large proofs.
pub fn new(
all_stark: &AllStark<F, D>,
degree_bits_ranges: &[Range<usize>; NUM_TABLES],
@ -450,6 +488,19 @@ where
/// Outputs the `VerifierCircuitData` needed to verify any block proof
/// generated by an honest prover.
/// While the [`AllRecursiveCircuits`] prover state can also verify proofs, verifiers
/// only need a fraction of the state to verify proofs. This allows much less powerful
/// entities to behave as verifiers, by only loading the necessary data to verify block proofs.
///
/// # Usage
///
/// ```ignore
/// let prover_state = AllRecursiveCircuits { ... };
/// let verifier_state = prover_state.final_verifier_data();
///
/// // Verify a provided block proof
/// assert!(verifier_state.verify(&block_proof).is_ok());
/// ```
pub fn final_verifier_data(&self) -> VerifierCircuitData<F, C, D> {
self.block.circuit.verifier_data()
}
@ -912,7 +963,29 @@ where
}
}
/// Create a proof for each STARK, then combine them, eventually culminating in a root proof.
/// For a given transaction payload passed as [`GenerationInputs`], create a proof
/// for each STARK module, then recursively shrink and combine them, eventually
/// culminating in a transaction proof, also called root proof.
///
/// # Arguments
///
/// - `all_stark`: a structure defining the logic of all STARK modules and their associated
/// cross-table lookups.
/// - `config`: the configuration to be used for the STARK prover. It will usually be a fast
/// one yielding large proofs.
/// - `generation_inputs`: a transaction and auxiliary data needed to generate a proof, provided
/// in Intermediary Representation.
/// - `timing`: a profiler defining a scope hierarchy and the time consumed by each one.
/// - `abort_signal`: an optional [`AtomicBool`] wrapped behind an [`Arc`], to send a kill signal
/// early. This is only necessary in a distributed setting where a worker may be blocking the entire
/// queue.
///
/// # Outputs
///
/// This method outputs a tuple of [`ProofWithPublicInputs<F, C, D>`] and its [`PublicValues`]. Only
/// the proof with public inputs is necessary for a verifier to assert correctness of the computation,
/// but the public values are output for the prover convenience, as these are necessary during proof
/// aggregation.
pub fn prove_root(
&self,
all_stark: &AllStark<F, D>,
@ -981,6 +1054,50 @@ where
/// From an initial set of STARK proofs passed with their associated recursive table circuits,
/// generate a recursive transaction proof.
/// It is aimed at being used when preprocessed table circuits have not been loaded to memory.
///
/// **Note**:
/// The type of the `table_circuits` passed as arguments is
/// `&[(RecursiveCircuitsForTableSize<F, C, D>, u8); NUM_TABLES]`. In particular, for each STARK
/// proof contained within the `AllProof` object provided to this method, we need to pass a tuple
/// of [`RecursiveCircuitsForTableSize<F, C, D>`] and a [`u8`]. The former is the recursive chain
/// corresponding to the initial degree size of the associated STARK proof. The latter is the
/// index of this degree in the range that was originally passed when constructing the entire prover
/// state.
///
/// # Usage
///
/// ```ignore
/// // Load a prover state without its recursive table circuits.
/// let gate_serializer = DefaultGateSerializer;
/// let generator_serializer = DefaultGeneratorSerializer::<C, D>::new();
/// let initial_ranges = [16..25, 10..20, 12..25, 14..25, 9..20, 12..20, 17..30];
/// let prover_state = AllRecursiveCircuits::<F, C, D>::new(
/// &all_stark,
/// &initial_ranges,
/// &config,
/// );
///
/// // Generate a proof from the provided inputs.
/// let stark_proof = prove::<F, C, D>(&all_stark, &config, inputs, &mut timing, abort_signal).unwrap();
///
/// // Read the degrees of the internal STARK proofs.
/// // Indices to be passed along the recursive tables
/// // can be easily recovered as `initial_ranges[i]` - `degrees[i]`.
/// let degrees = proof.degree_bits(&config);
///
/// // Retrieve the corresponding recursive table circuits for each table with the corresponding degree.
/// let table_circuits = { ... };
///
/// // Finally shrink the STARK proof.
/// let (proof, public_values) = prove_root_after_initial_stark(
/// &all_stark,
/// &config,
/// &stark_proof,
/// &table_circuits,
/// &mut timing,
/// abort_signal,
/// ).unwrap();
/// ```
pub fn prove_root_after_initial_stark(
&self,
all_stark: &AllStark<F, D>,
@ -1031,6 +1148,31 @@ where
self.root.circuit.verify(agg_proof)
}
/// Create an aggregation proof, combining two contiguous proofs into a single one. The combined
/// proofs can either be transaction (aka root) proofs, or other aggregation proofs, as long as
/// their states are contiguous, meaning that the final state of the left child proof is the initial
/// state of the right child proof.
///
/// While regular transaction proofs can only assert validity of a single transaction, aggregation
/// proofs can cover an arbitrary range, up to an entire block with all its transactions.
///
/// # Arguments
///
/// - `lhs_is_agg`: a boolean indicating whether the left child proof is an aggregation proof or
/// a regular transaction proof.
/// - `lhs_proof`: the left child proof.
/// - `lhs_public_values`: the public values associated to the right child proof.
/// - `rhs_is_agg`: a boolean indicating whether the right child proof is an aggregation proof or
/// a regular transaction proof.
/// - `rhs_proof`: the right child proof.
/// - `rhs_public_values`: the public values associated to the right child proof.
///
/// # Outputs
///
/// This method outputs a tuple of [`ProofWithPublicInputs<F, C, D>`] and its [`PublicValues`]. Only
/// the proof with public inputs is necessary for a verifier to assert correctness of the computation,
/// but the public values are output for the prover convenience, as these are necessary during proof
/// aggregation.
pub fn prove_aggregation(
&self,
lhs_is_agg: bool,
@ -1097,6 +1239,23 @@ where
)
}
/// Create a final block proof, once all transactions of a given block have been combined into a
/// single aggregation proof.
///
/// Block proofs can either be generated as standalone, or combined with a previous block proof
/// to assert validity of a range of blocks.
///
/// # Arguments
///
/// - `opt_parent_block_proof`: an optional parent block proof. Passing one will generate a proof of
/// validity for both the block range covered by the previous proof and the current block.
/// - `agg_root_proof`: the final aggregation proof containing all transactions within the current block.
/// - `public_values`: the public values associated to the aggregation proof.
///
/// # Outputs
///
/// This method outputs a tuple of [`ProofWithPublicInputs<F, C, D>`] and its [`PublicValues`]. Only
/// the proof with public inputs is necessary for a verifier to assert correctness of the computation.
pub fn prove_block(
&self,
opt_parent_block_proof: Option<&ProofWithPublicInputs<F, C, D>>,
@ -1245,6 +1404,7 @@ where
}
}
/// A map between initial degree sizes and their associated shrinking recursion circuits.
#[derive(Eq, PartialEq, Debug)]
pub struct RecursiveCircuitsForTable<F, C, const D: usize>
where

View File

@ -43,13 +43,17 @@ use crate::witness::util::mem_write_log;
/// Inputs needed for trace generation.
#[derive(Clone, Debug, Deserialize, Serialize, Default)]
pub struct GenerationInputs {
/// The index of the transaction being proven within its block.
pub txn_number_before: U256,
/// The cumulative gas used through the execution of all transactions prior the current one.
pub gas_used_before: U256,
/// The cumulative gas used after the execution of the current transaction. The exact gas used
/// by the current transaction is `gas_used_after` - `gas_used_before`.
pub gas_used_after: U256,
// A None would yield an empty proof, otherwise this contains the encoding of a transaction.
/// A None would yield an empty proof, otherwise this contains the encoding of a transaction.
pub signed_txn: Option<Vec<u8>>,
// Withdrawal pairs `(addr, amount)`. At the end of the txs, `amount` is added to `addr`'s balance. See EIP-4895.
/// Withdrawal pairs `(addr, amount)`. At the end of the txs, `amount` is added to `addr`'s balance. See EIP-4895.
pub withdrawals: Vec<(Address, U256)>,
pub tries: TrieInputs,
/// Expected trie roots after the transactions are executed.
@ -64,8 +68,10 @@ pub struct GenerationInputs {
/// All account smart contracts that are invoked will have an entry present.
pub contract_code: HashMap<H256, Vec<u8>>,
/// Information contained in the block header.
pub block_metadata: BlockMetadata,
/// The hash of the current block, and a list of the 256 previous block hashes.
pub block_hashes: BlockHashes,
}
@ -155,7 +161,8 @@ fn apply_metadata_and_tries_memops<F: RichField + Extendable<D>, 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,
)

View File

@ -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<F: Field> GenerationState<F> {
fn run_account_code(&mut self) -> Result<U256, ProgramError> {
// 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<F: Field> GenerationState<F> {
m_start_loc: usize,
) -> (Vec<U256>, Vec<U256>) {
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);

View File

@ -57,7 +57,7 @@ impl<F: Field> GenerationState<F> {
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<F: Field> GenerationState<F> {
}
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)

View File

@ -58,7 +58,7 @@ pub(crate) fn read_trie_helper<V>(
) -> 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))?];

View File

@ -32,7 +32,7 @@ pub(crate) struct KeccakSpongeColumnsView<T: Copy> {
/// not a padding byte; 0 otherwise.
pub is_full_input_block: T,
/// The context of the base addresss at which we will read the input block.
/// The context of the base address at which we will read the input block.
pub context: T,
/// The segment of the base address at which we will read the input block.
pub segment: T,

View File

@ -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,
};

View File

@ -1,3 +1,164 @@
//! An implementation of a Type 1 zk-EVM by Polygon Zero.
//!
//! Following the [zk-EVM classification of V. Buterin](https://vitalik.eth.limo/general/2022/08/04/zkevm.html),
//! the plonky2_evm crate aims at providing an efficient solution for the problem of generating cryptographic
//! proofs of Ethereum-like transactions with *full Ethereum capability*.
//!
//! To this end, the plonky2 zk-EVM is tailored for an AIR-based STARK system satisfying degree 3 constraints,
//! with support for recursive aggregation leveraging plonky2 circuits with FRI-based plonkish arithmetization.
//! These circuits require a one-time, offline preprocessing phase.
//! See the [`fixed_recursive_verifier`] module for more details on how this works.
//! These preprocessed circuits are gathered within the [`AllRecursiveCircuits`] prover state,
//! and can be generated as such:
//!
//! ```ignore
//! // Specify the base field to use.
//! type F = GoldilocksField;
//! // Specify the extension degree to use.
//! const D: usize = 2;
//! // Specify the recursive configuration to use, here leveraging Poseidon hash
//! // over the Goldilocks field both natively and in-circuit.
//! type C = PoseidonGoldilocksConfig;
//!
//! let all_stark = AllStark::<F, D>::default();
//! let config = StarkConfig::standard_fast_config();
//!
//! // Generate all the recursive circuits needed to generate succinct proofs for blocks.
//! // The ranges correspond to the supported table sizes for each individual STARK component.
//! let prover_state = AllRecursiveCircuits::<F, C, D>::new(
//! &all_stark,
//! &[16..25, 10..20, 12..25, 14..25, 9..20, 12..20, 17..30],
//! &config,
//! );
//! ```
//!
//! # Inputs type
//!
//! Transactions need to be processed into an Intermediary Representation (IR) format for the prover
//! to be able to generate proofs of valid state transition. This involves passing the encoded transaction,
//! the header of the block in which it was included, some information on the state prior execution
//! of this transaction, etc.
//! This intermediary representation is called [`GenerationInputs`].
//!
//!
//! # Generating succinct proofs
//!
//! ## Transaction proofs
//!
//! To generate a proof for a transaction, given its [`GenerationInputs`] and an [`AllRecursiveCircuits`]
//! prover state, one can simply call the [prove_root](AllRecursiveCircuits::prove_root) method.
//!
//! ```ignore
//! let mut timing = TimingTree::new("prove", log::Level::Debug);
//! let kill_signal = None; // Useful only with distributed proving to kill hanging jobs.
//! let (proof, public_values) =
//! prover_state.prove_root(all_stark, config, inputs, &mut timing, kill_signal);
//! ```
//!
//! This outputs a transaction proof and its associated public values. These are necessary during the
//! aggregation levels (see below). If one were to miss the public values, they are also retrievable directly
//! from the proof's encoded public inputs, as such:
//!
//! ```ignore
//! let public_values = PublicValues::from_public_inputs(&proof.public_inputs);
//! ```
//!
//! ## Aggregation proofs
//!
//! Because the plonky2 zkEVM generates proofs on a transaction basis, we then need to aggregate them for succinct
//! verification. This is done in a binary tree fashion, where each inner node proof verifies two children proofs,
//! through the [prove_aggregation](AllRecursiveCircuits::prove_aggregation) method.
//! Note that the tree does *not* need to be complete, as this aggregation process can take as inputs both regular
//! transaction proofs and aggregation proofs. We only need to specify for each child if it is an aggregation proof
//! or a regular one.
//!
//! ```ignore
//! let (proof_1, pv_1) =
//! prover_state.prove_root(all_stark, config, inputs_1, &mut timing, None);
//! let (proof_2, pv_2) =
//! prover_state.prove_root(all_stark, config, inputs_2, &mut timing, None);
//! let (proof_3, pv_3) =
//! prover_state.prove_root(all_stark, config, inputs_3, &mut timing, None);
//!
//! // Now aggregate proofs for txn 1 and 2.
//! let (agg_proof_1_2, pv_1_2) =
//! prover_state.prove_aggregation(false, proof_1, pv_1, false, proof_2, pv_2);
//!
//! // Now aggregate the newly generated aggregation proof with the last regular txn proof.
//! let (agg_proof_1_3, pv_1_3) =
//! prover_state.prove_aggregation(true, agg_proof_1_2, pv_1_2, false, proof_3, pv_3);
//! ```
//!
//! **Note**: The proofs provided to the [prove_aggregation](AllRecursiveCircuits::prove_aggregation) method *MUST* have contiguous states.
//! Trying to combine `proof_1` and `proof_3` from the example above would fail.
//!
//! ## Block proofs
//!
//! Once all transactions of a block have been proven and we are left with a single aggregation proof and its public values,
//! we can then wrap it into a final block proof, attesting validity of the entire block.
//! This [prove_block](AllRecursiveCircuits::prove_block) method accepts an optional previous block proof as argument,
//! which will then try combining the previously proven block with the current one, generating a validity proof for both.
//! Applying this process from genesis would yield a single proof attesting correctness of the entire chain.
//!
//! ```ignore
//! let previous_block_proof = { ... };
//! let (block_proof, block_public_values) =
//! prover_state.prove_block(Some(&previous_block_proof), &agg_proof, agg_pv)?;
//! ```
//!
//! ### Checkpoint heights
//!
//! The process of always providing a previous block proof when generating a proof for the current block may yield some
//! undesirable issues. For this reason, the plonky2 zk-EVM supports checkpoint heights. At given block heights,
//! the prover does not have to pass a previous block proof. This would in practice correspond to block heights at which
//! a proof has been generated and sent to L1 for settlement.
//!
//! The only requirement when generating a block proof without passing a previous one as argument is to have the
//! `checkpoint_state_trie_root` metadata in the `PublicValues` of the final aggregation proof be matching the state
//! trie before applying all the included transactions. If this condition is not met, the prover will fail to generate
//! a valid proof.
//!
//!
//! ```ignore
//! let (block_proof, block_public_values) =
//! prover_state.prove_block(None, &agg_proof, agg_pv)?;
//! ```
//!
//! # Prover state serialization
//!
//! Because the recursive circuits only need to be generated once, they can be saved to disk once the preprocessing phase
//! completed successfully, and deserialized on-demand.
//! The plonky2 zk-EVM provides serialization methods to convert the entire prover state to a vector of bytes, and vice-versa.
//! This requires the use of custom serializers for gates and generators for proper recursive circuit encoding. This crate provides
//! default serializers supporting all custom gates and associated generators defined within the [`plonky2`] crate.
//!
//! ```ignore
//! let prover_state = AllRecursiveCircuits::<F, C, D>::new(...);
//!
//! // Default serializers
//! let gate_serializer = DefaultGateSerializer;
//! let generator_serializer = DefaultGeneratorSerializer::<C, D> {
//! _phantom: PhantomData::<C>,
//! };
//!
//! // Serialize the prover state to a sequence of bytes
//! let bytes = prover_state.to_bytes(false, &gate_serializer, &generator_serializer).unwrap();
//!
//! // Deserialize the bytes into a prover state
//! let recovered_prover_state = AllRecursiveCircuits::<F, C, D>::from_bytes(
//! &all_circuits_bytes,
//! false,
//! &gate_serializer,
//! &generator_serializer,
//! ).unwrap();
//!
//! assert_eq!(prover_state, recovered_prover_state);
//! ```
//!
//! Note that an entire prover state built with wide ranges may be particularly large (up to ~25 GB), hence serialization methods,
//! while faster than doing another preprocessing, may take some non-negligible time.
#![cfg_attr(docsrs, feature(doc_cfg))]
#![allow(clippy::needless_range_loop)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::field_reassign_with_default)]
@ -43,4 +204,11 @@ use jemallocator::Jemalloc;
#[global_allocator]
static GLOBAL: Jemalloc = Jemalloc;
// Public definitions and re-exports
pub type Node = eth_trie_utils::partial_trie::Node<HashedPartialTrie>;
pub use all_stark::AllStark;
pub use config::StarkConfig;
pub use fixed_recursive_verifier::AllRecursiveCircuits;
pub use generation::GenerationInputs;

View File

@ -305,6 +305,10 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for MemoryStark<F
let filter = local_values[FILTER];
yield_constr.constraint(filter * (filter - P::ONES));
// IS_READ must be 0 or 1.
// This is implied by the MemoryStark CTL, where corresponding values are either
// hardcoded to 0/1, or boolean-constrained in their respective STARK modules.
// If this is a dummy row (filter is off), it must be a read. This means the prover can
// insert reads which never appear in the CPU trace (which are harmless), but not writes.
let is_dummy = P::ONES - filter;
@ -368,7 +372,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for MemoryStark<F
// specified ones (segment 0 is already included in initialize_aux).
// There is overlap with the previous constraint, but this is not a problem.
yield_constr.constraint_transition(
(next_addr_segment - P::Scalar::from_canonical_usize(Segment::TrieData as usize))
(next_addr_segment - P::Scalar::from_canonical_usize(Segment::TrieData.unscale()))
* initialize_aux
* next_values_limbs[i],
);
@ -411,6 +415,10 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for MemoryStark<F
let constraint = builder.mul_sub_extension(filter, filter, filter);
yield_constr.constraint(builder, constraint);
// IS_READ must be 0 or 1.
// This is implied by the MemoryStark CTL, where corresponding values are either
// hardcoded to 0/1, or boolean-constrained in their respective STARK modules.
// If this is a dummy row (filter is off), it must be a read. This means the prover can
// insert reads which never appear in the CPU trace (which are harmless), but not writes.
let is_dummy = builder.sub_extension(one, filter);
@ -524,7 +532,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for MemoryStark<F
// There is overlap with the previous constraint, but this is not a problem.
let segment_trie_data = builder.add_const_extension(
next_addr_segment,
F::NEG_ONE * F::from_canonical_u32(Segment::TrieData as u32),
F::NEG_ONE * F::from_canonical_usize(Segment::TrieData.unscale()),
);
let zero_init_constraint =
builder.mul_extension(segment_trie_data, context_zero_initializing_constraint);

View File

@ -1,74 +1,84 @@
use ethereum_types::U256;
use num::traits::AsPrimitive;
pub(crate) const SEGMENT_SCALING_FACTOR: usize = 32;
/// This contains all the existing memory segments. The values in the enum are shifted by 32 bits
/// to allow for convenient address components (context / segment / virtual) bundling in the kernel.
#[allow(dead_code)]
#[allow(clippy::enum_clike_unportable_variant)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug)]
pub(crate) enum Segment {
/// Contains EVM bytecode.
// The Kernel has optimizations relying on the Code segment being 0.
// This shouldn't be changed!
Code = 0,
/// The program stack.
Stack = 1,
Stack = 1 << SEGMENT_SCALING_FACTOR,
/// Main memory, owned by the contract code.
MainMemory = 2,
MainMemory = 2 << SEGMENT_SCALING_FACTOR,
/// Data passed to the current context by its caller.
Calldata = 3,
Calldata = 3 << SEGMENT_SCALING_FACTOR,
/// Data returned to the current context by its latest callee.
Returndata = 4,
Returndata = 4 << SEGMENT_SCALING_FACTOR,
/// A segment which contains a few fixed-size metadata fields, such as the caller's context, or the
/// size of `CALLDATA` and `RETURNDATA`.
GlobalMetadata = 5,
ContextMetadata = 6,
GlobalMetadata = 5 << SEGMENT_SCALING_FACTOR,
ContextMetadata = 6 << SEGMENT_SCALING_FACTOR,
/// General purpose kernel memory, used by various kernel functions.
/// In general, calling a helper function can result in this memory being clobbered.
KernelGeneral = 7,
KernelGeneral = 7 << SEGMENT_SCALING_FACTOR,
/// Another segment for general purpose kernel use.
KernelGeneral2 = 8,
KernelGeneral2 = 8 << SEGMENT_SCALING_FACTOR,
/// Segment to hold account code for opcodes like `CODESIZE, CODECOPY,...`.
KernelAccountCode = 9,
KernelAccountCode = 9 << SEGMENT_SCALING_FACTOR,
/// Contains normalized transaction fields; see `NormalizedTxnField`.
TxnFields = 10,
TxnFields = 10 << SEGMENT_SCALING_FACTOR,
/// Contains the data field of a transaction.
TxnData = 11,
TxnData = 11 << SEGMENT_SCALING_FACTOR,
/// A buffer used to hold raw RLP data.
RlpRaw = 12,
RlpRaw = 12 << SEGMENT_SCALING_FACTOR,
/// Contains all trie data. It is owned by the kernel, so it only lives on context 0.
TrieData = 13,
/// A buffer used to store the encodings of a branch node's children.
TrieEncodedChild = 14,
/// A buffer used to store the lengths of the encodings of a branch node's children.
TrieEncodedChildLen = 15,
/// A table of values 2^i for i=0..255 for use with shift
/// instructions; initialised by `kernel/asm/shift.asm::init_shift_table()`.
ShiftTable = 16,
JumpdestBits = 17,
EcdsaTable = 18,
BnWnafA = 19,
BnWnafB = 20,
BnTableQ = 21,
BnPairing = 22,
TrieData = 13 << SEGMENT_SCALING_FACTOR,
ShiftTable = 14 << SEGMENT_SCALING_FACTOR,
JumpdestBits = 15 << SEGMENT_SCALING_FACTOR,
EcdsaTable = 16 << SEGMENT_SCALING_FACTOR,
BnWnafA = 17 << SEGMENT_SCALING_FACTOR,
BnWnafB = 18 << SEGMENT_SCALING_FACTOR,
BnTableQ = 19 << SEGMENT_SCALING_FACTOR,
BnPairing = 20 << SEGMENT_SCALING_FACTOR,
/// List of addresses that have been accessed in the current transaction.
AccessedAddresses = 23,
AccessedAddresses = 21 << SEGMENT_SCALING_FACTOR,
/// List of storage keys that have been accessed in the current transaction.
AccessedStorageKeys = 24,
AccessedStorageKeys = 22 << SEGMENT_SCALING_FACTOR,
/// List of addresses that have called SELFDESTRUCT in the current transaction.
SelfDestructList = 25,
SelfDestructList = 23 << SEGMENT_SCALING_FACTOR,
/// Contains the bloom filter of a transaction.
TxnBloom = 26,
TxnBloom = 24 << SEGMENT_SCALING_FACTOR,
/// Contains the bloom filter present in the block header.
GlobalBlockBloom = 27,
GlobalBlockBloom = 25 << SEGMENT_SCALING_FACTOR,
/// List of log pointers pointing to the LogsData segment.
Logs = 28,
LogsData = 29,
Logs = 26 << SEGMENT_SCALING_FACTOR,
LogsData = 27 << SEGMENT_SCALING_FACTOR,
/// Journal of state changes. List of pointers to `JournalData`. Length in `GlobalMetadata`.
Journal = 30,
JournalData = 31,
JournalCheckpoints = 32,
Journal = 28 << SEGMENT_SCALING_FACTOR,
JournalData = 29 << SEGMENT_SCALING_FACTOR,
JournalCheckpoints = 30 << SEGMENT_SCALING_FACTOR,
/// List of addresses that have been touched in the current transaction.
TouchedAddresses = 33,
TouchedAddresses = 31 << SEGMENT_SCALING_FACTOR,
/// List of checkpoints for the current context. Length in `ContextMetadata`.
ContextCheckpoints = 34,
ContextCheckpoints = 32 << SEGMENT_SCALING_FACTOR,
/// List of 256 previous block hashes.
BlockHashes = 35,
BlockHashes = 33 << SEGMENT_SCALING_FACTOR,
}
impl Segment {
pub(crate) const COUNT: usize = 36;
pub(crate) const COUNT: usize = 34;
/// Unscales this segment by `SEGMENT_SCALING_FACTOR`.
pub(crate) const fn unscale(&self) -> usize {
*self as usize >> SEGMENT_SCALING_FACTOR
}
pub(crate) const fn all() -> [Self; Self::COUNT] {
[
@ -86,8 +96,6 @@ impl Segment {
Self::TxnData,
Self::RlpRaw,
Self::TrieData,
Self::TrieEncodedChild,
Self::TrieEncodedChildLen,
Self::ShiftTable,
Self::JumpdestBits,
Self::EcdsaTable,
@ -128,8 +136,6 @@ impl Segment {
Segment::TxnData => "SEGMENT_TXN_DATA",
Segment::RlpRaw => "SEGMENT_RLP_RAW",
Segment::TrieData => "SEGMENT_TRIE_DATA",
Segment::TrieEncodedChild => "SEGMENT_TRIE_ENCODED_CHILD",
Segment::TrieEncodedChildLen => "SEGMENT_TRIE_ENCODED_CHILD_LEN",
Segment::ShiftTable => "SEGMENT_SHIFT_TABLE",
Segment::JumpdestBits => "SEGMENT_JUMPDEST_BITS",
Segment::EcdsaTable => "SEGMENT_KERNEL_ECDSA_TABLE",
@ -169,8 +175,6 @@ impl Segment {
Segment::TxnData => 8,
Segment::RlpRaw => 8,
Segment::TrieData => 256,
Segment::TrieEncodedChild => 256,
Segment::TrieEncodedChildLen => 6,
Segment::ShiftTable => 256,
Segment::JumpdestBits => 1,
Segment::EcdsaTable => 256,
@ -193,4 +197,17 @@ impl Segment {
Segment::BlockHashes => 256,
}
}
pub(crate) fn constant(&self, virt: usize) -> Option<U256> {
match self {
Segment::RlpRaw => {
if virt == 0xFFFFFFFF {
Some(U256::from(0x80))
} else {
None
}
}
_ => None,
}
}
}

View File

@ -431,78 +431,94 @@ pub(crate) fn get_memory_extra_looking_sum_circuit<F: RichField + Extendable<D>,
// 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<F: RichField + Extendable<D>,
}
// 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<F: RichField + Extendable<D>,
// 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<F: RichField + Extendable<D>,
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<F: RichField + Extendable<D>,
challenge,
sum,
metadata_segment,
GlobalMetadata::KernelLen as usize,
GlobalMetadata::KernelLen.unscale(),
&[kernel_len_target],
);

View File

@ -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));

View File

@ -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<Self, ProgramError> {
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
}
@ -177,6 +190,11 @@ impl MemoryState {
}
let segment = Segment::all()[address.segment];
if let Some(constant) = Segment::constant(&segment, address.virt) {
return constant;
}
let val = self.contexts[address.context].segments[address.segment].get(address.virt);
assert!(
val.bits() <= segment.bit_range(),
@ -194,6 +212,15 @@ impl MemoryState {
}
let segment = Segment::all()[address.segment];
if let Some(constant) = Segment::constant(&segment, address.virt) {
assert!(
constant == val,
"Attempting to set constant {} to incorrect value",
address.virt
);
return;
}
assert!(
val.bits() <= segment.bit_range(),
"Value {} exceeds {:?} range of {} bits",
@ -204,12 +231,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())
}
}

View File

@ -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<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> 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<F: Field>(
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<F: Field>(
) -> 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<F: Field>(
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<F: Field>(
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<F: Field>(
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<F: Field>(
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<F: Field>(
// 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<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> 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<F: Field>(
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<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> 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<F: Field>(
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<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> 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<F: Field>(
}
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<F: Field>(
state: &mut GenerationState<F>,
mut row: CpuColumnsView<F>,
) -> 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(())
}

View File

@ -299,11 +299,11 @@ fn perform_op<F: Field>(
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::<u64>::try_into(state.memory.get(gas_limit_address));
match gas_limit {
@ -345,14 +345,14 @@ pub(crate) fn fill_stack_fields<F: Field>(
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<F: Field>(state: &mut GenerationState<F>) -> 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);

View File

@ -83,7 +83,7 @@ fn test_empty_txn_list() -> anyhow::Result<()> {
{
let gate_serializer = DefaultGateSerializer;
let generator_serializer = DefaultGeneratorSerializer {
let generator_serializer = DefaultGeneratorSerializer::<C, D> {
_phantom: PhantomData::<C>,
};

View File

@ -442,7 +442,7 @@ fn test_log_with_aggreg() -> anyhow::Result<()> {
// Preprocess all circuits.
let all_circuits = AllRecursiveCircuits::<F, C, D>::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,
);

View File

@ -104,7 +104,7 @@ impl Field for GoldilocksField {
/// Therefore $a^(p-2) = a^-1 (mod p)$
///
/// The following code has been adapted from winterfell/math/src/field/f64/mod.rs
/// located at https://github.com/facebook/winterfell.
/// located at <https://github.com/facebook/winterfell>.
fn try_inverse(&self) -> Option<Self> {
if self.is_zero() {
return None;

View File

@ -18,6 +18,7 @@ mod validate_shape;
pub mod verifier;
pub mod witness_util;
/// A configuration for the FRI protocol.
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
pub struct FriConfig {
/// `rate = 2^{-rate_bits}`.
@ -26,8 +27,10 @@ pub struct FriConfig {
/// Height of Merkle tree caps.
pub cap_height: usize,
/// Number of bits used for grinding.
pub proof_of_work_bits: u32,
/// The reduction strategy to be applied at each layer during the commit phase.
pub reduction_strategy: FriReductionStrategy,
/// Number of query rounds to perform.