mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-10 01:33:07 +00:00
Merge branch 'main' of github.com:mir-protocol/plonky2 into non-inv
This commit is contained in:
commit
2e2007eede
@ -46,6 +46,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 -Cprefer-dynamic=y
|
RUSTFLAGS: -Copt-level=3 -Cdebug-assertions -Coverflow-checks=y -Cdebuginfo=0 -Cprefer-dynamic=y
|
||||||
CARGO_INCREMENTAL: 1
|
CARGO_INCREMENTAL: 1
|
||||||
|
RUST_BACKTRACE: 1
|
||||||
|
|
||||||
lints:
|
lints:
|
||||||
name: Formatting and Clippy
|
name: Formatting and Clippy
|
||||||
|
|||||||
@ -8,7 +8,7 @@ use plonky2::field::types::{Field, PrimeField};
|
|||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
|
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||||
use plonky2::iop::target::{BoolTarget, Target};
|
use plonky2::iop::target::{BoolTarget, Target};
|
||||||
use plonky2::iop::witness::PartitionWitness;
|
use plonky2::iop::witness::{PartitionWitness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
|
|
||||||
use crate::curve::glv::{decompose_secp256k1_scalar, GLV_BETA, GLV_S};
|
use crate::curve::glv::{decompose_secp256k1_scalar, GLV_BETA, GLV_S};
|
||||||
|
|||||||
@ -8,7 +8,7 @@ use plonky2::field::types::{Field, PrimeField};
|
|||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
|
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||||
use plonky2::iop::target::{BoolTarget, Target};
|
use plonky2::iop::target::{BoolTarget, Target};
|
||||||
use plonky2::iop::witness::PartitionWitness;
|
use plonky2::iop::witness::{PartitionWitness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::util::ceil_div_usize;
|
use plonky2::util::ceil_div_usize;
|
||||||
use plonky2_u32::gadgets::arithmetic_u32::{CircuitBuilderU32, U32Target};
|
use plonky2_u32::gadgets::arithmetic_u32::{CircuitBuilderU32, U32Target};
|
||||||
|
|||||||
@ -76,6 +76,18 @@ pub enum Table {
|
|||||||
|
|
||||||
pub(crate) const NUM_TABLES: usize = Table::Memory as usize + 1;
|
pub(crate) const NUM_TABLES: usize = Table::Memory as usize + 1;
|
||||||
|
|
||||||
|
impl Table {
|
||||||
|
pub(crate) fn all() -> [Self; NUM_TABLES] {
|
||||||
|
[
|
||||||
|
Self::Cpu,
|
||||||
|
Self::Keccak,
|
||||||
|
Self::KeccakSponge,
|
||||||
|
Self::Logic,
|
||||||
|
Self::Memory,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn all_cross_table_lookups<F: Field>() -> Vec<CrossTableLookup<F>> {
|
pub(crate) fn all_cross_table_lookups<F: Field>() -> Vec<CrossTableLookup<F>> {
|
||||||
let mut ctls = vec![ctl_keccak(), ctl_logic(), ctl_memory(), ctl_keccak_sponge()];
|
let mut ctls = vec![ctl_keccak(), ctl_logic(), ctl_memory(), ctl_keccak_sponge()];
|
||||||
// TODO: Some CTLs temporarily disabled while we get them working.
|
// TODO: Some CTLs temporarily disabled while we get them working.
|
||||||
|
|||||||
@ -97,46 +97,10 @@ pub(crate) struct CpuLogicView<T: Copy> {
|
|||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub(crate) struct CpuJumpsView<T: Copy> {
|
pub(crate) struct CpuJumpsView<T: Copy> {
|
||||||
/// `input0` is `mem_channel[0].value`. It's the top stack value at entry (for jumps, the
|
// A flag.
|
||||||
/// address; for `EXIT_KERNEL`, the address and new privilege level).
|
|
||||||
/// `input1` is `mem_channel[1].value`. For `JUMPI`, it's the second stack value (the
|
|
||||||
/// predicate). For `JUMP`, 1.
|
|
||||||
|
|
||||||
/// Inverse of `input0[1] + ... + input0[7]`, if one exists; otherwise, an arbitrary value.
|
|
||||||
/// Needed to prove that `input0` is nonzero.
|
|
||||||
pub(crate) input0_upper_sum_inv: T,
|
|
||||||
/// 1 if `input0[1..7]` is zero; else 0.
|
|
||||||
pub(crate) input0_upper_zero: T,
|
|
||||||
|
|
||||||
/// 1 if `input0[0]` is the address of a valid jump destination (i.e. `JUMPDEST` that is not
|
|
||||||
/// part of a `PUSH` immediate); else 0. Note that the kernel is allowed to jump anywhere it
|
|
||||||
/// wants, so this flag is computed but ignored in kernel mode.
|
|
||||||
/// NOTE: this flag only considers `input0[0]`, the low 32 bits of the 256-bit register. Even if
|
|
||||||
/// this flag is 1, `input0` will still be an invalid address if the high 224 bits are not 0.
|
|
||||||
pub(crate) dst_valid: T, // TODO: populate this (check for JUMPDEST)
|
|
||||||
/// 1 if either `dst_valid` is 1 or we are in kernel mode; else 0. (Just a logical OR.)
|
|
||||||
pub(crate) dst_valid_or_kernel: T,
|
|
||||||
/// 1 if `dst_valid_or_kernel` and `input0_upper_zero` are both 1; else 0. In other words, we
|
|
||||||
/// are allowed to jump to `input0[0]` because either it's a valid address or we're in kernel
|
|
||||||
/// mode (`dst_valid_or_kernel`), and also `input0[1..7]` are all 0 so `input0[0]` is in fact
|
|
||||||
/// the whole address (we're not being asked to jump to an address that would overflow).
|
|
||||||
pub(crate) input0_jumpable: T,
|
|
||||||
|
|
||||||
/// Inverse of `input1[0] + ... + input1[7]`, if one exists; otherwise, an arbitrary value.
|
|
||||||
/// Needed to prove that `input1` is nonzero.
|
|
||||||
pub(crate) input1_sum_inv: T,
|
|
||||||
|
|
||||||
/// Note that the below flags are mutually exclusive.
|
|
||||||
/// 1 if the JUMPI falls though (because input1 is 0); else 0.
|
|
||||||
pub(crate) should_continue: T,
|
|
||||||
/// 1 if the JUMP/JUMPI does in fact jump to `input0`; else 0. This requires `input0` to be a
|
|
||||||
/// valid destination (`input0[0]` is a `JUMPDEST` not in an immediate or we are in kernel mode
|
|
||||||
/// and also `input0[1..7]` is 0) and `input1` to be nonzero.
|
|
||||||
pub(crate) should_jump: T,
|
pub(crate) should_jump: T,
|
||||||
/// 1 if the JUMP/JUMPI faults; else 0. This happens when `input0` is not a valid destination
|
// Pseudoinverse of `cond.iter().sum()`. Used to check `should_jump`.
|
||||||
/// (`input0[0]` is not `JUMPDEST` that is not in an immediate while we are in user mode, or
|
pub(crate) cond_sum_pinv: T,
|
||||||
/// `input0[1..7]` is nonzero) and `input1` is nonzero.
|
|
||||||
pub(crate) should_trap: T,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
|||||||
82
evm/src/cpu/contextops.rs
Normal file
82
evm/src/cpu/contextops.rs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
use plonky2::field::extension::Extendable;
|
||||||
|
use plonky2::field::packed::PackedField;
|
||||||
|
use plonky2::hash::hash_types::RichField;
|
||||||
|
use plonky2::iop::ext_target::ExtensionTarget;
|
||||||
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
|
|
||||||
|
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
||||||
|
use crate::cpu::columns::CpuColumnsView;
|
||||||
|
use crate::cpu::membus::NUM_GP_CHANNELS;
|
||||||
|
|
||||||
|
fn eval_packed_get<P: PackedField>(
|
||||||
|
lv: &CpuColumnsView<P>,
|
||||||
|
yield_constr: &mut ConstraintConsumer<P>,
|
||||||
|
) {
|
||||||
|
let filter = lv.op.get_context;
|
||||||
|
let push_channel = lv.mem_channels[NUM_GP_CHANNELS - 1];
|
||||||
|
yield_constr.constraint(filter * (push_channel.value[0] - lv.context));
|
||||||
|
for &limb in &push_channel.value[1..] {
|
||||||
|
yield_constr.constraint(filter * limb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_ext_circuit_get<F: RichField + Extendable<D>, const D: usize>(
|
||||||
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
|
lv: &CpuColumnsView<ExtensionTarget<D>>,
|
||||||
|
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
||||||
|
) {
|
||||||
|
let filter = lv.op.get_context;
|
||||||
|
let push_channel = lv.mem_channels[NUM_GP_CHANNELS - 1];
|
||||||
|
{
|
||||||
|
let diff = builder.sub_extension(push_channel.value[0], lv.context);
|
||||||
|
let constr = builder.mul_extension(filter, diff);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
for &limb in &push_channel.value[1..] {
|
||||||
|
let constr = builder.mul_extension(filter, limb);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_packed_set<P: PackedField>(
|
||||||
|
lv: &CpuColumnsView<P>,
|
||||||
|
nv: &CpuColumnsView<P>,
|
||||||
|
yield_constr: &mut ConstraintConsumer<P>,
|
||||||
|
) {
|
||||||
|
let filter = lv.op.set_context;
|
||||||
|
let pop_channel = lv.mem_channels[0];
|
||||||
|
yield_constr.constraint_transition(filter * (pop_channel.value[0] - nv.context));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_ext_circuit_set<F: RichField + Extendable<D>, const D: usize>(
|
||||||
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
|
lv: &CpuColumnsView<ExtensionTarget<D>>,
|
||||||
|
nv: &CpuColumnsView<ExtensionTarget<D>>,
|
||||||
|
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
||||||
|
) {
|
||||||
|
let filter = lv.op.set_context;
|
||||||
|
let pop_channel = lv.mem_channels[0];
|
||||||
|
|
||||||
|
let diff = builder.sub_extension(pop_channel.value[0], nv.context);
|
||||||
|
let constr = builder.mul_extension(filter, diff);
|
||||||
|
yield_constr.constraint_transition(builder, constr);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval_packed<P: PackedField>(
|
||||||
|
lv: &CpuColumnsView<P>,
|
||||||
|
nv: &CpuColumnsView<P>,
|
||||||
|
yield_constr: &mut ConstraintConsumer<P>,
|
||||||
|
) {
|
||||||
|
eval_packed_get(lv, yield_constr);
|
||||||
|
eval_packed_set(lv, nv, yield_constr);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
|
||||||
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
|
lv: &CpuColumnsView<ExtensionTarget<D>>,
|
||||||
|
nv: &CpuColumnsView<ExtensionTarget<D>>,
|
||||||
|
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
||||||
|
) {
|
||||||
|
eval_ext_circuit_get(builder, lv, yield_constr);
|
||||||
|
eval_ext_circuit_set(builder, lv, nv, yield_constr);
|
||||||
|
}
|
||||||
@ -11,8 +11,8 @@ use plonky2::hash::hash_types::RichField;
|
|||||||
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
||||||
use crate::cpu::columns::{CpuColumnsView, COL_MAP, NUM_CPU_COLUMNS};
|
use crate::cpu::columns::{CpuColumnsView, COL_MAP, NUM_CPU_COLUMNS};
|
||||||
use crate::cpu::{
|
use crate::cpu::{
|
||||||
bootstrap_kernel, control_flow, decode, dup_swap, jumps, membus, modfp254, shift, simple_logic,
|
bootstrap_kernel, contextops, control_flow, decode, dup_swap, jumps, membus, memio, modfp254,
|
||||||
stack, stack_bounds, syscalls,
|
pc, shift, simple_logic, stack, stack_bounds, syscalls,
|
||||||
};
|
};
|
||||||
use crate::cross_table_lookup::Column;
|
use crate::cross_table_lookup::Column;
|
||||||
use crate::memory::segments::Segment;
|
use crate::memory::segments::Segment;
|
||||||
@ -141,15 +141,18 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuStark<F, D
|
|||||||
// TODO: Some failing constraints temporarily disabled by using this dummy consumer.
|
// TODO: Some failing constraints temporarily disabled by using this dummy consumer.
|
||||||
let mut dummy_yield_constr = ConstraintConsumer::new(vec![], P::ZEROS, P::ZEROS, P::ZEROS);
|
let mut dummy_yield_constr = ConstraintConsumer::new(vec![], P::ZEROS, P::ZEROS, P::ZEROS);
|
||||||
bootstrap_kernel::eval_bootstrap_kernel(vars, yield_constr);
|
bootstrap_kernel::eval_bootstrap_kernel(vars, yield_constr);
|
||||||
|
contextops::eval_packed(local_values, next_values, yield_constr);
|
||||||
control_flow::eval_packed_generic(local_values, next_values, yield_constr);
|
control_flow::eval_packed_generic(local_values, next_values, yield_constr);
|
||||||
decode::eval_packed_generic(local_values, yield_constr);
|
decode::eval_packed_generic(local_values, &mut dummy_yield_constr);
|
||||||
dup_swap::eval_packed(local_values, yield_constr);
|
dup_swap::eval_packed(local_values, yield_constr);
|
||||||
jumps::eval_packed(local_values, next_values, &mut dummy_yield_constr);
|
jumps::eval_packed(local_values, next_values, yield_constr);
|
||||||
membus::eval_packed(local_values, yield_constr);
|
membus::eval_packed(local_values, yield_constr);
|
||||||
|
memio::eval_packed(local_values, yield_constr);
|
||||||
modfp254::eval_packed(local_values, yield_constr);
|
modfp254::eval_packed(local_values, yield_constr);
|
||||||
|
pc::eval_packed(local_values, yield_constr);
|
||||||
shift::eval_packed(local_values, yield_constr);
|
shift::eval_packed(local_values, yield_constr);
|
||||||
simple_logic::eval_packed(local_values, yield_constr);
|
simple_logic::eval_packed(local_values, yield_constr);
|
||||||
stack::eval_packed(local_values, yield_constr);
|
stack::eval_packed(local_values, &mut dummy_yield_constr);
|
||||||
stack_bounds::eval_packed(local_values, &mut dummy_yield_constr);
|
stack_bounds::eval_packed(local_values, &mut dummy_yield_constr);
|
||||||
syscalls::eval_packed(local_values, next_values, yield_constr);
|
syscalls::eval_packed(local_values, next_values, yield_constr);
|
||||||
}
|
}
|
||||||
@ -167,15 +170,18 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuStark<F, D
|
|||||||
let mut dummy_yield_constr =
|
let mut dummy_yield_constr =
|
||||||
RecursiveConstraintConsumer::new(zero, vec![], zero, zero, zero);
|
RecursiveConstraintConsumer::new(zero, vec![], zero, zero, zero);
|
||||||
bootstrap_kernel::eval_bootstrap_kernel_circuit(builder, vars, yield_constr);
|
bootstrap_kernel::eval_bootstrap_kernel_circuit(builder, vars, yield_constr);
|
||||||
|
contextops::eval_ext_circuit(builder, local_values, next_values, yield_constr);
|
||||||
control_flow::eval_ext_circuit(builder, local_values, next_values, yield_constr);
|
control_flow::eval_ext_circuit(builder, local_values, next_values, yield_constr);
|
||||||
decode::eval_ext_circuit(builder, local_values, yield_constr);
|
decode::eval_ext_circuit(builder, local_values, &mut dummy_yield_constr);
|
||||||
dup_swap::eval_ext_circuit(builder, local_values, yield_constr);
|
dup_swap::eval_ext_circuit(builder, local_values, yield_constr);
|
||||||
jumps::eval_ext_circuit(builder, local_values, next_values, &mut dummy_yield_constr);
|
jumps::eval_ext_circuit(builder, local_values, next_values, yield_constr);
|
||||||
membus::eval_ext_circuit(builder, local_values, yield_constr);
|
membus::eval_ext_circuit(builder, local_values, yield_constr);
|
||||||
|
memio::eval_ext_circuit(builder, local_values, yield_constr);
|
||||||
modfp254::eval_ext_circuit(builder, local_values, yield_constr);
|
modfp254::eval_ext_circuit(builder, local_values, yield_constr);
|
||||||
|
pc::eval_ext_circuit(builder, local_values, yield_constr);
|
||||||
shift::eval_ext_circuit(builder, local_values, yield_constr);
|
shift::eval_ext_circuit(builder, local_values, yield_constr);
|
||||||
simple_logic::eval_ext_circuit(builder, local_values, yield_constr);
|
simple_logic::eval_ext_circuit(builder, local_values, yield_constr);
|
||||||
stack::eval_ext_circuit(builder, local_values, yield_constr);
|
stack::eval_ext_circuit(builder, local_values, &mut dummy_yield_constr);
|
||||||
stack_bounds::eval_ext_circuit(builder, local_values, &mut dummy_yield_constr);
|
stack_bounds::eval_ext_circuit(builder, local_values, &mut dummy_yield_constr);
|
||||||
syscalls::eval_ext_circuit(builder, local_values, next_values, yield_constr);
|
syscalls::eval_ext_circuit(builder, local_values, next_values, yield_constr);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
use once_cell::sync::Lazy;
|
|
||||||
use plonky2::field::extension::Extendable;
|
use plonky2::field::extension::Extendable;
|
||||||
use plonky2::field::packed::PackedField;
|
use plonky2::field::packed::PackedField;
|
||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
@ -7,10 +6,8 @@ use plonky2::iop::ext_target::ExtensionTarget;
|
|||||||
|
|
||||||
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
||||||
use crate::cpu::columns::CpuColumnsView;
|
use crate::cpu::columns::CpuColumnsView;
|
||||||
use crate::cpu::kernel::aggregator::KERNEL;
|
use crate::cpu::membus::NUM_GP_CHANNELS;
|
||||||
|
use crate::memory::segments::Segment;
|
||||||
static INVALID_DST_HANDLER_ADDR: Lazy<usize> =
|
|
||||||
Lazy::new(|| KERNEL.global_labels["fault_exception"]);
|
|
||||||
|
|
||||||
pub fn eval_packed_exit_kernel<P: PackedField>(
|
pub fn eval_packed_exit_kernel<P: PackedField>(
|
||||||
lv: &CpuColumnsView<P>,
|
lv: &CpuColumnsView<P>,
|
||||||
@ -58,99 +55,65 @@ pub fn eval_packed_jump_jumpi<P: PackedField>(
|
|||||||
yield_constr: &mut ConstraintConsumer<P>,
|
yield_constr: &mut ConstraintConsumer<P>,
|
||||||
) {
|
) {
|
||||||
let jumps_lv = lv.general.jumps();
|
let jumps_lv = lv.general.jumps();
|
||||||
let input0 = lv.mem_channels[0].value;
|
let dst = lv.mem_channels[0].value;
|
||||||
let input1 = lv.mem_channels[1].value;
|
let cond = lv.mem_channels[1].value;
|
||||||
let filter = lv.op.jump + lv.op.jumpi; // `JUMP` or `JUMPI`
|
let filter = lv.op.jump + lv.op.jumpi; // `JUMP` or `JUMPI`
|
||||||
|
let jumpdest_flag_channel = lv.mem_channels[NUM_GP_CHANNELS - 1];
|
||||||
|
|
||||||
// If `JUMP`, re-use the `JUMPI` logic, but setting the second input (the predicate) to be 1.
|
// If `JUMP`, re-use the `JUMPI` logic, but setting the second input (the predicate) to be 1.
|
||||||
// In other words, we implement `JUMP(addr)` as `JUMPI(addr, cond=1)`.
|
// In other words, we implement `JUMP(dst)` as `JUMPI(dst, cond=1)`.
|
||||||
yield_constr.constraint(lv.op.jump * (input1[0] - P::ONES));
|
yield_constr.constraint(lv.op.jump * (cond[0] - P::ONES));
|
||||||
for &limb in &input1[1..] {
|
for &limb in &cond[1..] {
|
||||||
// Set all limbs (other than the least-significant limb) to 0.
|
// Set all limbs (other than the least-significant limb) to 0.
|
||||||
// NB: Technically, they don't have to be 0, as long as the sum
|
// NB: Technically, they don't have to be 0, as long as the sum
|
||||||
// `input1[0] + ... + input1[7]` cannot overflow.
|
// `cond[0] + ... + cond[7]` cannot overflow.
|
||||||
yield_constr.constraint(lv.op.jump * limb);
|
yield_constr.constraint(lv.op.jump * limb);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check `input0_upper_zero`
|
// Check `should_jump`:
|
||||||
// `input0_upper_zero` is either 0 or 1.
|
|
||||||
yield_constr
|
|
||||||
.constraint(filter * jumps_lv.input0_upper_zero * (jumps_lv.input0_upper_zero - P::ONES));
|
|
||||||
// The below sum cannot overflow due to the limb size.
|
|
||||||
let input0_upper_sum: P = input0[1..].iter().copied().sum();
|
|
||||||
// `input0_upper_zero` = 1 implies `input0_upper_sum` = 0.
|
|
||||||
yield_constr.constraint(filter * jumps_lv.input0_upper_zero * input0_upper_sum);
|
|
||||||
// `input0_upper_zero` = 0 implies `input0_upper_sum_inv * input0_upper_sum` = 1, which can only
|
|
||||||
// happen when `input0_upper_sum` is nonzero.
|
|
||||||
yield_constr.constraint(
|
|
||||||
filter
|
|
||||||
* (jumps_lv.input0_upper_sum_inv * input0_upper_sum + jumps_lv.input0_upper_zero
|
|
||||||
- P::ONES),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check `dst_valid_or_kernel` (this is just a logical OR)
|
|
||||||
yield_constr.constraint(
|
|
||||||
filter
|
|
||||||
* (jumps_lv.dst_valid + lv.is_kernel_mode
|
|
||||||
- jumps_lv.dst_valid * lv.is_kernel_mode
|
|
||||||
- jumps_lv.dst_valid_or_kernel),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check `input0_jumpable` (this is just `dst_valid_or_kernel` AND `input0_upper_zero`)
|
|
||||||
yield_constr.constraint(
|
|
||||||
filter
|
|
||||||
* (jumps_lv.dst_valid_or_kernel * jumps_lv.input0_upper_zero
|
|
||||||
- jumps_lv.input0_jumpable),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Make sure that `should_continue`, `should_jump`, `should_trap` are all binary and exactly one
|
|
||||||
// is set.
|
|
||||||
yield_constr
|
|
||||||
.constraint(filter * jumps_lv.should_continue * (jumps_lv.should_continue - P::ONES));
|
|
||||||
yield_constr.constraint(filter * jumps_lv.should_jump * (jumps_lv.should_jump - P::ONES));
|
yield_constr.constraint(filter * jumps_lv.should_jump * (jumps_lv.should_jump - P::ONES));
|
||||||
yield_constr.constraint(filter * jumps_lv.should_trap * (jumps_lv.should_trap - P::ONES));
|
let cond_sum: P = cond.into_iter().sum();
|
||||||
|
yield_constr.constraint(filter * (jumps_lv.should_jump - P::ONES) * cond_sum);
|
||||||
|
yield_constr.constraint(filter * (jumps_lv.cond_sum_pinv * cond_sum - jumps_lv.should_jump));
|
||||||
|
|
||||||
|
// If we're jumping, then the high 7 limbs of the destination must be 0.
|
||||||
|
let dst_hi_sum: P = dst[1..].iter().copied().sum();
|
||||||
|
yield_constr.constraint(filter * jumps_lv.should_jump * dst_hi_sum);
|
||||||
|
// Check that the destination address holds a `JUMPDEST` instruction. Note that this constraint
|
||||||
|
// does not need to be conditioned on `should_jump` because no read takes place if we're not
|
||||||
|
// jumping, so we're free to set the channel to 1.
|
||||||
|
yield_constr.constraint(filter * (jumpdest_flag_channel.value[0] - P::ONES));
|
||||||
|
|
||||||
|
// Make sure that the JUMPDEST flag channel is constrained.
|
||||||
|
// Only need to read if we're about to jump and we're not in kernel mode.
|
||||||
yield_constr.constraint(
|
yield_constr.constraint(
|
||||||
filter * (jumps_lv.should_continue + jumps_lv.should_jump + jumps_lv.should_trap - P::ONES),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Validate `should_continue`
|
|
||||||
// This sum cannot overflow (due to limb size).
|
|
||||||
let input1_sum: P = input1.into_iter().sum();
|
|
||||||
// `should_continue` = 1 implies `input1_sum` = 0.
|
|
||||||
yield_constr.constraint(filter * jumps_lv.should_continue * input1_sum);
|
|
||||||
// `should_continue` = 0 implies `input1_sum * input1_sum_inv` = 1, which can only happen if
|
|
||||||
// input1_sum is nonzero.
|
|
||||||
yield_constr.constraint(
|
|
||||||
filter * (input1_sum * jumps_lv.input1_sum_inv + jumps_lv.should_continue - P::ONES),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Validate `should_jump` and `should_trap` by splitting on `input0_jumpable`.
|
|
||||||
// Note that `should_jump` = 1 and `should_trap` = 1 both imply that `should_continue` = 0, so
|
|
||||||
// `input1` is nonzero.
|
|
||||||
yield_constr.constraint(filter * jumps_lv.should_jump * (jumps_lv.input0_jumpable - P::ONES));
|
|
||||||
yield_constr.constraint(filter * jumps_lv.should_trap * jumps_lv.input0_jumpable);
|
|
||||||
|
|
||||||
// Handle trap
|
|
||||||
// Set program counter and kernel flag
|
|
||||||
yield_constr
|
|
||||||
.constraint_transition(filter * jumps_lv.should_trap * (nv.is_kernel_mode - P::ONES));
|
|
||||||
yield_constr.constraint_transition(
|
|
||||||
filter
|
filter
|
||||||
* jumps_lv.should_trap
|
* (jumpdest_flag_channel.used - jumps_lv.should_jump * (P::ONES - lv.is_kernel_mode)),
|
||||||
* (nv.program_counter - P::Scalar::from_canonical_usize(*INVALID_DST_HANDLER_ADDR)),
|
|
||||||
);
|
);
|
||||||
|
yield_constr.constraint(filter * (jumpdest_flag_channel.is_read - P::ONES));
|
||||||
|
yield_constr.constraint(filter * (jumpdest_flag_channel.addr_context - lv.context));
|
||||||
|
yield_constr.constraint(
|
||||||
|
filter
|
||||||
|
* (jumpdest_flag_channel.addr_segment
|
||||||
|
- P::Scalar::from_canonical_u64(Segment::JumpdestBits as u64)),
|
||||||
|
);
|
||||||
|
yield_constr.constraint(filter * (jumpdest_flag_channel.addr_virtual - dst[0]));
|
||||||
|
|
||||||
// Handle continue and jump
|
// Disable unused memory channels
|
||||||
let continue_or_jump = jumps_lv.should_continue + jumps_lv.should_jump;
|
for &channel in &lv.mem_channels[2..NUM_GP_CHANNELS - 1] {
|
||||||
// Keep kernel mode.
|
yield_constr.constraint(filter * channel.used);
|
||||||
yield_constr
|
}
|
||||||
.constraint_transition(filter * continue_or_jump * (nv.is_kernel_mode - lv.is_kernel_mode));
|
// Channel 1 is unused by the `JUMP` instruction.
|
||||||
// Set program counter depending on whether we're continuing or jumping.
|
yield_constr.constraint(lv.op.jump * lv.mem_channels[1].used);
|
||||||
|
|
||||||
|
// Finally, set the next program counter.
|
||||||
|
let fallthrough_dst = lv.program_counter + P::ONES;
|
||||||
|
let jump_dest = dst[0];
|
||||||
yield_constr.constraint_transition(
|
yield_constr.constraint_transition(
|
||||||
filter * jumps_lv.should_continue * (nv.program_counter - lv.program_counter - P::ONES),
|
filter * (jumps_lv.should_jump - P::ONES) * (nv.program_counter - fallthrough_dst),
|
||||||
);
|
);
|
||||||
yield_constr
|
yield_constr
|
||||||
.constraint_transition(filter * jumps_lv.should_jump * (nv.program_counter - input0[0]));
|
.constraint_transition(filter * jumps_lv.should_jump * (nv.program_counter - jump_dest));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_ext_circuit_jump_jumpi<F: RichField + Extendable<D>, const D: usize>(
|
pub fn eval_ext_circuit_jump_jumpi<F: RichField + Extendable<D>, const D: usize>(
|
||||||
@ -160,178 +123,124 @@ pub fn eval_ext_circuit_jump_jumpi<F: RichField + Extendable<D>, const D: usize>
|
|||||||
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
||||||
) {
|
) {
|
||||||
let jumps_lv = lv.general.jumps();
|
let jumps_lv = lv.general.jumps();
|
||||||
let input0 = lv.mem_channels[0].value;
|
let dst = lv.mem_channels[0].value;
|
||||||
let input1 = lv.mem_channels[1].value;
|
let cond = lv.mem_channels[1].value;
|
||||||
let filter = builder.add_extension(lv.op.jump, lv.op.jumpi); // `JUMP` or `JUMPI`
|
let filter = builder.add_extension(lv.op.jump, lv.op.jumpi); // `JUMP` or `JUMPI`
|
||||||
|
let jumpdest_flag_channel = lv.mem_channels[NUM_GP_CHANNELS - 1];
|
||||||
|
|
||||||
// If `JUMP`, re-use the `JUMPI` logic, but setting the second input (the predicate) to be 1.
|
// If `JUMP`, re-use the `JUMPI` logic, but setting the second input (the predicate) to be 1.
|
||||||
// In other words, we implement `JUMP(addr)` as `JUMPI(addr, cond=1)`.
|
// In other words, we implement `JUMP(dst)` as `JUMPI(dst, cond=1)`.
|
||||||
{
|
{
|
||||||
let constr = builder.mul_sub_extension(lv.op.jump, input1[0], lv.op.jump);
|
let constr = builder.mul_sub_extension(lv.op.jump, cond[0], lv.op.jump);
|
||||||
yield_constr.constraint(builder, constr);
|
yield_constr.constraint(builder, constr);
|
||||||
}
|
}
|
||||||
for &limb in &input1[1..] {
|
for &limb in &cond[1..] {
|
||||||
// Set all limbs (other than the least-significant limb) to 0.
|
// Set all limbs (other than the least-significant limb) to 0.
|
||||||
// NB: Technically, they don't have to be 0, as long as the sum
|
// NB: Technically, they don't have to be 0, as long as the sum
|
||||||
// `input1[0] + ... + input1[7]` cannot overflow.
|
// `cond[0] + ... + cond[7]` cannot overflow.
|
||||||
let constr = builder.mul_extension(lv.op.jump, limb);
|
let constr = builder.mul_extension(lv.op.jump, limb);
|
||||||
yield_constr.constraint(builder, constr);
|
yield_constr.constraint(builder, constr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check `input0_upper_zero`
|
// Check `should_jump`:
|
||||||
// `input0_upper_zero` is either 0 or 1.
|
|
||||||
{
|
{
|
||||||
let constr = builder.mul_sub_extension(
|
let constr = builder.mul_sub_extension(
|
||||||
jumps_lv.input0_upper_zero,
|
jumps_lv.should_jump,
|
||||||
jumps_lv.input0_upper_zero,
|
jumps_lv.should_jump,
|
||||||
jumps_lv.input0_upper_zero,
|
jumps_lv.should_jump,
|
||||||
);
|
);
|
||||||
let constr = builder.mul_extension(filter, constr);
|
let constr = builder.mul_extension(filter, constr);
|
||||||
yield_constr.constraint(builder, constr);
|
yield_constr.constraint(builder, constr);
|
||||||
}
|
}
|
||||||
|
let cond_sum = builder.add_many_extension(cond);
|
||||||
{
|
{
|
||||||
// The below sum cannot overflow due to the limb size.
|
let constr = builder.mul_sub_extension(cond_sum, jumps_lv.should_jump, cond_sum);
|
||||||
let input0_upper_sum = builder.add_many_extension(input0[1..].iter());
|
|
||||||
|
|
||||||
// `input0_upper_zero` = 1 implies `input0_upper_sum` = 0.
|
|
||||||
let constr = builder.mul_extension(jumps_lv.input0_upper_zero, input0_upper_sum);
|
|
||||||
let constr = builder.mul_extension(filter, constr);
|
let constr = builder.mul_extension(filter, constr);
|
||||||
yield_constr.constraint(builder, constr);
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
// `input0_upper_zero` = 0 implies `input0_upper_sum_inv * input0_upper_sum` = 1, which can
|
|
||||||
// only happen when `input0_upper_sum` is nonzero.
|
|
||||||
let constr = builder.mul_add_extension(
|
|
||||||
jumps_lv.input0_upper_sum_inv,
|
|
||||||
input0_upper_sum,
|
|
||||||
jumps_lv.input0_upper_zero,
|
|
||||||
);
|
|
||||||
let constr = builder.mul_sub_extension(filter, constr, filter);
|
|
||||||
yield_constr.constraint(builder, constr);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check `dst_valid_or_kernel` (this is just a logical OR)
|
|
||||||
{
|
{
|
||||||
let constr = builder.mul_add_extension(
|
let constr =
|
||||||
jumps_lv.dst_valid,
|
builder.mul_sub_extension(jumps_lv.cond_sum_pinv, cond_sum, jumps_lv.should_jump);
|
||||||
|
let constr = builder.mul_extension(filter, constr);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're jumping, then the high 7 limbs of the destination must be 0.
|
||||||
|
let dst_hi_sum = builder.add_many_extension(&dst[1..]);
|
||||||
|
{
|
||||||
|
let constr = builder.mul_extension(jumps_lv.should_jump, dst_hi_sum);
|
||||||
|
let constr = builder.mul_extension(filter, constr);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
// Check that the destination address holds a `JUMPDEST` instruction. Note that this constraint
|
||||||
|
// does not need to be conditioned on `should_jump` because no read takes place if we're not
|
||||||
|
// jumping, so we're free to set the channel to 1.
|
||||||
|
{
|
||||||
|
let constr = builder.mul_sub_extension(filter, jumpdest_flag_channel.value[0], filter);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that the JUMPDEST flag channel is constrained.
|
||||||
|
// Only need to read if we're about to jump and we're not in kernel mode.
|
||||||
|
{
|
||||||
|
let constr = builder.mul_sub_extension(
|
||||||
|
jumps_lv.should_jump,
|
||||||
lv.is_kernel_mode,
|
lv.is_kernel_mode,
|
||||||
jumps_lv.dst_valid_or_kernel,
|
|
||||||
);
|
|
||||||
let constr = builder.sub_extension(jumps_lv.dst_valid, constr);
|
|
||||||
let constr = builder.add_extension(lv.is_kernel_mode, constr);
|
|
||||||
let constr = builder.mul_extension(filter, constr);
|
|
||||||
yield_constr.constraint(builder, constr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check `input0_jumpable` (this is just `dst_valid_or_kernel` AND `input0_upper_zero`)
|
|
||||||
{
|
|
||||||
let constr = builder.mul_sub_extension(
|
|
||||||
jumps_lv.dst_valid_or_kernel,
|
|
||||||
jumps_lv.input0_upper_zero,
|
|
||||||
jumps_lv.input0_jumpable,
|
|
||||||
);
|
|
||||||
let constr = builder.mul_extension(filter, constr);
|
|
||||||
yield_constr.constraint(builder, constr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that `should_continue`, `should_jump`, `should_trap` are all binary and exactly one
|
|
||||||
// is set.
|
|
||||||
for flag in [
|
|
||||||
jumps_lv.should_continue,
|
|
||||||
jumps_lv.should_jump,
|
|
||||||
jumps_lv.should_trap,
|
|
||||||
] {
|
|
||||||
let constr = builder.mul_sub_extension(flag, flag, flag);
|
|
||||||
let constr = builder.mul_extension(filter, constr);
|
|
||||||
yield_constr.constraint(builder, constr);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let constr = builder.add_extension(jumps_lv.should_continue, jumps_lv.should_jump);
|
|
||||||
let constr = builder.add_extension(constr, jumps_lv.should_trap);
|
|
||||||
let constr = builder.mul_sub_extension(filter, constr, filter);
|
|
||||||
yield_constr.constraint(builder, constr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate `should_continue`
|
|
||||||
{
|
|
||||||
// This sum cannot overflow (due to limb size).
|
|
||||||
let input1_sum = builder.add_many_extension(input1.into_iter());
|
|
||||||
|
|
||||||
// `should_continue` = 1 implies `input1_sum` = 0.
|
|
||||||
let constr = builder.mul_extension(jumps_lv.should_continue, input1_sum);
|
|
||||||
let constr = builder.mul_extension(filter, constr);
|
|
||||||
yield_constr.constraint(builder, constr);
|
|
||||||
|
|
||||||
// `should_continue` = 0 implies `input1_sum * input1_sum_inv` = 1, which can only happen if
|
|
||||||
// input1_sum is nonzero.
|
|
||||||
let constr = builder.mul_add_extension(
|
|
||||||
input1_sum,
|
|
||||||
jumps_lv.input1_sum_inv,
|
|
||||||
jumps_lv.should_continue,
|
|
||||||
);
|
|
||||||
let constr = builder.mul_sub_extension(filter, constr, filter);
|
|
||||||
yield_constr.constraint(builder, constr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate `should_jump` and `should_trap` by splitting on `input0_jumpable`.
|
|
||||||
// Note that `should_jump` = 1 and `should_trap` = 1 both imply that `should_continue` = 0, so
|
|
||||||
// `input1` is nonzero.
|
|
||||||
{
|
|
||||||
let constr = builder.mul_sub_extension(
|
|
||||||
jumps_lv.should_jump,
|
|
||||||
jumps_lv.input0_jumpable,
|
|
||||||
jumps_lv.should_jump,
|
jumps_lv.should_jump,
|
||||||
);
|
);
|
||||||
|
let constr = builder.add_extension(jumpdest_flag_channel.used, constr);
|
||||||
let constr = builder.mul_extension(filter, constr);
|
let constr = builder.mul_extension(filter, constr);
|
||||||
yield_constr.constraint(builder, constr);
|
yield_constr.constraint(builder, constr);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let constr = builder.mul_extension(jumps_lv.should_trap, jumps_lv.input0_jumpable);
|
let constr = builder.mul_sub_extension(filter, jumpdest_flag_channel.is_read, filter);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let constr = builder.sub_extension(jumpdest_flag_channel.addr_context, lv.context);
|
||||||
let constr = builder.mul_extension(filter, constr);
|
let constr = builder.mul_extension(filter, constr);
|
||||||
yield_constr.constraint(builder, constr);
|
yield_constr.constraint(builder, constr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle trap
|
|
||||||
{
|
{
|
||||||
let trap_filter = builder.mul_extension(filter, jumps_lv.should_trap);
|
|
||||||
|
|
||||||
// Set kernel flag
|
|
||||||
let constr = builder.mul_sub_extension(trap_filter, nv.is_kernel_mode, trap_filter);
|
|
||||||
yield_constr.constraint_transition(builder, constr);
|
|
||||||
|
|
||||||
// Set program counter
|
|
||||||
let constr = builder.arithmetic_extension(
|
let constr = builder.arithmetic_extension(
|
||||||
F::ONE,
|
F::ONE,
|
||||||
-F::from_canonical_usize(*INVALID_DST_HANDLER_ADDR),
|
-F::from_canonical_u64(Segment::JumpdestBits as u64),
|
||||||
trap_filter,
|
filter,
|
||||||
nv.program_counter,
|
jumpdest_flag_channel.addr_segment,
|
||||||
trap_filter,
|
filter,
|
||||||
);
|
);
|
||||||
yield_constr.constraint_transition(builder, constr);
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let constr = builder.sub_extension(jumpdest_flag_channel.addr_virtual, dst[0]);
|
||||||
|
let constr = builder.mul_extension(filter, constr);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle continue and jump
|
// Disable unused memory channels
|
||||||
|
for &channel in &lv.mem_channels[2..NUM_GP_CHANNELS - 1] {
|
||||||
|
let constr = builder.mul_extension(filter, channel.used);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
// Channel 1 is unused by the `JUMP` instruction.
|
||||||
{
|
{
|
||||||
// Keep kernel mode.
|
let constr = builder.mul_extension(lv.op.jump, lv.mem_channels[1].used);
|
||||||
let continue_or_jump =
|
yield_constr.constraint(builder, constr);
|
||||||
builder.add_extension(jumps_lv.should_continue, jumps_lv.should_jump);
|
}
|
||||||
let constr = builder.sub_extension(nv.is_kernel_mode, lv.is_kernel_mode);
|
|
||||||
let constr = builder.mul_extension(continue_or_jump, constr);
|
// Finally, set the next program counter.
|
||||||
let constr = builder.mul_extension(filter, constr);
|
let fallthrough_dst = builder.add_const_extension(lv.program_counter, F::ONE);
|
||||||
|
let jump_dest = dst[0];
|
||||||
|
{
|
||||||
|
let constr_a = builder.mul_sub_extension(filter, jumps_lv.should_jump, filter);
|
||||||
|
let constr_b = builder.sub_extension(nv.program_counter, fallthrough_dst);
|
||||||
|
let constr = builder.mul_extension(constr_a, constr_b);
|
||||||
yield_constr.constraint_transition(builder, constr);
|
yield_constr.constraint_transition(builder, constr);
|
||||||
}
|
}
|
||||||
// Set program counter depending on whether we're continuing...
|
|
||||||
{
|
{
|
||||||
let constr = builder.sub_extension(nv.program_counter, lv.program_counter);
|
let constr_a = builder.mul_extension(filter, jumps_lv.should_jump);
|
||||||
let constr =
|
let constr_b = builder.sub_extension(nv.program_counter, jump_dest);
|
||||||
builder.mul_sub_extension(jumps_lv.should_continue, constr, jumps_lv.should_continue);
|
let constr = builder.mul_extension(constr_a, constr_b);
|
||||||
let constr = builder.mul_extension(filter, constr);
|
|
||||||
yield_constr.constraint_transition(builder, constr);
|
|
||||||
}
|
|
||||||
// ...or jumping.
|
|
||||||
{
|
|
||||||
let constr = builder.sub_extension(nv.program_counter, input0[0]);
|
|
||||||
let constr = builder.mul_extension(jumps_lv.should_jump, constr);
|
|
||||||
let constr = builder.mul_extension(filter, constr);
|
|
||||||
yield_constr.constraint_transition(builder, constr);
|
yield_constr.constraint_transition(builder, constr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,6 +48,7 @@ pub(crate) fn combined_kernel() -> Kernel {
|
|||||||
include_str!("asm/memory/memcpy.asm"),
|
include_str!("asm/memory/memcpy.asm"),
|
||||||
include_str!("asm/memory/metadata.asm"),
|
include_str!("asm/memory/metadata.asm"),
|
||||||
include_str!("asm/memory/packing.asm"),
|
include_str!("asm/memory/packing.asm"),
|
||||||
|
include_str!("asm/memory/syscalls.asm"),
|
||||||
include_str!("asm/memory/txn_fields.asm"),
|
include_str!("asm/memory/txn_fields.asm"),
|
||||||
include_str!("asm/mpt/accounts.asm"),
|
include_str!("asm/mpt/accounts.asm"),
|
||||||
include_str!("asm/mpt/delete/delete.asm"),
|
include_str!("asm/mpt/delete/delete.asm"),
|
||||||
@ -71,8 +72,9 @@ pub(crate) fn combined_kernel() -> Kernel {
|
|||||||
include_str!("asm/ripemd/main.asm"),
|
include_str!("asm/ripemd/main.asm"),
|
||||||
include_str!("asm/ripemd/memory.asm"),
|
include_str!("asm/ripemd/memory.asm"),
|
||||||
include_str!("asm/ripemd/update.asm"),
|
include_str!("asm/ripemd/update.asm"),
|
||||||
include_str!("asm/rlp/encode.asm"),
|
|
||||||
include_str!("asm/rlp/decode.asm"),
|
include_str!("asm/rlp/decode.asm"),
|
||||||
|
include_str!("asm/rlp/encode.asm"),
|
||||||
|
include_str!("asm/rlp/encode_rlp_string.asm"),
|
||||||
include_str!("asm/rlp/num_bytes.asm"),
|
include_str!("asm/rlp/num_bytes.asm"),
|
||||||
include_str!("asm/rlp/read_to_memory.asm"),
|
include_str!("asm/rlp/read_to_memory.asm"),
|
||||||
include_str!("asm/sha2/compression.asm"),
|
include_str!("asm/sha2/compression.asm"),
|
||||||
|
|||||||
@ -4,7 +4,7 @@ global balance:
|
|||||||
// stack: account_ptr, retdest
|
// stack: account_ptr, retdest
|
||||||
DUP1 ISZERO %jumpi(retzero) // If the account pointer is null, return 0.
|
DUP1 ISZERO %jumpi(retzero) // If the account pointer is null, return 0.
|
||||||
%add_const(1)
|
%add_const(1)
|
||||||
// stack: balance_ptr
|
// stack: balance_ptr, retdest
|
||||||
%mload_trie_data
|
%mload_trie_data
|
||||||
// stack: balance, retdest
|
// stack: balance, retdest
|
||||||
SWAP1 JUMP
|
SWAP1 JUMP
|
||||||
|
|||||||
@ -45,7 +45,9 @@ global delegate_call:
|
|||||||
-> (0, 0, value, sender, self, address, gas)
|
-> (0, 0, value, sender, self, address, gas)
|
||||||
%jump(call_common)
|
%jump(call_common)
|
||||||
|
|
||||||
call_common:
|
// Pre stack: static, should_transfer_value, value, sender, address, code_addr, gas, args_offset, args_size, ret_offset, ret_size, retdest
|
||||||
|
// Post stack: success, leftover_gas
|
||||||
|
global call_common:
|
||||||
// stack: static, should_transfer_value, value, sender, address, code_addr, gas, args_offset, args_size, ret_offset, ret_size, retdest
|
// stack: static, should_transfer_value, value, sender, address, code_addr, gas, args_offset, args_size, ret_offset, ret_size, retdest
|
||||||
%create_context
|
%create_context
|
||||||
// Store the static flag in metadata.
|
// Store the static flag in metadata.
|
||||||
@ -108,3 +110,4 @@ after_call:
|
|||||||
// stack: new_ctx, ret_offset, ret_size, retdest
|
// stack: new_ctx, ret_offset, ret_size, retdest
|
||||||
// TODO: Set RETURNDATA.
|
// TODO: Set RETURNDATA.
|
||||||
// TODO: Return to caller w/ EXIT_KERNEL.
|
// TODO: Return to caller w/ EXIT_KERNEL.
|
||||||
|
// TODO: Return leftover gas
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
global get_nonce:
|
global get_nonce:
|
||||||
// stack: address, retdest
|
// stack: address, retdest
|
||||||
// TODO: Replace with actual implementation.
|
// TODO: Replace with actual implementation.
|
||||||
|
POP
|
||||||
JUMP
|
JUMP
|
||||||
|
|
||||||
// Convenience macro to call get_nonce and return where we left off.
|
// Convenience macro to call get_nonce and return where we left off.
|
||||||
|
|||||||
@ -3,39 +3,58 @@
|
|||||||
|
|
||||||
// TODO: Save checkpoints in @CTX_METADATA_STATE_TRIE_CHECKPOINT_PTR and @SEGMENT_STORAGE_TRIE_CHECKPOINT_PTRS.
|
// TODO: Save checkpoints in @CTX_METADATA_STATE_TRIE_CHECKPOINT_PTR and @SEGMENT_STORAGE_TRIE_CHECKPOINT_PTRS.
|
||||||
|
|
||||||
|
// Pre stack: retdest
|
||||||
|
// Post stack: (empty)
|
||||||
global process_normalized_txn:
|
global process_normalized_txn:
|
||||||
// stack: (empty)
|
// stack: retdest
|
||||||
PUSH validate
|
PUSH validate
|
||||||
%jump(intrinsic_gas)
|
%jump(intrinsic_gas)
|
||||||
|
|
||||||
validate:
|
global validate:
|
||||||
// stack: intrinsic_gas
|
// stack: intrinsic_gas, retdest
|
||||||
// TODO: Check gas >= intrinsic_gas.
|
// TODO: Check signature? (Or might happen in type_0.asm etc.)
|
||||||
// TODO: Check sender_balance >= intrinsic_gas + value.
|
// TODO: Assert nonce is correct.
|
||||||
|
// TODO: Assert sender has no code.
|
||||||
|
POP // TODO: Assert gas_limit >= intrinsic_gas.
|
||||||
|
// stack: retdest
|
||||||
|
|
||||||
buy_gas:
|
global charge_gas:
|
||||||
// TODO: Deduct gas from sender (some may be refunded later).
|
// TODO: Deduct gas limit from sender (some gas may be refunded later).
|
||||||
|
|
||||||
increment_nonce:
|
PUSH 0 // TODO: Push sender.
|
||||||
// TODO: Increment nonce.
|
%increment_nonce
|
||||||
|
|
||||||
process_based_on_type:
|
global process_based_on_type:
|
||||||
%is_contract_creation
|
%is_contract_creation
|
||||||
%jumpi(process_contract_creation_txn)
|
%jumpi(process_contract_creation_txn)
|
||||||
%jump(process_message_txn)
|
%jump(process_message_txn)
|
||||||
|
|
||||||
process_contract_creation_txn:
|
global process_contract_creation_txn:
|
||||||
// stack: (empty)
|
// stack: retdest
|
||||||
// Push the code address & length onto the stack, then call `create`.
|
// Push the code address & length onto the stack, then call `create`.
|
||||||
%mload_txn_field(@TXN_FIELD_DATA_LEN)
|
%mload_txn_field(@TXN_FIELD_DATA_LEN)
|
||||||
// stack: code_len
|
// stack: code_len, retdest
|
||||||
PUSH 0
|
PUSH 0
|
||||||
// stack: code_offset, code_len
|
// stack: code_offset, code_len, retdest
|
||||||
PUSH @SEGMENT_TXN_DATA
|
PUSH @SEGMENT_TXN_DATA
|
||||||
// stack: code_segment, code_offset, code_len
|
// stack: code_segment, code_offset, code_len, retdest
|
||||||
PUSH 0 // context
|
PUSH 0 // context
|
||||||
// stack: CODE_ADDR, code_len
|
// stack: CODE_ADDR, code_len, retdest
|
||||||
%jump(create)
|
%jump(create)
|
||||||
|
|
||||||
process_message_txn:
|
global process_message_txn:
|
||||||
// TODO
|
// stack: retdest
|
||||||
|
%mload_txn_field(@TXN_FIELD_VALUE)
|
||||||
|
%mload_txn_field(@TXN_FIELD_TO)
|
||||||
|
%mload_txn_field(@TXN_FIELD_ORIGIN)
|
||||||
|
// stack: from, to, amount, retdest
|
||||||
|
%transfer_eth
|
||||||
|
// stack: transfer_eth_status, retdest
|
||||||
|
%jumpi(process_message_txn_insufficient_balance)
|
||||||
|
// stack: retdest
|
||||||
|
// TODO: If code is non-empty, execute it in a new context.
|
||||||
|
JUMP
|
||||||
|
|
||||||
|
global process_message_txn_insufficient_balance:
|
||||||
|
// stack: retdest
|
||||||
|
PANIC // TODO
|
||||||
|
|||||||
@ -7,7 +7,6 @@ global sys_signextend:
|
|||||||
global sys_slt:
|
global sys_slt:
|
||||||
global sys_sgt:
|
global sys_sgt:
|
||||||
global sys_sar:
|
global sys_sar:
|
||||||
global sys_keccak256:
|
|
||||||
global sys_address:
|
global sys_address:
|
||||||
global sys_balance:
|
global sys_balance:
|
||||||
global sys_origin:
|
global sys_origin:
|
||||||
@ -33,9 +32,6 @@ global sys_gaslimit:
|
|||||||
global sys_chainid:
|
global sys_chainid:
|
||||||
global sys_selfbalance:
|
global sys_selfbalance:
|
||||||
global sys_basefee:
|
global sys_basefee:
|
||||||
global sys_mload:
|
|
||||||
global sys_mstore:
|
|
||||||
global sys_mstore8:
|
|
||||||
global sys_sload:
|
global sys_sload:
|
||||||
global sys_sstore:
|
global sys_sstore:
|
||||||
global sys_msize:
|
global sys_msize:
|
||||||
|
|||||||
@ -1,15 +1,20 @@
|
|||||||
// Transfers some ETH from one address to another. The amount is given in wei.
|
// Transfers some ETH from one address to another. The amount is given in wei.
|
||||||
// Pre stack: from, to, amount, retdest
|
// Pre stack: from, to, amount, retdest
|
||||||
// Post stack: (empty)
|
// Post stack: status (0 indicates success)
|
||||||
global transfer_eth:
|
global transfer_eth:
|
||||||
// stack: from, to, amount, retdest
|
// stack: from, to, amount, retdest
|
||||||
%stack (from, to, amount, retdest)
|
%stack (from, to, amount, retdest)
|
||||||
-> (from, amount, to, amount)
|
-> (from, amount, to, amount, retdest)
|
||||||
%deduct_eth
|
%deduct_eth
|
||||||
// TODO: Handle exception from %deduct_eth?
|
// stack: deduct_eth_status, to, amount, retdest
|
||||||
|
%jumpi(transfer_eth_failure)
|
||||||
// stack: to, amount, retdest
|
// stack: to, amount, retdest
|
||||||
%add_eth
|
%add_eth
|
||||||
// stack: retdest
|
global transfer_eth_3:
|
||||||
|
%stack (retdest) -> (retdest, 0)
|
||||||
|
JUMP
|
||||||
|
global transfer_eth_failure:
|
||||||
|
%stack (to, amount, retdest) -> (retdest, 1)
|
||||||
JUMP
|
JUMP
|
||||||
|
|
||||||
// Convenience macro to call transfer_eth and return where we left off.
|
// Convenience macro to call transfer_eth and return where we left off.
|
||||||
@ -31,11 +36,31 @@ global transfer_eth:
|
|||||||
%%after:
|
%%after:
|
||||||
%endmacro
|
%endmacro
|
||||||
|
|
||||||
|
// Returns 0 on success, or 1 if addr has insufficient balance. Panics if addr isn't found in the trie.
|
||||||
|
// Pre stack: addr, amount, retdest
|
||||||
|
// Post stack: status (0 indicates success)
|
||||||
global deduct_eth:
|
global deduct_eth:
|
||||||
// stack: addr, amount, retdest
|
// stack: addr, amount, retdest
|
||||||
%jump(mpt_read_state_trie)
|
%mpt_read_state_trie
|
||||||
deduct_eth_after_read:
|
// stack: account_ptr, amount, retdest
|
||||||
PANIC // TODO
|
DUP1 ISZERO %jumpi(panic) // If the account pointer is null, return 0.
|
||||||
|
%add_const(1)
|
||||||
|
// stack: balance_ptr, amount, retdest
|
||||||
|
DUP1 %mload_trie_data
|
||||||
|
// stack: balance, balance_ptr, amount, retdest
|
||||||
|
DUP1 DUP4 GT
|
||||||
|
// stack: amount > balance, balance, balance_ptr, amount, retdest
|
||||||
|
%jumpi(deduct_eth_insufficient_balance)
|
||||||
|
%stack (balance, balance_ptr, amount, retdest) -> (balance, amount, balance_ptr, retdest, 0)
|
||||||
|
SUB
|
||||||
|
SWAP1
|
||||||
|
// stack: balance_ptr, balance - amount, retdest, 0
|
||||||
|
%mstore_trie_data
|
||||||
|
// stack: retdest, 0
|
||||||
|
JUMP
|
||||||
|
global deduct_eth_insufficient_balance:
|
||||||
|
%stack (balance, balance_ptr, amount, retdest) -> (retdest, 1)
|
||||||
|
JUMP
|
||||||
|
|
||||||
// Convenience macro to call deduct_eth and return where we left off.
|
// Convenience macro to call deduct_eth and return where we left off.
|
||||||
%macro deduct_eth
|
%macro deduct_eth
|
||||||
@ -44,8 +69,40 @@ deduct_eth_after_read:
|
|||||||
%%after:
|
%%after:
|
||||||
%endmacro
|
%endmacro
|
||||||
|
|
||||||
|
// Pre stack: addr, amount, redest
|
||||||
|
// Post stack: (empty)
|
||||||
global add_eth:
|
global add_eth:
|
||||||
PANIC // TODO
|
// stack: addr, amount, retdest
|
||||||
|
DUP1 %mpt_read_state_trie
|
||||||
|
// stack: account_ptr, addr, amount, retdest
|
||||||
|
DUP1 ISZERO %jumpi(add_eth_new_account) // If the account pointer is null, we need to create the account.
|
||||||
|
%add_const(1)
|
||||||
|
// stack: balance_ptr, addr, amount, retdest
|
||||||
|
DUP1 %mload_trie_data
|
||||||
|
// stack: balance, balance_ptr, addr, amount, retdest
|
||||||
|
%stack (balance, balance_ptr, addr, amount) -> (amount, balance, addr, balance_ptr)
|
||||||
|
ADD
|
||||||
|
// stack: new_balance, addr, balance_ptr, retdest
|
||||||
|
SWAP1 %mstore_trie_data
|
||||||
|
// stack: addr, retdest
|
||||||
|
POP JUMP
|
||||||
|
global add_eth_new_account:
|
||||||
|
// TODO: Skip creation if amount == 0?
|
||||||
|
// stack: null_account_ptr, addr, amount, retdest
|
||||||
|
POP
|
||||||
|
%get_trie_data_size // pointer to new account we're about to create
|
||||||
|
// stack: new_account_ptr, addr, amount, retdest
|
||||||
|
SWAP2
|
||||||
|
// stack: amount, addr, new_account_ptr, retdest
|
||||||
|
PUSH 0 %append_to_trie_data // nonce
|
||||||
|
%append_to_trie_data // balance
|
||||||
|
// stack: addr, new_account_ptr, retdest
|
||||||
|
PUSH 0 %append_to_trie_data // storage root pointer
|
||||||
|
PUSH @EMPTY_STRING_HASH %append_to_trie_data // code hash
|
||||||
|
// stack: addr, new_account_ptr, retdest
|
||||||
|
%addr_to_state_key
|
||||||
|
// stack: key, new_account_ptr, retdest
|
||||||
|
%jump(mpt_insert_state_trie)
|
||||||
|
|
||||||
// Convenience macro to call add_eth and return where we left off.
|
// Convenience macro to call add_eth and return where we left off.
|
||||||
%macro add_eth
|
%macro add_eth
|
||||||
|
|||||||
@ -11,7 +11,7 @@ hash_initial_tries:
|
|||||||
%mpt_hash_txn_trie %mstore_global_metadata(@GLOBAL_METADATA_TXN_TRIE_DIGEST_BEFORE)
|
%mpt_hash_txn_trie %mstore_global_metadata(@GLOBAL_METADATA_TXN_TRIE_DIGEST_BEFORE)
|
||||||
%mpt_hash_receipt_trie %mstore_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_BEFORE)
|
%mpt_hash_receipt_trie %mstore_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_BEFORE)
|
||||||
|
|
||||||
txn_loop:
|
global txn_loop:
|
||||||
// If the prover has no more txns for us to process, halt.
|
// If the prover has no more txns for us to process, halt.
|
||||||
PROVER_INPUT(end_of_txns)
|
PROVER_INPUT(end_of_txns)
|
||||||
%jumpi(hash_final_tries)
|
%jumpi(hash_final_tries)
|
||||||
@ -20,7 +20,7 @@ txn_loop:
|
|||||||
PUSH txn_loop
|
PUSH txn_loop
|
||||||
%jump(route_txn)
|
%jump(route_txn)
|
||||||
|
|
||||||
hash_final_tries:
|
global hash_final_tries:
|
||||||
%mpt_hash_state_trie %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER)
|
%mpt_hash_state_trie %mstore_global_metadata(@GLOBAL_METADATA_STATE_TRIE_DIGEST_AFTER)
|
||||||
%mpt_hash_txn_trie %mstore_global_metadata(@GLOBAL_METADATA_TXN_TRIE_DIGEST_AFTER)
|
%mpt_hash_txn_trie %mstore_global_metadata(@GLOBAL_METADATA_TXN_TRIE_DIGEST_AFTER)
|
||||||
%mpt_hash_receipt_trie %mstore_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_AFTER)
|
%mpt_hash_receipt_trie %mstore_global_metadata(@GLOBAL_METADATA_RECEIPT_TRIE_DIGEST_AFTER)
|
||||||
|
|||||||
82
evm/src/cpu/kernel/asm/memory/syscalls.asm
Normal file
82
evm/src/cpu/kernel/asm/memory/syscalls.asm
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
global sys_mload:
|
||||||
|
// stack: kexit_info, offset
|
||||||
|
PUSH 0 // acc = 0
|
||||||
|
// stack: acc, kexit_info, offset
|
||||||
|
DUP3 %add_const( 0) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xf8) ADD
|
||||||
|
DUP3 %add_const( 1) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xf0) ADD
|
||||||
|
DUP3 %add_const( 2) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xe8) ADD
|
||||||
|
DUP3 %add_const( 3) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xe0) ADD
|
||||||
|
DUP3 %add_const( 4) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xd8) ADD
|
||||||
|
DUP3 %add_const( 5) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xd0) ADD
|
||||||
|
DUP3 %add_const( 6) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xc8) ADD
|
||||||
|
DUP3 %add_const( 7) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xc0) ADD
|
||||||
|
DUP3 %add_const( 8) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xb8) ADD
|
||||||
|
DUP3 %add_const( 9) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xb0) ADD
|
||||||
|
DUP3 %add_const(10) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xa8) ADD
|
||||||
|
DUP3 %add_const(11) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0xa0) ADD
|
||||||
|
DUP3 %add_const(12) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x98) ADD
|
||||||
|
DUP3 %add_const(13) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x90) ADD
|
||||||
|
DUP3 %add_const(14) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x88) ADD
|
||||||
|
DUP3 %add_const(15) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x80) ADD
|
||||||
|
DUP3 %add_const(16) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x78) ADD
|
||||||
|
DUP3 %add_const(17) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x70) ADD
|
||||||
|
DUP3 %add_const(18) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x68) ADD
|
||||||
|
DUP3 %add_const(19) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x60) ADD
|
||||||
|
DUP3 %add_const(20) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x58) ADD
|
||||||
|
DUP3 %add_const(21) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x50) ADD
|
||||||
|
DUP3 %add_const(22) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x48) ADD
|
||||||
|
DUP3 %add_const(23) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x40) ADD
|
||||||
|
DUP3 %add_const(24) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x38) ADD
|
||||||
|
DUP3 %add_const(25) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x30) ADD
|
||||||
|
DUP3 %add_const(26) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x28) ADD
|
||||||
|
DUP3 %add_const(27) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x20) ADD
|
||||||
|
DUP3 %add_const(28) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x18) ADD
|
||||||
|
DUP3 %add_const(29) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x10) ADD
|
||||||
|
DUP3 %add_const(30) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x08) ADD
|
||||||
|
DUP3 %add_const(31) %mload_current(@SEGMENT_MAIN_MEMORY) %shl_const(0x00) ADD
|
||||||
|
%stack (acc, kexit_info, offset) -> (kexit_info, acc)
|
||||||
|
EXIT_KERNEL
|
||||||
|
|
||||||
|
global sys_mstore:
|
||||||
|
// stack: kexit_info, offset, value
|
||||||
|
DUP3 PUSH 0 BYTE DUP3 %add_const( 0) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 1 BYTE DUP3 %add_const( 1) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 2 BYTE DUP3 %add_const( 2) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 3 BYTE DUP3 %add_const( 3) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 4 BYTE DUP3 %add_const( 4) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 5 BYTE DUP3 %add_const( 5) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 6 BYTE DUP3 %add_const( 6) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 7 BYTE DUP3 %add_const( 7) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 8 BYTE DUP3 %add_const( 8) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 9 BYTE DUP3 %add_const( 9) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 10 BYTE DUP3 %add_const(10) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 11 BYTE DUP3 %add_const(11) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 12 BYTE DUP3 %add_const(12) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 13 BYTE DUP3 %add_const(13) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 14 BYTE DUP3 %add_const(14) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 15 BYTE DUP3 %add_const(15) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 16 BYTE DUP3 %add_const(16) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 17 BYTE DUP3 %add_const(17) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 18 BYTE DUP3 %add_const(18) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 19 BYTE DUP3 %add_const(19) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 20 BYTE DUP3 %add_const(20) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 21 BYTE DUP3 %add_const(21) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 22 BYTE DUP3 %add_const(22) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 23 BYTE DUP3 %add_const(23) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 24 BYTE DUP3 %add_const(24) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 25 BYTE DUP3 %add_const(25) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 26 BYTE DUP3 %add_const(26) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 27 BYTE DUP3 %add_const(27) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 28 BYTE DUP3 %add_const(28) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 29 BYTE DUP3 %add_const(29) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 30 BYTE DUP3 %add_const(30) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
DUP3 PUSH 31 BYTE DUP3 %add_const(31) %mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
%stack (kexit_info, offset, value) -> (kexit_info)
|
||||||
|
EXIT_KERNEL
|
||||||
|
|
||||||
|
global sys_mstore8:
|
||||||
|
// stack: kexit_info, offset, value
|
||||||
|
%stack (kexit_info, offset, value) -> (offset, value, kexit_info)
|
||||||
|
%mstore_current(@SEGMENT_MAIN_MEMORY)
|
||||||
|
// stack: kexit_info
|
||||||
|
EXIT_KERNEL
|
||||||
@ -1,6 +1,8 @@
|
|||||||
// Insertion logic specific to a particular trie.
|
// Insertion logic specific to a particular trie.
|
||||||
|
|
||||||
// Mutate the state trie, inserting the given key-value pair.
|
// Mutate the state trie, inserting the given key-value pair.
|
||||||
|
// Pre stack: key, value_ptr, retdest
|
||||||
|
// Post stack: (empty)
|
||||||
global mpt_insert_state_trie:
|
global mpt_insert_state_trie:
|
||||||
// stack: key, value_ptr, retdest
|
// stack: key, value_ptr, retdest
|
||||||
%stack (key, value_ptr)
|
%stack (key, value_ptr)
|
||||||
|
|||||||
@ -8,7 +8,20 @@ global encode_rlp_scalar:
|
|||||||
%gt_const(0x7f)
|
%gt_const(0x7f)
|
||||||
%jumpi(encode_rlp_scalar_medium)
|
%jumpi(encode_rlp_scalar_medium)
|
||||||
|
|
||||||
// This is the "small" case, where the value is its own encoding.
|
// Else, if scalar != 0, this is the "small" case, where the value is its own encoding.
|
||||||
|
DUP2 %jumpi(encode_rlp_scalar_small)
|
||||||
|
|
||||||
|
// scalar = 0, so BE(scalar) is the empty string, which RLP encodes as a single byte 0x80.
|
||||||
|
// stack: pos, scalar, retdest
|
||||||
|
%stack (pos, scalar) -> (pos, 0x80, pos)
|
||||||
|
%mstore_rlp
|
||||||
|
// stack: pos, retdest
|
||||||
|
%increment
|
||||||
|
// stack: pos', retdest
|
||||||
|
SWAP1
|
||||||
|
JUMP
|
||||||
|
|
||||||
|
encode_rlp_scalar_small:
|
||||||
// stack: pos, scalar, retdest
|
// stack: pos, scalar, retdest
|
||||||
%stack (pos, scalar) -> (pos, scalar, pos)
|
%stack (pos, scalar) -> (pos, scalar, pos)
|
||||||
// stack: pos, scalar, pos, retdest
|
// stack: pos, scalar, pos, retdest
|
||||||
@ -127,14 +140,8 @@ encode_rlp_multi_byte_string_prefix_large:
|
|||||||
// stack: pos, len_of_len, str_len, retdest
|
// stack: pos, len_of_len, str_len, retdest
|
||||||
%increment
|
%increment
|
||||||
// stack: pos', len_of_len, str_len, retdest
|
// stack: pos', len_of_len, str_len, retdest
|
||||||
%stack (pos, len_of_len, str_len)
|
%stack (pos, len_of_len, str_len) -> (pos, str_len, len_of_len)
|
||||||
-> (pos, str_len, len_of_len,
|
|
||||||
encode_rlp_multi_byte_string_prefix_large_done_writing_len)
|
|
||||||
%jump(mstore_unpacking_rlp)
|
%jump(mstore_unpacking_rlp)
|
||||||
encode_rlp_multi_byte_string_prefix_large_done_writing_len:
|
|
||||||
// stack: pos'', retdest
|
|
||||||
SWAP1
|
|
||||||
JUMP
|
|
||||||
|
|
||||||
%macro encode_rlp_multi_byte_string_prefix
|
%macro encode_rlp_multi_byte_string_prefix
|
||||||
%stack (pos, str_len) -> (pos, str_len, %%after)
|
%stack (pos, str_len) -> (pos, str_len, %%after)
|
||||||
|
|||||||
72
evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm
Normal file
72
evm/src/cpu/kernel/asm/rlp/encode_rlp_string.asm
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Encodes an arbitrary string, given a pointer and length.
|
||||||
|
// Pre stack: pos, ADDR: 3, len, retdest
|
||||||
|
// Post stack: pos'
|
||||||
|
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
|
||||||
|
MLOAD_GENERAL
|
||||||
|
// stack: first_byte, len == 1, pos, ADDR: 3, len, retdest
|
||||||
|
%lt_const(128)
|
||||||
|
MUL // cheaper than AND
|
||||||
|
// stack: single_small_byte, pos, ADDR: 3, 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
|
||||||
|
%jumpi(encode_rlp_string_large)
|
||||||
|
|
||||||
|
global encode_rlp_string_small:
|
||||||
|
// stack: pos, ADDR: 3, len, retdest
|
||||||
|
DUP5 // 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
|
||||||
|
%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)
|
||||||
|
%jump(memcpy)
|
||||||
|
|
||||||
|
global encode_rlp_string_small_single_byte:
|
||||||
|
// stack: pos, ADDR: 3, len, retdest
|
||||||
|
%stack (pos, ADDR: 3, len) -> (ADDR, pos)
|
||||||
|
MLOAD_GENERAL
|
||||||
|
// stack: byte, pos, retdest
|
||||||
|
DUP2
|
||||||
|
%mstore_rlp
|
||||||
|
// stack: pos, retdest
|
||||||
|
%increment
|
||||||
|
JUMP
|
||||||
|
|
||||||
|
global encode_rlp_string_large:
|
||||||
|
// stack: pos, ADDR: 3, len, retdest
|
||||||
|
DUP5 %num_bytes
|
||||||
|
// stack: len_of_len, pos, ADDR: 3, len, retdest
|
||||||
|
SWAP1
|
||||||
|
DUP2 // 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
|
||||||
|
%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)
|
||||||
|
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)
|
||||||
|
%jump(memcpy)
|
||||||
@ -127,7 +127,92 @@ parse_r:
|
|||||||
%mstore_txn_field(@TXN_FIELD_S)
|
%mstore_txn_field(@TXN_FIELD_S)
|
||||||
// stack: retdest
|
// stack: retdest
|
||||||
|
|
||||||
// TODO: Write the signed txn data to memory, where it can be hashed and
|
type_0_compute_signed_data:
|
||||||
// checked against the signature.
|
// If a chain_id is present in v, the signed data is
|
||||||
|
// keccak256(rlp([nonce, gas_price, gas_limit, to, value, data, chain_id, 0, 0]))
|
||||||
|
// otherwise, it is
|
||||||
|
// keccak256(rlp([nonce, gas_price, gas_limit, to, value, data]))
|
||||||
|
|
||||||
|
%mload_txn_field(@TXN_FIELD_NONCE)
|
||||||
|
// stack: nonce, retdest
|
||||||
|
PUSH 9 // We start at 9 to leave room to prepend the largest possible RLP list header.
|
||||||
|
// stack: rlp_pos, nonce, retdest
|
||||||
|
%encode_rlp_scalar
|
||||||
|
// stack: rlp_pos, retdest
|
||||||
|
|
||||||
|
%mload_txn_field(@TXN_FIELD_MAX_FEE_PER_GAS)
|
||||||
|
SWAP1 %encode_rlp_scalar
|
||||||
|
// stack: rlp_pos, retdest
|
||||||
|
|
||||||
|
%mload_txn_field(@TXN_FIELD_GAS_LIMIT)
|
||||||
|
SWAP1 %encode_rlp_scalar
|
||||||
|
// stack: rlp_pos, retdest
|
||||||
|
|
||||||
|
%mload_txn_field(@TXN_FIELD_TO)
|
||||||
|
SWAP1 %encode_rlp_160
|
||||||
|
// stack: rlp_pos, retdest
|
||||||
|
|
||||||
|
%mload_txn_field(@TXN_FIELD_VALUE)
|
||||||
|
SWAP1 %encode_rlp_scalar
|
||||||
|
// stack: rlp_pos, 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, retdest
|
||||||
|
PUSH after_serializing_txn_data
|
||||||
|
// stack: after_serializing_txn_data, ADDR: 3, len, rlp_pos, retdest
|
||||||
|
SWAP5
|
||||||
|
// stack: rlp_pos, ADDR: 3, len, after_serializing_txn_data, retdest
|
||||||
|
%jump(encode_rlp_string)
|
||||||
|
|
||||||
|
after_serializing_txn_data:
|
||||||
|
// stack: rlp_pos, retdest
|
||||||
|
%mload_txn_field(@TXN_FIELD_CHAIN_ID_PRESENT)
|
||||||
|
ISZERO %jumpi(finish_rlp_list)
|
||||||
|
// stack: rlp_pos, retdest
|
||||||
|
|
||||||
|
%mload_txn_field(@TXN_FIELD_CHAIN_ID)
|
||||||
|
SWAP1 %encode_rlp_scalar
|
||||||
|
// stack: rlp_pos, retdest
|
||||||
|
|
||||||
|
PUSH 0
|
||||||
|
SWAP1 %encode_rlp_scalar
|
||||||
|
// stack: rlp_pos, retdest
|
||||||
|
|
||||||
|
PUSH 0
|
||||||
|
SWAP1 %encode_rlp_scalar
|
||||||
|
// stack: rlp_pos, retdest
|
||||||
|
|
||||||
|
finish_rlp_list:
|
||||||
|
%prepend_rlp_list_prefix
|
||||||
|
// stack: start_pos, rlp_len, retdest
|
||||||
|
PUSH @SEGMENT_RLP_RAW
|
||||||
|
PUSH 0 // context
|
||||||
|
// stack: ADDR: 3, rlp_len, retdest
|
||||||
|
KECCAK_GENERAL
|
||||||
|
// stack: hash, retdest
|
||||||
|
|
||||||
|
%mload_txn_field(@TXN_FIELD_S)
|
||||||
|
%mload_txn_field(@TXN_FIELD_R)
|
||||||
|
%mload_txn_field(@TXN_FIELD_Y_PARITY) %add_const(27) // ecrecover interprets v as y_parity + 27
|
||||||
|
|
||||||
|
PUSH store_origin
|
||||||
|
// stack: store_origin, v, r, s, hash, retdest
|
||||||
|
SWAP4
|
||||||
|
// stack: hash, v, r, s, store_origin, retdest
|
||||||
|
%jump(ecrecover)
|
||||||
|
|
||||||
|
store_origin:
|
||||||
|
// stack: address, retdest
|
||||||
|
// If ecrecover returned u256::MAX, that indicates failure.
|
||||||
|
DUP1
|
||||||
|
%eq_const(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
|
||||||
|
%jumpi(panic)
|
||||||
|
|
||||||
|
// stack: address, retdest
|
||||||
|
%mstore_txn_field(@TXN_FIELD_ORIGIN)
|
||||||
|
// stack: retdest
|
||||||
%jump(process_normalized_txn)
|
%jump(process_normalized_txn)
|
||||||
|
|||||||
@ -1,3 +1,14 @@
|
|||||||
|
global sys_keccak256:
|
||||||
|
// stack: kexit_info, offset, len
|
||||||
|
%stack (kexit_info, offset, len) -> (offset, len, kexit_info)
|
||||||
|
PUSH @SEGMENT_MAIN_MEMORY
|
||||||
|
GET_CONTEXT
|
||||||
|
// stack: ADDR: 3, len, kexit_info
|
||||||
|
KECCAK_GENERAL
|
||||||
|
// stack: hash, kexit_info
|
||||||
|
SWAP1
|
||||||
|
EXIT_KERNEL
|
||||||
|
|
||||||
// Computes Keccak256(input_word). Clobbers @SEGMENT_KERNEL_GENERAL.
|
// Computes Keccak256(input_word). Clobbers @SEGMENT_KERNEL_GENERAL.
|
||||||
//
|
//
|
||||||
// Pre stack: input_word
|
// Pre stack: input_word
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use ethereum_types::U256;
|
use ethereum_types::U256;
|
||||||
use itertools::izip;
|
use itertools::{izip, Itertools};
|
||||||
use keccak_hash::keccak;
|
use keccak_hash::keccak;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
@ -28,6 +28,7 @@ pub struct Kernel {
|
|||||||
pub(crate) code_hash: [u32; 8],
|
pub(crate) code_hash: [u32; 8],
|
||||||
|
|
||||||
pub(crate) global_labels: HashMap<String, usize>,
|
pub(crate) global_labels: HashMap<String, usize>,
|
||||||
|
pub(crate) ordered_labels: Vec<String>,
|
||||||
|
|
||||||
/// Map from `PROVER_INPUT` offsets to their corresponding `ProverInputFn`.
|
/// Map from `PROVER_INPUT` offsets to their corresponding `ProverInputFn`.
|
||||||
pub(crate) prover_inputs: HashMap<usize, ProverInputFn>,
|
pub(crate) prover_inputs: HashMap<usize, ProverInputFn>,
|
||||||
@ -43,18 +44,30 @@ impl Kernel {
|
|||||||
let code_hash = std::array::from_fn(|i| {
|
let code_hash = std::array::from_fn(|i| {
|
||||||
u32::from_le_bytes(std::array::from_fn(|j| code_hash_bytes[i * 4 + j]))
|
u32::from_le_bytes(std::array::from_fn(|j| code_hash_bytes[i * 4 + j]))
|
||||||
});
|
});
|
||||||
|
let ordered_labels = global_labels
|
||||||
|
.keys()
|
||||||
|
.cloned()
|
||||||
|
.sorted_by_key(|label| global_labels[label])
|
||||||
|
.collect();
|
||||||
Self {
|
Self {
|
||||||
code,
|
code,
|
||||||
code_hash,
|
code_hash,
|
||||||
global_labels,
|
global_labels,
|
||||||
|
ordered_labels,
|
||||||
prover_inputs,
|
prover_inputs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a string representation of the current offset for debugging purposes.
|
/// Get a string representation of the current offset for debugging purposes.
|
||||||
pub(crate) fn offset_name(&self, offset: usize) -> String {
|
pub(crate) fn offset_name(&self, offset: usize) -> String {
|
||||||
self.offset_label(offset)
|
match self
|
||||||
.unwrap_or_else(|| offset.to_string())
|
.ordered_labels
|
||||||
|
.binary_search_by_key(&offset, |label| self.global_labels[label])
|
||||||
|
{
|
||||||
|
Ok(idx) => self.ordered_labels[idx].clone(),
|
||||||
|
Err(0) => offset.to_string(),
|
||||||
|
Err(idx) => format!("{}, below {}", offset, self.ordered_labels[idx - 1]),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn offset_label(&self, offset: usize) -> Option<String> {
|
pub(crate) fn offset_label(&self, offset: usize) -> Option<String> {
|
||||||
|
|||||||
@ -49,7 +49,12 @@ pub fn evm_constants() -> HashMap<String, U256> {
|
|||||||
c
|
c
|
||||||
}
|
}
|
||||||
|
|
||||||
const HASH_CONSTANTS: [(&str, [u8; 32]); 1] = [
|
const HASH_CONSTANTS: [(&str, [u8; 32]); 2] = [
|
||||||
|
// Hash of an empty string: keccak(b'').hex()
|
||||||
|
(
|
||||||
|
"EMPTY_STRING_HASH",
|
||||||
|
hex!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"),
|
||||||
|
),
|
||||||
// Hash of an empty node: keccak(rlp.encode(b'')).hex()
|
// Hash of an empty node: keccak(rlp.encode(b'')).hex()
|
||||||
(
|
(
|
||||||
"EMPTY_NODE_HASH",
|
"EMPTY_NODE_HASH",
|
||||||
|
|||||||
@ -17,10 +17,11 @@ pub(crate) enum NormalizedTxnField {
|
|||||||
YParity = 9,
|
YParity = 9,
|
||||||
R = 10,
|
R = 10,
|
||||||
S = 11,
|
S = 11,
|
||||||
|
Origin = 12,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NormalizedTxnField {
|
impl NormalizedTxnField {
|
||||||
pub(crate) const COUNT: usize = 12;
|
pub(crate) const COUNT: usize = 13;
|
||||||
|
|
||||||
pub(crate) fn all() -> [Self; Self::COUNT] {
|
pub(crate) fn all() -> [Self; Self::COUNT] {
|
||||||
[
|
[
|
||||||
@ -36,6 +37,7 @@ impl NormalizedTxnField {
|
|||||||
Self::YParity,
|
Self::YParity,
|
||||||
Self::R,
|
Self::R,
|
||||||
Self::S,
|
Self::S,
|
||||||
|
Self::Origin,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +56,7 @@ impl NormalizedTxnField {
|
|||||||
NormalizedTxnField::YParity => "TXN_FIELD_Y_PARITY",
|
NormalizedTxnField::YParity => "TXN_FIELD_Y_PARITY",
|
||||||
NormalizedTxnField::R => "TXN_FIELD_R",
|
NormalizedTxnField::R => "TXN_FIELD_R",
|
||||||
NormalizedTxnField::S => "TXN_FIELD_S",
|
NormalizedTxnField::S => "TXN_FIELD_S",
|
||||||
|
NormalizedTxnField::Origin => "TXN_FIELD_ORIGIN",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ use crate::generation::prover_input::ProverInputFn;
|
|||||||
use crate::generation::state::GenerationState;
|
use crate::generation::state::GenerationState;
|
||||||
use crate::generation::GenerationInputs;
|
use crate::generation::GenerationInputs;
|
||||||
use crate::memory::segments::Segment;
|
use crate::memory::segments::Segment;
|
||||||
use crate::witness::memory::{MemoryContextState, MemorySegmentState, MemoryState};
|
use crate::witness::memory::{MemoryAddress, MemoryContextState, MemorySegmentState, MemoryState};
|
||||||
use crate::witness::util::stack_peek;
|
use crate::witness::util::stack_peek;
|
||||||
|
|
||||||
type F = GoldilocksField;
|
type F = GoldilocksField;
|
||||||
@ -26,22 +26,11 @@ const DEFAULT_HALT_OFFSET: usize = 0xdeadbeef;
|
|||||||
|
|
||||||
impl MemoryState {
|
impl MemoryState {
|
||||||
fn mload_general(&self, context: usize, segment: Segment, offset: usize) -> U256 {
|
fn mload_general(&self, context: usize, segment: Segment, offset: usize) -> U256 {
|
||||||
let value = self.contexts[context].segments[segment as usize].get(offset);
|
self.get(MemoryAddress::new(context, segment, offset))
|
||||||
assert!(
|
|
||||||
value.bits() <= segment.bit_range(),
|
|
||||||
"Value read from memory exceeds expected range of {:?} segment",
|
|
||||||
segment
|
|
||||||
);
|
|
||||||
value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mstore_general(&mut self, context: usize, segment: Segment, offset: usize, value: U256) {
|
fn mstore_general(&mut self, context: usize, segment: Segment, offset: usize, value: U256) {
|
||||||
assert!(
|
self.set(MemoryAddress::new(context, segment, offset), value);
|
||||||
value.bits() <= segment.bit_range(),
|
|
||||||
"Value written to memory exceeds expected range of {:?} segment",
|
|
||||||
segment
|
|
||||||
);
|
|
||||||
self.contexts[context].segments[segment as usize].set(offset, value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -744,13 +733,6 @@ impl<'a> Interpreter<'a> {
|
|||||||
let segment = Segment::all()[self.pop().as_usize()];
|
let segment = Segment::all()[self.pop().as_usize()];
|
||||||
let offset = self.pop().as_usize();
|
let offset = self.pop().as_usize();
|
||||||
let value = self.pop();
|
let value = self.pop();
|
||||||
assert!(
|
|
||||||
value.bits() <= segment.bit_range(),
|
|
||||||
"Value {} exceeds {:?} range of {} bits",
|
|
||||||
value,
|
|
||||||
segment,
|
|
||||||
segment.bit_range()
|
|
||||||
);
|
|
||||||
self.generation_state
|
self.generation_state
|
||||||
.memory
|
.memory
|
||||||
.mstore_general(context, segment, offset, value);
|
.mstore_general(context, segment, offset, value);
|
||||||
|
|||||||
171
evm/src/cpu/memio.rs
Normal file
171
evm/src/cpu/memio.rs
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
use itertools::izip;
|
||||||
|
use plonky2::field::extension::Extendable;
|
||||||
|
use plonky2::field::packed::PackedField;
|
||||||
|
use plonky2::hash::hash_types::RichField;
|
||||||
|
use plonky2::iop::ext_target::ExtensionTarget;
|
||||||
|
|
||||||
|
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
||||||
|
use crate::cpu::columns::CpuColumnsView;
|
||||||
|
use crate::cpu::membus::NUM_GP_CHANNELS;
|
||||||
|
|
||||||
|
fn get_addr<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)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_packed_load<P: PackedField>(
|
||||||
|
lv: &CpuColumnsView<P>,
|
||||||
|
yield_constr: &mut ConstraintConsumer<P>,
|
||||||
|
) {
|
||||||
|
let filter = lv.op.mload_general;
|
||||||
|
|
||||||
|
let (addr_context, addr_segment, addr_virtual) = get_addr(lv);
|
||||||
|
|
||||||
|
let load_channel = lv.mem_channels[3];
|
||||||
|
let push_channel = lv.mem_channels[NUM_GP_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));
|
||||||
|
yield_constr.constraint(filter * (load_channel.addr_segment - addr_segment));
|
||||||
|
yield_constr.constraint(filter * (load_channel.addr_virtual - addr_virtual));
|
||||||
|
for (load_limb, push_limb) in izip!(load_channel.value, push_channel.value) {
|
||||||
|
yield_constr.constraint(filter * (load_limb - push_limb));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable remaining memory channels, if any.
|
||||||
|
for &channel in &lv.mem_channels[4..NUM_GP_CHANNELS - 1] {
|
||||||
|
yield_constr.constraint(filter * channel.used);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_ext_circuit_load<F: RichField + Extendable<D>, const D: usize>(
|
||||||
|
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
|
||||||
|
lv: &CpuColumnsView<ExtensionTarget<D>>,
|
||||||
|
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
||||||
|
) {
|
||||||
|
let filter = lv.op.mload_general;
|
||||||
|
|
||||||
|
let (addr_context, addr_segment, addr_virtual) = get_addr(lv);
|
||||||
|
|
||||||
|
let load_channel = lv.mem_channels[3];
|
||||||
|
let push_channel = lv.mem_channels[NUM_GP_CHANNELS - 1];
|
||||||
|
{
|
||||||
|
let constr = builder.mul_sub_extension(filter, load_channel.used, filter);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let constr = builder.mul_sub_extension(filter, load_channel.is_read, filter);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
for (channel_field, target) in izip!(
|
||||||
|
[
|
||||||
|
load_channel.addr_context,
|
||||||
|
load_channel.addr_segment,
|
||||||
|
load_channel.addr_virtual,
|
||||||
|
],
|
||||||
|
[addr_context, addr_segment, addr_virtual]
|
||||||
|
) {
|
||||||
|
let diff = builder.sub_extension(channel_field, target);
|
||||||
|
let constr = builder.mul_extension(filter, diff);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
for (load_limb, push_limb) in izip!(load_channel.value, push_channel.value) {
|
||||||
|
let diff = builder.sub_extension(load_limb, push_limb);
|
||||||
|
let constr = builder.mul_extension(filter, diff);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable remaining memory channels, if any.
|
||||||
|
for &channel in &lv.mem_channels[4..NUM_GP_CHANNELS - 1] {
|
||||||
|
let constr = builder.mul_extension(filter, channel.used);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_packed_store<P: PackedField>(
|
||||||
|
lv: &CpuColumnsView<P>,
|
||||||
|
yield_constr: &mut ConstraintConsumer<P>,
|
||||||
|
) {
|
||||||
|
let filter = lv.op.mstore_general;
|
||||||
|
|
||||||
|
let (addr_context, addr_segment, addr_virtual) = get_addr(lv);
|
||||||
|
|
||||||
|
let value_channel = lv.mem_channels[3];
|
||||||
|
let store_channel = lv.mem_channels[4];
|
||||||
|
yield_constr.constraint(filter * (store_channel.used - P::ONES));
|
||||||
|
yield_constr.constraint(filter * store_channel.is_read);
|
||||||
|
yield_constr.constraint(filter * (store_channel.addr_context - addr_context));
|
||||||
|
yield_constr.constraint(filter * (store_channel.addr_segment - addr_segment));
|
||||||
|
yield_constr.constraint(filter * (store_channel.addr_virtual - addr_virtual));
|
||||||
|
for (value_limb, store_limb) in izip!(value_channel.value, store_channel.value) {
|
||||||
|
yield_constr.constraint(filter * (value_limb - store_limb));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable remaining memory channels, if any.
|
||||||
|
for &channel in &lv.mem_channels[5..] {
|
||||||
|
yield_constr.constraint(filter * channel.used);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_ext_circuit_store<F: RichField + Extendable<D>, const D: usize>(
|
||||||
|
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
|
||||||
|
lv: &CpuColumnsView<ExtensionTarget<D>>,
|
||||||
|
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
||||||
|
) {
|
||||||
|
let filter = lv.op.mstore_general;
|
||||||
|
|
||||||
|
let (addr_context, addr_segment, addr_virtual) = get_addr(lv);
|
||||||
|
|
||||||
|
let value_channel = lv.mem_channels[3];
|
||||||
|
let store_channel = lv.mem_channels[4];
|
||||||
|
{
|
||||||
|
let constr = builder.mul_sub_extension(filter, store_channel.used, filter);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let constr = builder.mul_extension(filter, store_channel.is_read);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
for (channel_field, target) in izip!(
|
||||||
|
[
|
||||||
|
store_channel.addr_context,
|
||||||
|
store_channel.addr_segment,
|
||||||
|
store_channel.addr_virtual,
|
||||||
|
],
|
||||||
|
[addr_context, addr_segment, addr_virtual]
|
||||||
|
) {
|
||||||
|
let diff = builder.sub_extension(channel_field, target);
|
||||||
|
let constr = builder.mul_extension(filter, diff);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
for (value_limb, store_limb) in izip!(value_channel.value, store_channel.value) {
|
||||||
|
let diff = builder.sub_extension(value_limb, store_limb);
|
||||||
|
let constr = builder.mul_extension(filter, diff);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable remaining memory channels, if any.
|
||||||
|
for &channel in &lv.mem_channels[5..] {
|
||||||
|
let constr = builder.mul_extension(filter, channel.used);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval_packed<P: PackedField>(
|
||||||
|
lv: &CpuColumnsView<P>,
|
||||||
|
yield_constr: &mut ConstraintConsumer<P>,
|
||||||
|
) {
|
||||||
|
eval_packed_load(lv, yield_constr);
|
||||||
|
eval_packed_store(lv, yield_constr);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
|
||||||
|
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
|
||||||
|
lv: &CpuColumnsView<ExtensionTarget<D>>,
|
||||||
|
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
||||||
|
) {
|
||||||
|
eval_ext_circuit_load(builder, lv, yield_constr);
|
||||||
|
eval_ext_circuit_store(builder, lv, yield_constr);
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
pub(crate) mod bootstrap_kernel;
|
pub(crate) mod bootstrap_kernel;
|
||||||
pub(crate) mod columns;
|
pub(crate) mod columns;
|
||||||
|
mod contextops;
|
||||||
pub(crate) mod control_flow;
|
pub(crate) mod control_flow;
|
||||||
pub mod cpu_stark;
|
pub mod cpu_stark;
|
||||||
pub(crate) mod decode;
|
pub(crate) mod decode;
|
||||||
@ -7,7 +8,9 @@ mod dup_swap;
|
|||||||
mod jumps;
|
mod jumps;
|
||||||
pub mod kernel;
|
pub mod kernel;
|
||||||
pub(crate) mod membus;
|
pub(crate) mod membus;
|
||||||
|
mod memio;
|
||||||
mod modfp254;
|
mod modfp254;
|
||||||
|
mod pc;
|
||||||
mod shift;
|
mod shift;
|
||||||
pub(crate) mod simple_logic;
|
pub(crate) mod simple_logic;
|
||||||
mod stack;
|
mod stack;
|
||||||
|
|||||||
38
evm/src/cpu/pc.rs
Normal file
38
evm/src/cpu/pc.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use plonky2::field::extension::Extendable;
|
||||||
|
use plonky2::field::packed::PackedField;
|
||||||
|
use plonky2::hash::hash_types::RichField;
|
||||||
|
use plonky2::iop::ext_target::ExtensionTarget;
|
||||||
|
|
||||||
|
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
||||||
|
use crate::cpu::columns::CpuColumnsView;
|
||||||
|
use crate::cpu::membus::NUM_GP_CHANNELS;
|
||||||
|
|
||||||
|
pub fn eval_packed<P: PackedField>(
|
||||||
|
lv: &CpuColumnsView<P>,
|
||||||
|
yield_constr: &mut ConstraintConsumer<P>,
|
||||||
|
) {
|
||||||
|
let filter = lv.op.pc;
|
||||||
|
let push_value = lv.mem_channels[NUM_GP_CHANNELS - 1].value;
|
||||||
|
yield_constr.constraint(filter * (push_value[0] - lv.program_counter));
|
||||||
|
for &limb in &push_value[1..] {
|
||||||
|
yield_constr.constraint(filter * limb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
|
||||||
|
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
|
||||||
|
lv: &CpuColumnsView<ExtensionTarget<D>>,
|
||||||
|
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
|
||||||
|
) {
|
||||||
|
let filter = lv.op.pc;
|
||||||
|
let push_value = lv.mem_channels[NUM_GP_CHANNELS - 1].value;
|
||||||
|
{
|
||||||
|
let diff = builder.sub_extension(push_value[0], lv.program_counter);
|
||||||
|
let constr = builder.mul_extension(filter, diff);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
for &limb in &push_value[1..] {
|
||||||
|
let constr = builder.mul_extension(filter, limb);
|
||||||
|
yield_constr.constraint(builder, constr);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -64,20 +64,56 @@ const STACK_BEHAVIORS: OpsColumnsView<Option<StackBehavior>> = OpsColumnsView {
|
|||||||
keccak_general: None, // TODO
|
keccak_general: None, // TODO
|
||||||
prover_input: None, // TODO
|
prover_input: None, // TODO
|
||||||
pop: None, // TODO
|
pop: None, // TODO
|
||||||
jump: None, // TODO
|
jump: Some(StackBehavior {
|
||||||
jumpi: None, // TODO
|
num_pops: 1,
|
||||||
pc: None, // TODO
|
pushes: false,
|
||||||
gas: None, // TODO
|
disable_other_channels: false,
|
||||||
jumpdest: None, // TODO
|
}),
|
||||||
push: None, // TODO
|
jumpi: Some(StackBehavior {
|
||||||
|
num_pops: 2,
|
||||||
|
pushes: false,
|
||||||
|
disable_other_channels: false,
|
||||||
|
}),
|
||||||
|
pc: Some(StackBehavior {
|
||||||
|
num_pops: 0,
|
||||||
|
pushes: true,
|
||||||
|
disable_other_channels: true,
|
||||||
|
}),
|
||||||
|
gas: None, // TODO
|
||||||
|
jumpdest: Some(StackBehavior {
|
||||||
|
num_pops: 0,
|
||||||
|
pushes: false,
|
||||||
|
disable_other_channels: true,
|
||||||
|
}),
|
||||||
|
push: None, // TODO
|
||||||
dup: None,
|
dup: None,
|
||||||
swap: None,
|
swap: None,
|
||||||
get_context: None, // TODO
|
get_context: Some(StackBehavior {
|
||||||
set_context: None, // TODO
|
num_pops: 0,
|
||||||
consume_gas: None, // TODO
|
pushes: true,
|
||||||
exit_kernel: None, // TODO
|
disable_other_channels: true,
|
||||||
mload_general: None, // TODO
|
}),
|
||||||
mstore_general: None, // TODO
|
set_context: Some(StackBehavior {
|
||||||
|
num_pops: 1,
|
||||||
|
pushes: false,
|
||||||
|
disable_other_channels: true,
|
||||||
|
}),
|
||||||
|
consume_gas: None, // TODO
|
||||||
|
exit_kernel: Some(StackBehavior {
|
||||||
|
num_pops: 1,
|
||||||
|
pushes: false,
|
||||||
|
disable_other_channels: true,
|
||||||
|
}),
|
||||||
|
mload_general: Some(StackBehavior {
|
||||||
|
num_pops: 3,
|
||||||
|
pushes: true,
|
||||||
|
disable_other_channels: false,
|
||||||
|
}),
|
||||||
|
mstore_general: Some(StackBehavior {
|
||||||
|
num_pops: 4,
|
||||||
|
pushes: false,
|
||||||
|
disable_other_channels: false,
|
||||||
|
}),
|
||||||
syscall: Some(StackBehavior {
|
syscall: Some(StackBehavior {
|
||||||
num_pops: 0,
|
num_pops: 0,
|
||||||
pushes: true,
|
pushes: true,
|
||||||
|
|||||||
@ -72,8 +72,8 @@ pub fn eval_packed<P: PackedField>(
|
|||||||
|
|
||||||
// This memory channel is constrained in `stack.rs`.
|
// This memory channel is constrained in `stack.rs`.
|
||||||
let output = lv.mem_channels[NUM_GP_CHANNELS - 1].value;
|
let output = lv.mem_channels[NUM_GP_CHANNELS - 1].value;
|
||||||
// Push current PC to stack
|
// Push current PC + 1 to stack
|
||||||
yield_constr.constraint(filter * (output[0] - lv.program_counter));
|
yield_constr.constraint(filter * (output[0] - (lv.program_counter + P::ONES)));
|
||||||
// Push current kernel flag to stack (share register with PC)
|
// Push current kernel flag to stack (share register with PC)
|
||||||
yield_constr.constraint(filter * (output[1] - lv.is_kernel_mode));
|
yield_constr.constraint(filter * (output[1] - lv.is_kernel_mode));
|
||||||
// Zero the rest of that register
|
// Zero the rest of that register
|
||||||
@ -180,9 +180,10 @@ pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
|
|||||||
|
|
||||||
// This memory channel is constrained in `stack.rs`.
|
// This memory channel is constrained in `stack.rs`.
|
||||||
let output = lv.mem_channels[NUM_GP_CHANNELS - 1].value;
|
let output = lv.mem_channels[NUM_GP_CHANNELS - 1].value;
|
||||||
// Push current PC to stack
|
// Push current PC + 1 to stack
|
||||||
{
|
{
|
||||||
let diff = builder.sub_extension(output[0], lv.program_counter);
|
let pc_plus_1 = builder.add_const_extension(lv.program_counter, F::ONE);
|
||||||
|
let diff = builder.sub_extension(output[0], pc_plus_1);
|
||||||
let constr = builder.mul_extension(filter, diff);
|
let constr = builder.mul_extension(filter, diff);
|
||||||
yield_constr.constraint(builder, constr);
|
yield_constr.constraint(builder, constr);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -125,11 +125,12 @@ fn simulate_cpu<F: RichField + Extendable<D>, const D: usize>(state: &mut Genera
|
|||||||
log::info!("CPU halted after {} cycles", state.traces.clock());
|
log::info!("CPU halted after {} cycles", state.traces.clock());
|
||||||
}
|
}
|
||||||
already_in_halt_loop |= in_halt_loop;
|
already_in_halt_loop |= in_halt_loop;
|
||||||
|
|
||||||
|
transition(state);
|
||||||
|
|
||||||
if already_in_halt_loop && state.traces.clock().is_power_of_two() {
|
if already_in_halt_loop && state.traces.clock().is_power_of_two() {
|
||||||
log::info!("CPU trace padded to {} cycles", state.traces.clock());
|
log::info!("CPU trace padded to {} cycles", state.traces.clock());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
transition(state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,17 @@ pub struct AccountRlp {
|
|||||||
pub code_hash: H256,
|
pub code_hash: H256,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for AccountRlp {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
nonce: U256::zero(),
|
||||||
|
balance: U256::zero(),
|
||||||
|
storage_root: PartialTrie::Empty.calc_hash(),
|
||||||
|
code_hash: keccak([]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn all_mpt_prover_inputs_reversed(trie_inputs: &TrieInputs) -> Vec<U256> {
|
pub(crate) fn all_mpt_prover_inputs_reversed(trie_inputs: &TrieInputs) -> Vec<U256> {
|
||||||
let mut inputs = all_mpt_prover_inputs(trie_inputs);
|
let mut inputs = all_mpt_prover_inputs(trie_inputs);
|
||||||
inputs.reverse();
|
inputs.reverse();
|
||||||
|
|||||||
@ -68,25 +68,6 @@ impl MemoryOp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_max_range_check(memory_ops: &[MemoryOp]) -> usize {
|
|
||||||
memory_ops
|
|
||||||
.iter()
|
|
||||||
.tuple_windows()
|
|
||||||
.map(|(curr, next)| {
|
|
||||||
if curr.address.context != next.address.context {
|
|
||||||
next.address.context - curr.address.context - 1
|
|
||||||
} else if curr.address.segment != next.address.segment {
|
|
||||||
next.address.segment - curr.address.segment - 1
|
|
||||||
} else if curr.address.virt != next.address.virt {
|
|
||||||
next.address.virt - curr.address.virt - 1
|
|
||||||
} else {
|
|
||||||
next.timestamp - curr.timestamp
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.max()
|
|
||||||
.unwrap_or(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates the `_FIRST_CHANGE` columns and the `RANGE_CHECK` column in the trace.
|
/// Generates the `_FIRST_CHANGE` columns and the `RANGE_CHECK` column in the trace.
|
||||||
pub fn generate_first_change_flags_and_rc<F: RichField>(trace_rows: &mut [[F; NUM_COLUMNS]]) {
|
pub fn generate_first_change_flags_and_rc<F: RichField>(trace_rows: &mut [[F; NUM_COLUMNS]]) {
|
||||||
let num_ops = trace_rows.len();
|
let num_ops = trace_rows.len();
|
||||||
@ -126,6 +107,12 @@ pub fn generate_first_change_flags_and_rc<F: RichField>(trace_rows: &mut [[F; NU
|
|||||||
} else {
|
} else {
|
||||||
next_timestamp - timestamp
|
next_timestamp - timestamp
|
||||||
};
|
};
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
row[RANGE_CHECK].to_canonical_u64() < num_ops as u64,
|
||||||
|
"Range check of {} is too large. Bug in fill_gaps?",
|
||||||
|
row[RANGE_CHECK]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,17 +120,15 @@ impl<F: RichField + Extendable<D>, const D: usize> MemoryStark<F, D> {
|
|||||||
/// Generate most of the trace rows. Excludes a few columns like `COUNTER`, which are generated
|
/// Generate most of the trace rows. Excludes a few columns like `COUNTER`, which are generated
|
||||||
/// later, after transposing to column-major form.
|
/// later, after transposing to column-major form.
|
||||||
fn generate_trace_row_major(&self, mut memory_ops: Vec<MemoryOp>) -> Vec<[F; NUM_COLUMNS]> {
|
fn generate_trace_row_major(&self, mut memory_ops: Vec<MemoryOp>) -> Vec<[F; NUM_COLUMNS]> {
|
||||||
memory_ops.sort_by_key(|op| {
|
// fill_gaps expects an ordered list of operations.
|
||||||
(
|
memory_ops.sort_by_key(MemoryOp::sorting_key);
|
||||||
op.address.context,
|
Self::fill_gaps(&mut memory_ops);
|
||||||
op.address.segment,
|
|
||||||
op.address.virt,
|
|
||||||
op.timestamp,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
Self::pad_memory_ops(&mut memory_ops);
|
Self::pad_memory_ops(&mut memory_ops);
|
||||||
|
|
||||||
|
// fill_gaps may have added operations at the end which break the order, so sort again.
|
||||||
|
memory_ops.sort_by_key(MemoryOp::sorting_key);
|
||||||
|
|
||||||
let mut trace_rows = memory_ops
|
let mut trace_rows = memory_ops
|
||||||
.into_par_iter()
|
.into_par_iter()
|
||||||
.map(|op| op.into_row())
|
.map(|op| op.into_row())
|
||||||
@ -164,26 +149,64 @@ impl<F: RichField + Extendable<D>, const D: usize> MemoryStark<F, D> {
|
|||||||
trace_col_vecs[COUNTER_PERMUTED] = permuted_table;
|
trace_col_vecs[COUNTER_PERMUTED] = permuted_table;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pad_memory_ops(memory_ops: &mut Vec<MemoryOp>) {
|
/// This memory STARK orders rows by `(context, segment, virt, timestamp)`. To enforce the
|
||||||
let num_ops = memory_ops.len();
|
/// ordering, it range checks the delta of the first field that changed.
|
||||||
let max_range_check = get_max_range_check(memory_ops);
|
///
|
||||||
let num_ops_padded = num_ops.max(max_range_check + 1).next_power_of_two();
|
/// This method adds some dummy operations to ensure that none of these range checks will be too
|
||||||
let to_pad = num_ops_padded - num_ops;
|
/// large, i.e. that they will all be smaller than the number of rows, allowing them to be
|
||||||
|
/// checked easily with a single lookup.
|
||||||
|
///
|
||||||
|
/// For example, say there are 32 memory operations, and a particular address is accessed at
|
||||||
|
/// timestamps 20 and 100. 80 would fail the range check, so this method would add two dummy
|
||||||
|
/// reads to the same address, say at timestamps 50 and 80.
|
||||||
|
fn fill_gaps(memory_ops: &mut Vec<MemoryOp>) {
|
||||||
|
let max_rc = memory_ops.len().next_power_of_two() - 1;
|
||||||
|
for (mut curr, next) in memory_ops.clone().into_iter().tuple_windows() {
|
||||||
|
if curr.address.context != next.address.context
|
||||||
|
|| curr.address.segment != next.address.segment
|
||||||
|
{
|
||||||
|
// We won't bother to check if there's a large context gap, because there can't be
|
||||||
|
// more than 500 contexts or so, as explained here:
|
||||||
|
// https://notes.ethereum.org/@vbuterin/proposals_to_adjust_memory_gas_costs
|
||||||
|
// Similarly, the number of possible segments is a small constant, so any gap must
|
||||||
|
// be small. max_rc will always be much larger, as just bootloading the kernel will
|
||||||
|
// trigger thousands of memory operations.
|
||||||
|
} else if curr.address.virt != next.address.virt {
|
||||||
|
while next.address.virt - curr.address.virt - 1 > max_rc {
|
||||||
|
let mut dummy_address = curr.address;
|
||||||
|
dummy_address.virt += max_rc + 1;
|
||||||
|
let dummy_read = MemoryOp::new_dummy_read(dummy_address, 0);
|
||||||
|
memory_ops.push(dummy_read);
|
||||||
|
curr = dummy_read;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while next.timestamp - curr.timestamp > max_rc {
|
||||||
|
let dummy_read =
|
||||||
|
MemoryOp::new_dummy_read(curr.address, curr.timestamp + max_rc);
|
||||||
|
memory_ops.push(dummy_read);
|
||||||
|
curr = dummy_read;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_memory_ops(memory_ops: &mut Vec<MemoryOp>) {
|
||||||
let last_op = *memory_ops.last().expect("No memory ops?");
|
let last_op = *memory_ops.last().expect("No memory ops?");
|
||||||
|
|
||||||
// We essentially repeat the last operation until our operation list has the desired size,
|
// We essentially repeat the last operation until our operation list has the desired size,
|
||||||
// with a few changes:
|
// with a few changes:
|
||||||
// - We change its filter to 0 to indicate that this is a dummy operation.
|
// - We change its filter to 0 to indicate that this is a dummy operation.
|
||||||
// - We increment its timestamp in order to pass the ordering check.
|
// - We make sure it's a read, since dummy operations must be reads.
|
||||||
// - We make sure it's a read, sine dummy operations must be reads.
|
let padding_op = MemoryOp {
|
||||||
for i in 0..to_pad {
|
filter: false,
|
||||||
memory_ops.push(MemoryOp {
|
kind: Read,
|
||||||
filter: false,
|
..last_op
|
||||||
timestamp: last_op.timestamp + i + 1,
|
};
|
||||||
kind: Read,
|
|
||||||
..last_op
|
let num_ops = memory_ops.len();
|
||||||
});
|
let num_ops_padded = num_ops.next_power_of_two();
|
||||||
|
for _ in num_ops..num_ops_padded {
|
||||||
|
memory_ops.push(padding_op);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use std::any::type_name;
|
use std::any::type_name;
|
||||||
|
|
||||||
use anyhow::{ensure, Result};
|
use anyhow::{ensure, Result};
|
||||||
|
use itertools::Itertools;
|
||||||
use maybe_rayon::*;
|
use maybe_rayon::*;
|
||||||
use plonky2::field::extension::Extendable;
|
use plonky2::field::extension::Extendable;
|
||||||
use plonky2::field::packable::Packable;
|
use plonky2::field::packable::Packable;
|
||||||
@ -53,7 +54,11 @@ where
|
|||||||
[(); LogicStark::<F, D>::COLUMNS]:,
|
[(); LogicStark::<F, D>::COLUMNS]:,
|
||||||
[(); MemoryStark::<F, D>::COLUMNS]:,
|
[(); MemoryStark::<F, D>::COLUMNS]:,
|
||||||
{
|
{
|
||||||
let (traces, public_values) = generate_traces(all_stark, inputs, config, timing);
|
let (traces, public_values) = timed!(
|
||||||
|
timing,
|
||||||
|
"generate all traces",
|
||||||
|
generate_traces(all_stark, inputs, config, timing)
|
||||||
|
);
|
||||||
prove_with_traces(all_stark, config, traces, public_values, timing)
|
prove_with_traces(all_stark, config, traces, public_values, timing)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,19 +85,24 @@ where
|
|||||||
|
|
||||||
let trace_commitments = timed!(
|
let trace_commitments = timed!(
|
||||||
timing,
|
timing,
|
||||||
"compute trace commitments",
|
"compute all trace commitments",
|
||||||
trace_poly_values
|
trace_poly_values
|
||||||
.iter()
|
.iter()
|
||||||
.map(|trace| {
|
.zip_eq(Table::all())
|
||||||
PolynomialBatch::<F, C, D>::from_values(
|
.map(|(trace, table)| {
|
||||||
// TODO: Cloning this isn't great; consider having `from_values` accept a reference,
|
timed!(
|
||||||
// or having `compute_permutation_z_polys` read trace values from the `PolynomialBatch`.
|
|
||||||
trace.clone(),
|
|
||||||
rate_bits,
|
|
||||||
false,
|
|
||||||
cap_height,
|
|
||||||
timing,
|
timing,
|
||||||
None,
|
&format!("compute trace commitment for {:?}", table),
|
||||||
|
PolynomialBatch::<F, C, D>::from_values(
|
||||||
|
// TODO: Cloning this isn't great; consider having `from_values` accept a reference,
|
||||||
|
// or having `compute_permutation_z_polys` read trace values from the `PolynomialBatch`.
|
||||||
|
trace.clone(),
|
||||||
|
rate_bits,
|
||||||
|
false,
|
||||||
|
cap_height,
|
||||||
|
timing,
|
||||||
|
None,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
@ -107,66 +117,30 @@ where
|
|||||||
challenger.observe_cap(cap);
|
challenger.observe_cap(cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ctl_data_per_table = cross_table_lookup_data::<F, C, D>(
|
let ctl_data_per_table = timed!(
|
||||||
config,
|
timing,
|
||||||
&trace_poly_values,
|
"compute CTL data",
|
||||||
&all_stark.cross_table_lookups,
|
cross_table_lookup_data::<F, C, D>(
|
||||||
&mut challenger,
|
config,
|
||||||
|
&trace_poly_values,
|
||||||
|
&all_stark.cross_table_lookups,
|
||||||
|
&mut challenger,
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
let cpu_proof = prove_single_table(
|
let stark_proofs = timed!(
|
||||||
&all_stark.cpu_stark,
|
|
||||||
config,
|
|
||||||
&trace_poly_values[Table::Cpu as usize],
|
|
||||||
&trace_commitments[Table::Cpu as usize],
|
|
||||||
&ctl_data_per_table[Table::Cpu as usize],
|
|
||||||
&mut challenger,
|
|
||||||
timing,
|
timing,
|
||||||
)?;
|
"compute all proofs given commitments",
|
||||||
let keccak_proof = prove_single_table(
|
prove_with_commitments(
|
||||||
&all_stark.keccak_stark,
|
all_stark,
|
||||||
config,
|
config,
|
||||||
&trace_poly_values[Table::Keccak as usize],
|
trace_poly_values,
|
||||||
&trace_commitments[Table::Keccak as usize],
|
trace_commitments,
|
||||||
&ctl_data_per_table[Table::Keccak as usize],
|
ctl_data_per_table,
|
||||||
&mut challenger,
|
&mut challenger,
|
||||||
timing,
|
timing
|
||||||
)?;
|
)?
|
||||||
let keccak_sponge_proof = prove_single_table(
|
);
|
||||||
&all_stark.keccak_sponge_stark,
|
|
||||||
config,
|
|
||||||
&trace_poly_values[Table::KeccakSponge as usize],
|
|
||||||
&trace_commitments[Table::KeccakSponge as usize],
|
|
||||||
&ctl_data_per_table[Table::KeccakSponge as usize],
|
|
||||||
&mut challenger,
|
|
||||||
timing,
|
|
||||||
)?;
|
|
||||||
let logic_proof = prove_single_table(
|
|
||||||
&all_stark.logic_stark,
|
|
||||||
config,
|
|
||||||
&trace_poly_values[Table::Logic as usize],
|
|
||||||
&trace_commitments[Table::Logic as usize],
|
|
||||||
&ctl_data_per_table[Table::Logic as usize],
|
|
||||||
&mut challenger,
|
|
||||||
timing,
|
|
||||||
)?;
|
|
||||||
let memory_proof = prove_single_table(
|
|
||||||
&all_stark.memory_stark,
|
|
||||||
config,
|
|
||||||
&trace_poly_values[Table::Memory as usize],
|
|
||||||
&trace_commitments[Table::Memory as usize],
|
|
||||||
&ctl_data_per_table[Table::Memory as usize],
|
|
||||||
&mut challenger,
|
|
||||||
timing,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let stark_proofs = [
|
|
||||||
cpu_proof,
|
|
||||||
keccak_proof,
|
|
||||||
keccak_sponge_proof,
|
|
||||||
logic_proof,
|
|
||||||
memory_proof,
|
|
||||||
];
|
|
||||||
|
|
||||||
Ok(AllProof {
|
Ok(AllProof {
|
||||||
stark_proofs,
|
stark_proofs,
|
||||||
@ -174,6 +148,99 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prove_with_commitments<F, C, const D: usize>(
|
||||||
|
all_stark: &AllStark<F, D>,
|
||||||
|
config: &StarkConfig,
|
||||||
|
trace_poly_values: [Vec<PolynomialValues<F>>; NUM_TABLES],
|
||||||
|
trace_commitments: Vec<PolynomialBatch<F, C, D>>,
|
||||||
|
ctl_data_per_table: [CtlData<F>; NUM_TABLES],
|
||||||
|
challenger: &mut Challenger<F, C::Hasher>,
|
||||||
|
timing: &mut TimingTree,
|
||||||
|
) -> Result<[StarkProof<F, C, D>; NUM_TABLES]>
|
||||||
|
where
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
C: GenericConfig<D, F = F>,
|
||||||
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
|
[(); CpuStark::<F, D>::COLUMNS]:,
|
||||||
|
[(); KeccakStark::<F, D>::COLUMNS]:,
|
||||||
|
[(); KeccakSpongeStark::<F, D>::COLUMNS]:,
|
||||||
|
[(); LogicStark::<F, D>::COLUMNS]:,
|
||||||
|
[(); MemoryStark::<F, D>::COLUMNS]:,
|
||||||
|
{
|
||||||
|
let cpu_proof = timed!(
|
||||||
|
timing,
|
||||||
|
"prove CPU STARK",
|
||||||
|
prove_single_table(
|
||||||
|
&all_stark.cpu_stark,
|
||||||
|
config,
|
||||||
|
&trace_poly_values[Table::Cpu as usize],
|
||||||
|
&trace_commitments[Table::Cpu as usize],
|
||||||
|
&ctl_data_per_table[Table::Cpu as usize],
|
||||||
|
challenger,
|
||||||
|
timing,
|
||||||
|
)?
|
||||||
|
);
|
||||||
|
let keccak_proof = timed!(
|
||||||
|
timing,
|
||||||
|
"prove Keccak STARK",
|
||||||
|
prove_single_table(
|
||||||
|
&all_stark.keccak_stark,
|
||||||
|
config,
|
||||||
|
&trace_poly_values[Table::Keccak as usize],
|
||||||
|
&trace_commitments[Table::Keccak as usize],
|
||||||
|
&ctl_data_per_table[Table::Keccak as usize],
|
||||||
|
challenger,
|
||||||
|
timing,
|
||||||
|
)?
|
||||||
|
);
|
||||||
|
let keccak_sponge_proof = timed!(
|
||||||
|
timing,
|
||||||
|
"prove Keccak sponge STARK",
|
||||||
|
prove_single_table(
|
||||||
|
&all_stark.keccak_sponge_stark,
|
||||||
|
config,
|
||||||
|
&trace_poly_values[Table::KeccakSponge as usize],
|
||||||
|
&trace_commitments[Table::KeccakSponge as usize],
|
||||||
|
&ctl_data_per_table[Table::KeccakSponge as usize],
|
||||||
|
challenger,
|
||||||
|
timing,
|
||||||
|
)?
|
||||||
|
);
|
||||||
|
let logic_proof = timed!(
|
||||||
|
timing,
|
||||||
|
"prove logic STARK",
|
||||||
|
prove_single_table(
|
||||||
|
&all_stark.logic_stark,
|
||||||
|
config,
|
||||||
|
&trace_poly_values[Table::Logic as usize],
|
||||||
|
&trace_commitments[Table::Logic as usize],
|
||||||
|
&ctl_data_per_table[Table::Logic as usize],
|
||||||
|
challenger,
|
||||||
|
timing,
|
||||||
|
)?
|
||||||
|
);
|
||||||
|
let memory_proof = timed!(
|
||||||
|
timing,
|
||||||
|
"prove memory STARK",
|
||||||
|
prove_single_table(
|
||||||
|
&all_stark.memory_stark,
|
||||||
|
config,
|
||||||
|
&trace_poly_values[Table::Memory as usize],
|
||||||
|
&trace_commitments[Table::Memory as usize],
|
||||||
|
&ctl_data_per_table[Table::Memory as usize],
|
||||||
|
challenger,
|
||||||
|
timing,
|
||||||
|
)?
|
||||||
|
);
|
||||||
|
Ok([
|
||||||
|
cpu_proof,
|
||||||
|
keccak_proof,
|
||||||
|
keccak_sponge_proof,
|
||||||
|
logic_proof,
|
||||||
|
memory_proof,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
/// Compute proof for a single STARK table.
|
/// Compute proof for a single STARK table.
|
||||||
pub(crate) fn prove_single_table<F, C, S, const D: usize>(
|
pub(crate) fn prove_single_table<F, C, S, const D: usize>(
|
||||||
stark: &S,
|
stark: &S,
|
||||||
|
|||||||
@ -18,7 +18,7 @@ use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
|||||||
use plonky2::util::reducing::ReducingFactorTarget;
|
use plonky2::util::reducing::ReducingFactorTarget;
|
||||||
use plonky2::with_context;
|
use plonky2::with_context;
|
||||||
|
|
||||||
use crate::all_stark::{AllStark, Table, NUM_TABLES};
|
use crate::all_stark::{all_cross_table_lookups, AllStark, Table, NUM_TABLES};
|
||||||
use crate::config::StarkConfig;
|
use crate::config::StarkConfig;
|
||||||
use crate::constraint_consumer::RecursiveConstraintConsumer;
|
use crate::constraint_consumer::RecursiveConstraintConsumer;
|
||||||
use crate::cpu::cpu_stark::CpuStark;
|
use crate::cpu::cpu_stark::CpuStark;
|
||||||
@ -162,7 +162,6 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
builder: &mut CircuitBuilder<F, D>,
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
recursive_all_proof_target: RecursiveAllProofTargetWithData<D>,
|
recursive_all_proof_target: RecursiveAllProofTargetWithData<D>,
|
||||||
verifier_data: &[VerifierCircuitData<F, C, D>; NUM_TABLES],
|
verifier_data: &[VerifierCircuitData<F, C, D>; NUM_TABLES],
|
||||||
cross_table_lookups: Vec<CrossTableLookup<F>>,
|
|
||||||
inner_config: &StarkConfig,
|
inner_config: &StarkConfig,
|
||||||
) where
|
) where
|
||||||
[(); C::Hasher::HASH_SIZE]:,
|
[(); C::Hasher::HASH_SIZE]:,
|
||||||
@ -219,7 +218,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
|
|||||||
let degrees_bits = std::array::from_fn(|i| verifier_data[i].common.degree_bits());
|
let degrees_bits = std::array::from_fn(|i| verifier_data[i].common.degree_bits());
|
||||||
verify_cross_table_lookups_circuit::<F, C, D>(
|
verify_cross_table_lookups_circuit::<F, C, D>(
|
||||||
builder,
|
builder,
|
||||||
cross_table_lookups,
|
all_cross_table_lookups(),
|
||||||
pis.map(|p| p.ctl_zs_last),
|
pis.map(|p| p.ctl_zs_last),
|
||||||
degrees_bits,
|
degrees_bits,
|
||||||
ctl_challenges,
|
ctl_challenges,
|
||||||
@ -842,7 +841,7 @@ pub(crate) mod tests {
|
|||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
use plonky2::hash::hashing::SPONGE_WIDTH;
|
use plonky2::hash::hashing::SPONGE_WIDTH;
|
||||||
use plonky2::iop::challenger::RecursiveChallenger;
|
use plonky2::iop::challenger::RecursiveChallenger;
|
||||||
use plonky2::iop::witness::{PartialWitness, Witness};
|
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::{CircuitConfig, VerifierCircuitData};
|
use plonky2::plonk::circuit_data::{CircuitConfig, VerifierCircuitData};
|
||||||
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher};
|
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher};
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use plonky2::field::extension::{Extendable, FieldExtension};
|
|||||||
use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues};
|
use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues};
|
||||||
use plonky2::field::types::{Field, Sample};
|
use plonky2::field::types::{Field, Sample};
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
use plonky2::iop::witness::{PartialWitness, Witness};
|
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||||
use plonky2::plonk::config::{GenericConfig, Hasher};
|
use plonky2::plonk::config::{GenericConfig, Hasher};
|
||||||
|
|||||||
@ -144,11 +144,3 @@ pub(crate) fn biguint_to_u256(x: BigUint) -> U256 {
|
|||||||
let bytes = x.to_bytes_le();
|
let bytes = x.to_bytes_le();
|
||||||
U256::from_little_endian(&bytes)
|
U256::from_little_endian(&bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn u256_saturating_cast_usize(x: U256) -> usize {
|
|
||||||
if x > usize::MAX.into() {
|
|
||||||
usize::MAX
|
|
||||||
} else {
|
|
||||||
x.as_usize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -11,7 +11,6 @@ pub enum MemoryChannel {
|
|||||||
use MemoryChannel::{Code, GeneralPurpose};
|
use MemoryChannel::{Code, GeneralPurpose};
|
||||||
|
|
||||||
use crate::memory::segments::Segment;
|
use crate::memory::segments::Segment;
|
||||||
use crate::util::u256_saturating_cast_usize;
|
|
||||||
|
|
||||||
impl MemoryChannel {
|
impl MemoryChannel {
|
||||||
pub fn index(&self) -> usize {
|
pub fn index(&self) -> usize {
|
||||||
@ -42,10 +41,17 @@ impl MemoryAddress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_u256s(context: U256, segment: U256, virt: U256) -> Self {
|
pub(crate) fn new_u256s(context: U256, segment: U256, virt: U256) -> Self {
|
||||||
|
assert!(context.bits() <= 32, "context too large: {}", context);
|
||||||
|
assert!(
|
||||||
|
segment < Segment::COUNT.into(),
|
||||||
|
"segment too large: {}",
|
||||||
|
segment
|
||||||
|
);
|
||||||
|
assert!(virt.bits() <= 32, "virt too large: {}", virt);
|
||||||
Self {
|
Self {
|
||||||
context: u256_saturating_cast_usize(context),
|
context: context.as_usize(),
|
||||||
segment: u256_saturating_cast_usize(segment),
|
segment: segment.as_usize(),
|
||||||
virt: u256_saturating_cast_usize(virt),
|
virt: virt.as_usize(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +93,25 @@ impl MemoryOp {
|
|||||||
value,
|
value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_dummy_read(address: MemoryAddress, timestamp: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
filter: false,
|
||||||
|
timestamp,
|
||||||
|
address,
|
||||||
|
kind: MemoryOpKind::Read,
|
||||||
|
value: U256::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sorting_key(&self) -> (usize, usize, usize, usize) {
|
||||||
|
(
|
||||||
|
self.address.context,
|
||||||
|
self.address.segment,
|
||||||
|
self.address.virt,
|
||||||
|
self.timestamp,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -117,10 +142,27 @@ impl MemoryState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, address: MemoryAddress) -> U256 {
|
pub fn get(&self, address: MemoryAddress) -> U256 {
|
||||||
self.contexts[address.context].segments[address.segment].get(address.virt)
|
let segment = Segment::all()[address.segment];
|
||||||
|
let val = self.contexts[address.context].segments[address.segment].get(address.virt);
|
||||||
|
assert!(
|
||||||
|
val.bits() <= segment.bit_range(),
|
||||||
|
"Value {} exceeds {:?} range of {} bits",
|
||||||
|
val,
|
||||||
|
segment,
|
||||||
|
segment.bit_range()
|
||||||
|
);
|
||||||
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, address: MemoryAddress, val: U256) {
|
pub fn set(&mut self, address: MemoryAddress, val: U256) {
|
||||||
|
let segment = Segment::all()[address.segment];
|
||||||
|
assert!(
|
||||||
|
val.bits() <= segment.bit_range(),
|
||||||
|
"Value {} exceeds {:?} range of {} bits",
|
||||||
|
val,
|
||||||
|
segment,
|
||||||
|
segment.bit_range()
|
||||||
|
);
|
||||||
self.contexts[address.context].segments[address.segment].set(address.virt, val);
|
self.contexts[address.context].segments[address.segment].set(address.virt, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,11 +5,11 @@ use plonky2::field::types::Field;
|
|||||||
|
|
||||||
use crate::cpu::columns::CpuColumnsView;
|
use crate::cpu::columns::CpuColumnsView;
|
||||||
use crate::cpu::kernel::aggregator::KERNEL;
|
use crate::cpu::kernel::aggregator::KERNEL;
|
||||||
|
use crate::cpu::kernel::assembler::BYTES_PER_OFFSET;
|
||||||
use crate::cpu::membus::NUM_GP_CHANNELS;
|
use crate::cpu::membus::NUM_GP_CHANNELS;
|
||||||
use crate::cpu::simple_logic::eq_iszero::generate_pinv_diff;
|
use crate::cpu::simple_logic::eq_iszero::generate_pinv_diff;
|
||||||
use crate::generation::state::GenerationState;
|
use crate::generation::state::GenerationState;
|
||||||
use crate::memory::segments::Segment;
|
use crate::memory::segments::Segment;
|
||||||
use crate::util::u256_saturating_cast_usize;
|
|
||||||
use crate::witness::errors::ProgramError;
|
use crate::witness::errors::ProgramError;
|
||||||
use crate::witness::memory::MemoryAddress;
|
use crate::witness::memory::MemoryAddress;
|
||||||
use crate::witness::util::{
|
use crate::witness::util::{
|
||||||
@ -20,9 +20,6 @@ use crate::{arithmetic, logic};
|
|||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub(crate) enum Operation {
|
pub(crate) enum Operation {
|
||||||
Push(u8),
|
|
||||||
Dup(u8),
|
|
||||||
Swap(u8),
|
|
||||||
Iszero,
|
Iszero,
|
||||||
Not,
|
Not,
|
||||||
Byte,
|
Byte,
|
||||||
@ -39,6 +36,9 @@ pub(crate) enum Operation {
|
|||||||
Pc,
|
Pc,
|
||||||
Gas,
|
Gas,
|
||||||
Jumpdest,
|
Jumpdest,
|
||||||
|
Push(u8),
|
||||||
|
Dup(u8),
|
||||||
|
Swap(u8),
|
||||||
GetContext,
|
GetContext,
|
||||||
SetContext,
|
SetContext,
|
||||||
ConsumeGas,
|
ConsumeGas,
|
||||||
@ -186,11 +186,37 @@ pub(crate) fn generate_jump<F: Field>(
|
|||||||
mut row: CpuColumnsView<F>,
|
mut row: CpuColumnsView<F>,
|
||||||
) -> Result<(), ProgramError> {
|
) -> Result<(), ProgramError> {
|
||||||
let [(dst, log_in0)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?;
|
let [(dst, log_in0)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?;
|
||||||
|
let dst: u32 = dst
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| ProgramError::InvalidJumpDestination)?;
|
||||||
|
|
||||||
|
let (jumpdest_bit, jumpdest_bit_log) = mem_read_gp_with_log_and_fill(
|
||||||
|
NUM_GP_CHANNELS - 1,
|
||||||
|
MemoryAddress::new(state.registers.context, Segment::JumpdestBits, dst as usize),
|
||||||
|
state,
|
||||||
|
&mut row,
|
||||||
|
);
|
||||||
|
if state.registers.is_kernel {
|
||||||
|
// Don't actually do the read, just set the address, etc.
|
||||||
|
let mut channel = &mut row.mem_channels[NUM_GP_CHANNELS - 1];
|
||||||
|
channel.used = F::ZERO;
|
||||||
|
channel.value[0] = F::ONE;
|
||||||
|
|
||||||
|
row.mem_channels[1].value[0] = F::ONE;
|
||||||
|
} else {
|
||||||
|
if jumpdest_bit != U256::one() {
|
||||||
|
return Err(ProgramError::InvalidJumpDestination);
|
||||||
|
}
|
||||||
|
state.traces.push_memory(jumpdest_bit_log);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extra fields required by the constraints.
|
||||||
|
row.general.jumps_mut().should_jump = F::ONE;
|
||||||
|
row.general.jumps_mut().cond_sum_pinv = F::ONE;
|
||||||
|
|
||||||
state.traces.push_memory(log_in0);
|
state.traces.push_memory(log_in0);
|
||||||
state.traces.push_cpu(row);
|
state.traces.push_cpu(row);
|
||||||
state.registers.program_counter = u256_saturating_cast_usize(dst);
|
state.registers.program_counter = dst as usize;
|
||||||
// TODO: Set other cols like input0_upper_sum_inv.
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,15 +226,92 @@ pub(crate) fn generate_jumpi<F: Field>(
|
|||||||
) -> Result<(), ProgramError> {
|
) -> Result<(), ProgramError> {
|
||||||
let [(dst, log_in0), (cond, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?;
|
let [(dst, log_in0), (cond, log_in1)] = stack_pop_with_log_and_fill::<2, _>(state, &mut row)?;
|
||||||
|
|
||||||
|
let should_jump = !cond.is_zero();
|
||||||
|
if should_jump {
|
||||||
|
row.general.jumps_mut().should_jump = F::ONE;
|
||||||
|
let cond_sum_u64 = cond
|
||||||
|
.0
|
||||||
|
.into_iter()
|
||||||
|
.map(|limb| ((limb as u32) as u64) + (limb >> 32))
|
||||||
|
.sum();
|
||||||
|
let cond_sum = F::from_canonical_u64(cond_sum_u64);
|
||||||
|
row.general.jumps_mut().cond_sum_pinv = cond_sum.inverse();
|
||||||
|
|
||||||
|
let dst: u32 = dst
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| ProgramError::InvalidJumpiDestination)?;
|
||||||
|
state.registers.program_counter = dst as usize;
|
||||||
|
} else {
|
||||||
|
row.general.jumps_mut().should_jump = F::ZERO;
|
||||||
|
row.general.jumps_mut().cond_sum_pinv = F::ZERO;
|
||||||
|
state.registers.program_counter += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (jumpdest_bit, jumpdest_bit_log) = mem_read_gp_with_log_and_fill(
|
||||||
|
NUM_GP_CHANNELS - 1,
|
||||||
|
MemoryAddress::new(
|
||||||
|
state.registers.context,
|
||||||
|
Segment::JumpdestBits,
|
||||||
|
dst.low_u32() as usize,
|
||||||
|
),
|
||||||
|
state,
|
||||||
|
&mut row,
|
||||||
|
);
|
||||||
|
if !should_jump || state.registers.is_kernel {
|
||||||
|
// Don't actually do the read, just set the address, etc.
|
||||||
|
let mut channel = &mut row.mem_channels[NUM_GP_CHANNELS - 1];
|
||||||
|
channel.used = F::ZERO;
|
||||||
|
channel.value[0] = F::ONE;
|
||||||
|
} else {
|
||||||
|
if jumpdest_bit != U256::one() {
|
||||||
|
return Err(ProgramError::InvalidJumpiDestination);
|
||||||
|
}
|
||||||
|
state.traces.push_memory(jumpdest_bit_log);
|
||||||
|
}
|
||||||
|
|
||||||
state.traces.push_memory(log_in0);
|
state.traces.push_memory(log_in0);
|
||||||
state.traces.push_memory(log_in1);
|
state.traces.push_memory(log_in1);
|
||||||
state.traces.push_cpu(row);
|
state.traces.push_cpu(row);
|
||||||
state.registers.program_counter = if cond.is_zero() {
|
Ok(())
|
||||||
state.registers.program_counter + 1
|
}
|
||||||
} else {
|
|
||||||
u256_saturating_cast_usize(dst)
|
pub(crate) fn generate_pc<F: Field>(
|
||||||
};
|
state: &mut GenerationState<F>,
|
||||||
// TODO: Set other cols like input0_upper_sum_inv.
|
mut row: CpuColumnsView<F>,
|
||||||
|
) -> Result<(), ProgramError> {
|
||||||
|
let write = stack_push_log_and_fill(state, &mut row, state.registers.program_counter.into())?;
|
||||||
|
state.traces.push_memory(write);
|
||||||
|
state.traces.push_cpu(row);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generate_jumpdest<F: Field>(
|
||||||
|
state: &mut GenerationState<F>,
|
||||||
|
row: CpuColumnsView<F>,
|
||||||
|
) -> Result<(), ProgramError> {
|
||||||
|
state.traces.push_cpu(row);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generate_get_context<F: Field>(
|
||||||
|
state: &mut GenerationState<F>,
|
||||||
|
mut row: CpuColumnsView<F>,
|
||||||
|
) -> Result<(), ProgramError> {
|
||||||
|
let ctx = state.registers.context.into();
|
||||||
|
let write = stack_push_log_and_fill(state, &mut row, ctx)?;
|
||||||
|
state.traces.push_memory(write);
|
||||||
|
state.traces.push_cpu(row);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generate_set_context<F: Field>(
|
||||||
|
state: &mut GenerationState<F>,
|
||||||
|
mut row: CpuColumnsView<F>,
|
||||||
|
) -> Result<(), ProgramError> {
|
||||||
|
let [(ctx, log_in)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?;
|
||||||
|
state.registers.context = ctx.as_usize();
|
||||||
|
state.traces.push_memory(log_in);
|
||||||
|
state.traces.push_cpu(row);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,7 +489,9 @@ pub(crate) fn generate_syscall<F: Field>(
|
|||||||
mut row: CpuColumnsView<F>,
|
mut row: CpuColumnsView<F>,
|
||||||
) -> Result<(), ProgramError> {
|
) -> Result<(), ProgramError> {
|
||||||
let handler_jumptable_addr = KERNEL.global_labels["syscall_jumptable"];
|
let handler_jumptable_addr = KERNEL.global_labels["syscall_jumptable"];
|
||||||
let handler_addr_addr = handler_jumptable_addr + (opcode as usize);
|
let handler_addr_addr =
|
||||||
|
handler_jumptable_addr + (opcode as usize) * (BYTES_PER_OFFSET as usize);
|
||||||
|
assert_eq!(BYTES_PER_OFFSET, 3, "Code below assumes 3 bytes per offset");
|
||||||
let (handler_addr0, log_in0) = mem_read_gp_with_log_and_fill(
|
let (handler_addr0, log_in0) = mem_read_gp_with_log_and_fill(
|
||||||
0,
|
0,
|
||||||
MemoryAddress::new(0, Segment::Code, handler_addr_addr),
|
MemoryAddress::new(0, Segment::Code, handler_addr_addr),
|
||||||
@ -409,11 +514,12 @@ pub(crate) fn generate_syscall<F: Field>(
|
|||||||
let handler_addr = (handler_addr0 << 16) + (handler_addr1 << 8) + handler_addr2;
|
let handler_addr = (handler_addr0 << 16) + (handler_addr1 << 8) + handler_addr2;
|
||||||
let new_program_counter = handler_addr.as_usize();
|
let new_program_counter = handler_addr.as_usize();
|
||||||
|
|
||||||
let syscall_info = U256::from(state.registers.program_counter)
|
let syscall_info = U256::from(state.registers.program_counter + 1)
|
||||||
+ (U256::from(u64::from(state.registers.is_kernel)) << 32);
|
+ (U256::from(u64::from(state.registers.is_kernel)) << 32);
|
||||||
let log_out = stack_push_log_and_fill(state, &mut row, syscall_info)?;
|
let log_out = stack_push_log_and_fill(state, &mut row, syscall_info)?;
|
||||||
|
|
||||||
state.registers.program_counter = new_program_counter;
|
state.registers.program_counter = new_program_counter;
|
||||||
|
log::debug!("Syscall to {}", KERNEL.offset_name(new_program_counter));
|
||||||
state.registers.is_kernel = true;
|
state.registers.is_kernel = true;
|
||||||
|
|
||||||
state.traces.push_memory(log_in0);
|
state.traces.push_memory(log_in0);
|
||||||
@ -448,14 +554,19 @@ pub(crate) fn generate_exit_kernel<F: Field>(
|
|||||||
mut row: CpuColumnsView<F>,
|
mut row: CpuColumnsView<F>,
|
||||||
) -> Result<(), ProgramError> {
|
) -> Result<(), ProgramError> {
|
||||||
let [(kexit_info, log_in)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?;
|
let [(kexit_info, log_in)] = stack_pop_with_log_and_fill::<1, _>(state, &mut row)?;
|
||||||
let kexit_info_u64: [u64; 4] = kexit_info.0;
|
let kexit_info_u64 = kexit_info.0[0];
|
||||||
let program_counter = kexit_info_u64[0] as usize;
|
let program_counter = kexit_info_u64 as u32 as usize;
|
||||||
let is_kernel_mode_val = (kexit_info_u64[1] >> 32) as u32;
|
let is_kernel_mode_val = (kexit_info_u64 >> 32) as u32;
|
||||||
assert!(is_kernel_mode_val == 0 || is_kernel_mode_val == 1);
|
assert!(is_kernel_mode_val == 0 || is_kernel_mode_val == 1);
|
||||||
let is_kernel_mode = is_kernel_mode_val != 0;
|
let is_kernel_mode = is_kernel_mode_val != 0;
|
||||||
|
|
||||||
state.registers.program_counter = program_counter;
|
state.registers.program_counter = program_counter;
|
||||||
state.registers.is_kernel = is_kernel_mode;
|
state.registers.is_kernel = is_kernel_mode;
|
||||||
|
log::debug!(
|
||||||
|
"Exiting to {}, is_kernel={}",
|
||||||
|
KERNEL.offset_name(program_counter),
|
||||||
|
is_kernel_mode
|
||||||
|
);
|
||||||
|
|
||||||
state.traces.push_memory(log_in);
|
state.traces.push_memory(log_in);
|
||||||
state.traces.push_cpu(row);
|
state.traces.push_cpu(row);
|
||||||
@ -470,14 +581,19 @@ pub(crate) fn generate_mload_general<F: Field>(
|
|||||||
let [(context, log_in0), (segment, log_in1), (virt, log_in2)] =
|
let [(context, log_in0), (segment, log_in1), (virt, log_in2)] =
|
||||||
stack_pop_with_log_and_fill::<3, _>(state, &mut row)?;
|
stack_pop_with_log_and_fill::<3, _>(state, &mut row)?;
|
||||||
|
|
||||||
let val = state
|
let (val, log_read) = mem_read_gp_with_log_and_fill(
|
||||||
.memory
|
3,
|
||||||
.get(MemoryAddress::new_u256s(context, segment, virt));
|
MemoryAddress::new_u256s(context, segment, virt),
|
||||||
|
state,
|
||||||
|
&mut row,
|
||||||
|
);
|
||||||
|
|
||||||
let log_out = stack_push_log_and_fill(state, &mut row, val)?;
|
let log_out = stack_push_log_and_fill(state, &mut row, val)?;
|
||||||
|
|
||||||
state.traces.push_memory(log_in0);
|
state.traces.push_memory(log_in0);
|
||||||
state.traces.push_memory(log_in1);
|
state.traces.push_memory(log_in1);
|
||||||
state.traces.push_memory(log_in2);
|
state.traces.push_memory(log_in2);
|
||||||
|
state.traces.push_memory(log_read);
|
||||||
state.traces.push_memory(log_out);
|
state.traces.push_memory(log_out);
|
||||||
state.traces.push_cpu(row);
|
state.traces.push_cpu(row);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use itertools::Itertools;
|
|||||||
use plonky2::field::extension::Extendable;
|
use plonky2::field::extension::Extendable;
|
||||||
use plonky2::field::polynomial::PolynomialValues;
|
use plonky2::field::polynomial::PolynomialValues;
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
|
use plonky2::timed;
|
||||||
use plonky2::util::timing::TimingTree;
|
use plonky2::util::timing::TimingTree;
|
||||||
|
|
||||||
use crate::all_stark::{AllStark, NUM_TABLES};
|
use crate::all_stark::{AllStark, NUM_TABLES};
|
||||||
@ -131,18 +132,32 @@ impl<T: Copy> Traces<T> {
|
|||||||
|
|
||||||
let cpu_rows = cpu.into_iter().map(|x| x.into()).collect();
|
let cpu_rows = cpu.into_iter().map(|x| x.into()).collect();
|
||||||
let cpu_trace = trace_rows_to_poly_values(cpu_rows);
|
let cpu_trace = trace_rows_to_poly_values(cpu_rows);
|
||||||
let keccak_trace =
|
let keccak_trace = timed!(
|
||||||
|
timing,
|
||||||
|
"generate Keccak trace",
|
||||||
all_stark
|
all_stark
|
||||||
.keccak_stark
|
.keccak_stark
|
||||||
.generate_trace(keccak_inputs, cap_elements, timing);
|
.generate_trace(keccak_inputs, cap_elements, timing)
|
||||||
let keccak_sponge_trace =
|
);
|
||||||
|
let keccak_sponge_trace = timed!(
|
||||||
|
timing,
|
||||||
|
"generate Keccak sponge trace",
|
||||||
all_stark
|
all_stark
|
||||||
.keccak_sponge_stark
|
.keccak_sponge_stark
|
||||||
.generate_trace(keccak_sponge_ops, cap_elements, timing);
|
.generate_trace(keccak_sponge_ops, cap_elements, timing)
|
||||||
let logic_trace = all_stark
|
);
|
||||||
.logic_stark
|
let logic_trace = timed!(
|
||||||
.generate_trace(logic_ops, cap_elements, timing);
|
timing,
|
||||||
let memory_trace = all_stark.memory_stark.generate_trace(memory_ops, timing);
|
"generate logic trace",
|
||||||
|
all_stark
|
||||||
|
.logic_stark
|
||||||
|
.generate_trace(logic_ops, cap_elements, timing)
|
||||||
|
);
|
||||||
|
let memory_trace = timed!(
|
||||||
|
timing,
|
||||||
|
"generate memory trace",
|
||||||
|
all_stark.memory_stark.generate_trace(memory_ops, timing)
|
||||||
|
);
|
||||||
|
|
||||||
[
|
[
|
||||||
cpu_trace,
|
cpu_trace,
|
||||||
|
|||||||
@ -113,6 +113,10 @@ fn decode(registers: RegistersState, opcode: u8) -> Result<Operation, ProgramErr
|
|||||||
(0xa2, _) => Ok(Operation::Syscall(opcode)),
|
(0xa2, _) => Ok(Operation::Syscall(opcode)),
|
||||||
(0xa3, _) => Ok(Operation::Syscall(opcode)),
|
(0xa3, _) => Ok(Operation::Syscall(opcode)),
|
||||||
(0xa4, _) => Ok(Operation::Syscall(opcode)),
|
(0xa4, _) => Ok(Operation::Syscall(opcode)),
|
||||||
|
(0xa5, _) => panic!(
|
||||||
|
"Kernel panic at {}",
|
||||||
|
KERNEL.offset_name(registers.program_counter)
|
||||||
|
),
|
||||||
(0xf0, _) => Ok(Operation::Syscall(opcode)),
|
(0xf0, _) => Ok(Operation::Syscall(opcode)),
|
||||||
(0xf1, _) => Ok(Operation::Syscall(opcode)),
|
(0xf1, _) => Ok(Operation::Syscall(opcode)),
|
||||||
(0xf2, _) => Ok(Operation::Syscall(opcode)),
|
(0xf2, _) => Ok(Operation::Syscall(opcode)),
|
||||||
@ -128,7 +132,10 @@ fn decode(registers: RegistersState, opcode: u8) -> Result<Operation, ProgramErr
|
|||||||
(0xfc, true) => Ok(Operation::MstoreGeneral),
|
(0xfc, true) => Ok(Operation::MstoreGeneral),
|
||||||
(0xfd, _) => Ok(Operation::Syscall(opcode)),
|
(0xfd, _) => Ok(Operation::Syscall(opcode)),
|
||||||
(0xff, _) => Ok(Operation::Syscall(opcode)),
|
(0xff, _) => Ok(Operation::Syscall(opcode)),
|
||||||
_ => Err(ProgramError::InvalidOpcode),
|
_ => {
|
||||||
|
log::warn!("Invalid opcode: {}", opcode);
|
||||||
|
Err(ProgramError::InvalidOpcode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,11 +208,11 @@ fn perform_op<F: Field>(
|
|||||||
Operation::Pop => generate_pop(state, row)?,
|
Operation::Pop => generate_pop(state, row)?,
|
||||||
Operation::Jump => generate_jump(state, row)?,
|
Operation::Jump => generate_jump(state, row)?,
|
||||||
Operation::Jumpi => generate_jumpi(state, row)?,
|
Operation::Jumpi => generate_jumpi(state, row)?,
|
||||||
Operation::Pc => todo!(),
|
Operation::Pc => generate_pc(state, row)?,
|
||||||
Operation::Gas => todo!(),
|
Operation::Gas => todo!(),
|
||||||
Operation::Jumpdest => todo!(),
|
Operation::Jumpdest => generate_jumpdest(state, row)?,
|
||||||
Operation::GetContext => todo!(),
|
Operation::GetContext => generate_get_context(state, row)?,
|
||||||
Operation::SetContext => todo!(),
|
Operation::SetContext => generate_set_context(state, row)?,
|
||||||
Operation::ConsumeGas => todo!(),
|
Operation::ConsumeGas => todo!(),
|
||||||
Operation::ExitKernel => generate_exit_kernel(state, row)?,
|
Operation::ExitKernel => generate_exit_kernel(state, row)?,
|
||||||
Operation::MloadGeneral => generate_mload_general(state, row)?,
|
Operation::MloadGeneral => generate_mload_general(state, row)?,
|
||||||
@ -219,12 +226,6 @@ fn perform_op<F: Field>(
|
|||||||
_ => 1,
|
_ => 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(label) = KERNEL.offset_label(state.registers.program_counter) {
|
|
||||||
if !label.starts_with("halt_pc") {
|
|
||||||
log::debug!("At {label}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,19 +240,39 @@ fn try_perform_instruction<F: Field>(state: &mut GenerationState<F>) -> Result<(
|
|||||||
|
|
||||||
let opcode = read_code_memory(state, &mut row);
|
let opcode = read_code_memory(state, &mut row);
|
||||||
let op = decode(state.registers, opcode)?;
|
let op = decode(state.registers, opcode)?;
|
||||||
let pc = state.registers.program_counter;
|
|
||||||
|
|
||||||
log::trace!("\nCycle {}", state.traces.clock());
|
log_instruction(state, op);
|
||||||
log::trace!(
|
|
||||||
"Stack: {:?}",
|
fill_op_flag(op, &mut row);
|
||||||
|
|
||||||
|
perform_op(state, op, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log_instruction<F: Field>(state: &mut GenerationState<F>, op: Operation) {
|
||||||
|
let pc = state.registers.program_counter;
|
||||||
|
let is_interesting_offset = KERNEL
|
||||||
|
.offset_label(pc)
|
||||||
|
.filter(|label| !label.starts_with("halt_pc"))
|
||||||
|
.is_some();
|
||||||
|
let level = if is_interesting_offset {
|
||||||
|
log::Level::Debug
|
||||||
|
} else {
|
||||||
|
log::Level::Trace
|
||||||
|
};
|
||||||
|
log::log!(
|
||||||
|
level,
|
||||||
|
"Cycle {}, pc={}, instruction={:?}, stack={:?}",
|
||||||
|
state.traces.clock(),
|
||||||
|
KERNEL.offset_name(pc),
|
||||||
|
op,
|
||||||
(0..state.registers.stack_len)
|
(0..state.registers.stack_len)
|
||||||
.map(|i| stack_peek(state, i).unwrap())
|
.map(|i| stack_peek(state, i).unwrap())
|
||||||
.collect_vec()
|
.collect_vec()
|
||||||
);
|
);
|
||||||
log::trace!("Executing {:?} at {}", op, KERNEL.offset_name(pc));
|
|
||||||
fill_op_flag(op, &mut row);
|
|
||||||
|
|
||||||
perform_op(state, op, row)
|
if state.registers.is_kernel && pc >= KERNEL.code.len() {
|
||||||
|
panic!("Kernel PC is out of range: {}", pc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_error<F: Field>(_state: &mut GenerationState<F>) {
|
fn handle_error<F: Field>(_state: &mut GenerationState<F>) {
|
||||||
@ -270,7 +291,8 @@ pub(crate) fn transition<F: Field>(state: &mut GenerationState<F>) {
|
|||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if state.registers.is_kernel {
|
if state.registers.is_kernel {
|
||||||
panic!("exception in kernel mode: {:?}", e);
|
let offset_name = KERNEL.offset_name(state.registers.program_counter);
|
||||||
|
panic!("exception in kernel mode at {}: {:?}", offset_name, e);
|
||||||
}
|
}
|
||||||
state.rollback(checkpoint);
|
state.rollback(checkpoint);
|
||||||
handle_error(state)
|
handle_error(state)
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV};
|
use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV};
|
||||||
use eth_trie_utils::partial_trie::PartialTrie;
|
use eth_trie_utils::partial_trie::PartialTrie;
|
||||||
@ -49,7 +50,7 @@ fn test_empty_txn_list() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
let mut timing = TimingTree::new("prove", log::Level::Debug);
|
let mut timing = TimingTree::new("prove", log::Level::Debug);
|
||||||
let proof = prove::<F, C, D>(&all_stark, &config, inputs, &mut timing)?;
|
let proof = prove::<F, C, D>(&all_stark, &config, inputs, &mut timing)?;
|
||||||
timing.print();
|
timing.filter(Duration::from_millis(100)).print();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
proof.public_values.trie_roots_before.state_root,
|
proof.public_values.trie_roots_before.state_root,
|
||||||
@ -80,5 +81,5 @@ fn test_empty_txn_list() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn init_logger() {
|
fn init_logger() {
|
||||||
let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "debug"));
|
let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info"));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,17 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use eth_trie_utils::partial_trie::PartialTrie;
|
use env_logger::{try_init_from_env, Env, DEFAULT_FILTER_ENV};
|
||||||
|
use eth_trie_utils::partial_trie::{Nibbles, PartialTrie};
|
||||||
|
use ethereum_types::U256;
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
|
use keccak_hash::keccak;
|
||||||
use plonky2::field::goldilocks_field::GoldilocksField;
|
use plonky2::field::goldilocks_field::GoldilocksField;
|
||||||
use plonky2::plonk::config::PoseidonGoldilocksConfig;
|
use plonky2::plonk::config::PoseidonGoldilocksConfig;
|
||||||
use plonky2::util::timing::TimingTree;
|
use plonky2::util::timing::TimingTree;
|
||||||
use plonky2_evm::all_stark::AllStark;
|
use plonky2_evm::all_stark::AllStark;
|
||||||
use plonky2_evm::config::StarkConfig;
|
use plonky2_evm::config::StarkConfig;
|
||||||
|
use plonky2_evm::generation::mpt::AccountRlp;
|
||||||
use plonky2_evm::generation::{GenerationInputs, TrieInputs};
|
use plonky2_evm::generation::{GenerationInputs, TrieInputs};
|
||||||
use plonky2_evm::proof::BlockMetadata;
|
use plonky2_evm::proof::BlockMetadata;
|
||||||
use plonky2_evm::prover::prove;
|
use plonky2_evm::prover::prove;
|
||||||
@ -18,28 +23,95 @@ type C = PoseidonGoldilocksConfig;
|
|||||||
|
|
||||||
/// Test a simple token transfer to a new address.
|
/// Test a simple token transfer to a new address.
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore] // TODO: Won't work until txn parsing, storage, etc. are implemented.
|
|
||||||
fn test_simple_transfer() -> anyhow::Result<()> {
|
fn test_simple_transfer() -> anyhow::Result<()> {
|
||||||
|
init_logger();
|
||||||
|
|
||||||
let all_stark = AllStark::<F, D>::default();
|
let all_stark = AllStark::<F, D>::default();
|
||||||
let config = StarkConfig::standard_fast_config();
|
let config = StarkConfig::standard_fast_config();
|
||||||
|
|
||||||
let block_metadata = BlockMetadata::default();
|
let sender = hex!("2c7536e3605d9c16a7a3d7b1898e529396a65c23");
|
||||||
|
let to = hex!("a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0");
|
||||||
|
let sender_state_key = keccak(sender);
|
||||||
|
let to_state_key = keccak(to);
|
||||||
|
let sender_nibbles = Nibbles::from(sender_state_key);
|
||||||
|
let to_nibbles = Nibbles::from(to_state_key);
|
||||||
|
let value = U256::from(100u32);
|
||||||
|
|
||||||
let txn = hex!("f85f050a82520894000000000000000000000000000000000000000064801ca0fa56df5d988638fad8798e5ef75a1e1125dc7fb55d2ac4bce25776a63f0c2967a02cb47a5579eb5f83a1cabe4662501c0059f1b58e60ef839a1b0da67af6b9fb38");
|
let sender_account_before = AccountRlp {
|
||||||
|
nonce: 5.into(),
|
||||||
|
balance: eth_to_wei(100_000.into()),
|
||||||
|
storage_root: PartialTrie::Empty.calc_hash(),
|
||||||
|
code_hash: keccak([]),
|
||||||
|
};
|
||||||
|
|
||||||
|
let state_trie_before = PartialTrie::Leaf {
|
||||||
|
nibbles: sender_nibbles,
|
||||||
|
value: rlp::encode(&sender_account_before).to_vec(),
|
||||||
|
};
|
||||||
|
let tries_before = TrieInputs {
|
||||||
|
state_trie: state_trie_before,
|
||||||
|
transactions_trie: PartialTrie::Empty,
|
||||||
|
receipts_trie: PartialTrie::Empty,
|
||||||
|
storage_tries: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generated using a little py-evm script.
|
||||||
|
let txn = hex!("f861050a8255f094a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0648242421ba02c89eb757d9deeb1f5b3859a9d4d679951ef610ac47ad4608dc142beb1b7e313a05af7e9fbab825455d36c36c7f4cfcafbeafa9a77bdff936b52afb36d4fe4bcdd");
|
||||||
|
|
||||||
|
let block_metadata = BlockMetadata::default();
|
||||||
|
|
||||||
let inputs = GenerationInputs {
|
let inputs = GenerationInputs {
|
||||||
signed_txns: vec![txn.to_vec()],
|
signed_txns: vec![txn.to_vec()],
|
||||||
tries: TrieInputs {
|
tries: tries_before,
|
||||||
state_trie: PartialTrie::Empty,
|
|
||||||
transactions_trie: PartialTrie::Empty,
|
|
||||||
receipts_trie: PartialTrie::Empty,
|
|
||||||
storage_tries: vec![],
|
|
||||||
},
|
|
||||||
contract_code: HashMap::new(),
|
contract_code: HashMap::new(),
|
||||||
block_metadata,
|
block_metadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
let proof = prove::<F, C, D>(&all_stark, &config, inputs, &mut TimingTree::default())?;
|
let mut timing = TimingTree::new("prove", log::Level::Debug);
|
||||||
|
let proof = prove::<F, C, D>(&all_stark, &config, inputs, &mut timing)?;
|
||||||
|
timing.filter(Duration::from_millis(100)).print();
|
||||||
|
|
||||||
|
let expected_state_trie_after = {
|
||||||
|
let sender_account_after = AccountRlp {
|
||||||
|
balance: sender_account_before.balance - value, // TODO: Also subtract gas_used * price.
|
||||||
|
// nonce: sender_account_before.nonce + 1, // TODO
|
||||||
|
..sender_account_before
|
||||||
|
};
|
||||||
|
let to_account_after = AccountRlp {
|
||||||
|
balance: value,
|
||||||
|
..AccountRlp::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut children = std::array::from_fn(|_| PartialTrie::Empty.into());
|
||||||
|
children[sender_nibbles.get_nibble(0) as usize] = PartialTrie::Leaf {
|
||||||
|
nibbles: sender_nibbles.truncate_n_nibbles_front(1),
|
||||||
|
value: rlp::encode(&sender_account_after).to_vec(),
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
children[to_nibbles.get_nibble(0) as usize] = PartialTrie::Leaf {
|
||||||
|
nibbles: to_nibbles.truncate_n_nibbles_front(1),
|
||||||
|
value: rlp::encode(&to_account_after).to_vec(),
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
PartialTrie::Branch {
|
||||||
|
children,
|
||||||
|
value: vec![],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
proof.public_values.trie_roots_after.state_root,
|
||||||
|
expected_state_trie_after.calc_hash()
|
||||||
|
);
|
||||||
|
|
||||||
verify_proof(all_stark, proof, &config)
|
verify_proof(all_stark, proof, &config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn eth_to_wei(eth: U256) -> U256 {
|
||||||
|
// 1 ether = 10^18 wei.
|
||||||
|
eth * U256::from(10).pow(18.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_logger() {
|
||||||
|
let _ = try_init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info"));
|
||||||
|
}
|
||||||
|
|||||||
@ -14,7 +14,7 @@ use plonky2::iop::ext_target::ExtensionTarget;
|
|||||||
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use plonky2::iop::target::Target;
|
use plonky2::iop::target::Target;
|
||||||
use plonky2::iop::wire::Wire;
|
use plonky2::iop::wire::Wire;
|
||||||
use plonky2::iop::witness::{PartitionWitness, Witness};
|
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
use plonky2::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ use log::{info, Level, LevelFilter};
|
|||||||
use maybe_rayon::rayon;
|
use maybe_rayon::rayon;
|
||||||
use plonky2::gates::noop::NoopGate;
|
use plonky2::gates::noop::NoopGate;
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
use plonky2::iop::witness::{PartialWitness, Witness};
|
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::{
|
use plonky2::plonk::circuit_data::{
|
||||||
CircuitConfig, CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData,
|
CircuitConfig, CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use plonky2::iop::witness::{PartialWitness, Witness};
|
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||||
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||||
@ -35,7 +35,7 @@ fn main() -> Result<()> {
|
|||||||
let proof = data.prove(pw)?;
|
let proof = data.prove(pw)?;
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"Factorial starting at {} is {}!",
|
"Factorial starting at {} is {}",
|
||||||
proof.public_inputs[0], proof.public_inputs[1]
|
proof.public_inputs[0], proof.public_inputs[1]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use plonky2::iop::witness::{PartialWitness, Witness};
|
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||||
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use plonky2::field::types::{PrimeField, Sample};
|
|||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
|
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||||
use plonky2::iop::target::Target;
|
use plonky2::iop::target::Target;
|
||||||
use plonky2::iop::witness::{PartialWitness, PartitionWitness, Witness};
|
use plonky2::iop::witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||||
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use itertools::Itertools;
|
|||||||
use crate::field::extension::Extendable;
|
use crate::field::extension::Extendable;
|
||||||
use crate::fri::proof::{FriProof, FriProofTarget};
|
use crate::fri::proof::{FriProof, FriProofTarget};
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
use crate::iop::witness::Witness;
|
use crate::iop::witness::WitnessWrite;
|
||||||
use crate::plonk::config::AlgebraicHasher;
|
use crate::plonk::config::AlgebraicHasher;
|
||||||
|
|
||||||
/// Set the targets in a `FriProofTarget` to their corresponding values in a `FriProof`.
|
/// Set the targets in a `FriProofTarget` to their corresponding values in a `FriProof`.
|
||||||
@ -13,7 +13,7 @@ pub fn set_fri_proof_target<F, W, H, const D: usize>(
|
|||||||
fri_proof: &FriProof<F, H, D>,
|
fri_proof: &FriProof<F, H, D>,
|
||||||
) where
|
) where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
W: Witness<F> + ?Sized,
|
W: WitnessWrite<F> + ?Sized,
|
||||||
H: AlgebraicHasher<F>,
|
H: AlgebraicHasher<F>,
|
||||||
{
|
{
|
||||||
witness.set_target(fri_proof_target.pow_witness, fri_proof.pow_witness);
|
witness.set_target(fri_proof_target.pow_witness, fri_proof.pow_witness);
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use crate::gates::exponentiation::ExponentiationGate;
|
|||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||||
use crate::iop::target::{BoolTarget, Target};
|
use crate::iop::target::{BoolTarget, Target};
|
||||||
use crate::iop::witness::{PartitionWitness, Witness};
|
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||||
|
|||||||
@ -10,7 +10,7 @@ use crate::hash::hash_types::RichField;
|
|||||||
use crate::iop::ext_target::{ExtensionAlgebraTarget, ExtensionTarget};
|
use crate::iop::ext_target::{ExtensionAlgebraTarget, ExtensionTarget};
|
||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
use crate::iop::witness::{PartitionWitness, Witness};
|
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::util::bits_u64;
|
use crate::util::bits_u64;
|
||||||
|
|
||||||
@ -573,7 +573,7 @@ mod tests {
|
|||||||
use crate::field::extension::algebra::ExtensionAlgebra;
|
use crate::field::extension::algebra::ExtensionAlgebra;
|
||||||
use crate::field::types::Sample;
|
use crate::field::types::Sample;
|
||||||
use crate::iop::ext_target::ExtensionAlgebraTarget;
|
use crate::iop::ext_target::ExtensionAlgebraTarget;
|
||||||
use crate::iop::witness::{PartialWitness, Witness};
|
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, KeccakGoldilocksConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, KeccakGoldilocksConfig, PoseidonGoldilocksConfig};
|
||||||
@ -588,7 +588,7 @@ mod tests {
|
|||||||
|
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
|
|
||||||
let mut pw = PartialWitness::new();
|
let mut pw = PartialWitness::<F>::new();
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||||
|
|
||||||
let vs = FF::rand_vec(3);
|
let vs = FF::rand_vec(3);
|
||||||
|
|||||||
@ -5,7 +5,7 @@ use crate::field::extension::Extendable;
|
|||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||||
use crate::iop::target::{BoolTarget, Target};
|
use crate::iop::target::{BoolTarget, Target};
|
||||||
use crate::iop::witness::{PartitionWitness, Witness};
|
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||||
|
|||||||
@ -41,7 +41,7 @@ mod tests {
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::field::types::Sample;
|
use crate::field::types::Sample;
|
||||||
use crate::iop::witness::{PartialWitness, Witness};
|
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||||
@ -54,7 +54,7 @@ mod tests {
|
|||||||
type F = <C as GenericConfig<D>>::F;
|
type F = <C as GenericConfig<D>>::F;
|
||||||
type FF = <C as GenericConfig<D>>::FE;
|
type FF = <C as GenericConfig<D>>::FE;
|
||||||
let config = CircuitConfig::standard_recursion_config();
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
let mut pw = PartialWitness::new();
|
let mut pw = PartialWitness::<F>::new();
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||||
|
|
||||||
let (x, y) = (FF::rand(), FF::rand());
|
let (x, y) = (FF::rand(), FF::rand());
|
||||||
|
|||||||
@ -10,7 +10,7 @@ use crate::gates::base_sum::BaseSumGate;
|
|||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||||
use crate::iop::target::{BoolTarget, Target};
|
use crate::iop::target::{BoolTarget, Target};
|
||||||
use crate::iop::witness::{PartitionWitness, Witness};
|
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::util::log_floor;
|
use crate::util::log_floor;
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use crate::gates::base_sum::BaseSumGate;
|
|||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||||
use crate::iop::target::{BoolTarget, Target};
|
use crate::iop::target::{BoolTarget, Target};
|
||||||
use crate::iop::witness::{PartitionWitness, Witness};
|
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::util::ceil_div_usize;
|
use crate::util::ceil_div_usize;
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,7 @@ use crate::hash::hash_types::RichField;
|
|||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
use crate::iop::witness::{PartitionWitness, Witness};
|
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::vars::{
|
use crate::plonk::vars::{
|
||||||
|
|||||||
@ -11,7 +11,7 @@ use crate::hash::hash_types::RichField;
|
|||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
use crate::iop::witness::{PartitionWitness, Witness};
|
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||||
|
|||||||
@ -14,7 +14,7 @@ use crate::hash::hash_types::RichField;
|
|||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
use crate::iop::witness::{PartitionWitness, Witness};
|
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_circuit};
|
use crate::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_circuit};
|
||||||
|
|||||||
@ -16,7 +16,7 @@ use crate::iop::ext_target::ExtensionTarget;
|
|||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
use crate::iop::wire::Wire;
|
use crate::iop::wire::Wire;
|
||||||
use crate::iop::witness::{PartitionWitness, Witness};
|
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::vars::{
|
use crate::plonk::vars::{
|
||||||
|
|||||||
@ -8,7 +8,7 @@ use crate::field::polynomial::{PolynomialCoeffs, PolynomialValues};
|
|||||||
use crate::field::types::{Field, Sample};
|
use crate::field::types::{Field, Sample};
|
||||||
use crate::gates::gate::Gate;
|
use crate::gates::gate::Gate;
|
||||||
use crate::hash::hash_types::{HashOut, RichField};
|
use crate::hash::hash_types::{HashOut, RichField};
|
||||||
use crate::iop::witness::{PartialWitness, Witness};
|
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::GenericConfig;
|
use crate::plonk::config::GenericConfig;
|
||||||
|
|||||||
@ -18,7 +18,7 @@ use crate::iop::ext_target::ExtensionTarget;
|
|||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
use crate::iop::wire::Wire;
|
use crate::iop::wire::Wire;
|
||||||
use crate::iop::witness::{PartitionWitness, Witness};
|
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@ use crate::iop::ext_target::ExtensionTarget;
|
|||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
use crate::iop::wire::Wire;
|
use crate::iop::wire::Wire;
|
||||||
use crate::iop::witness::{PartitionWitness, Witness};
|
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ use crate::hash::hash_types::RichField;
|
|||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
use crate::iop::witness::{PartitionWitness, Witness};
|
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||||
|
|||||||
@ -17,7 +17,7 @@ use crate::iop::ext_target::ExtensionTarget;
|
|||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
use crate::iop::wire::Wire;
|
use crate::iop::wire::Wire;
|
||||||
use crate::iop::witness::{PartitionWitness, Witness};
|
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||||
|
|
||||||
@ -514,7 +514,7 @@ mod tests {
|
|||||||
use crate::hash::poseidon::Poseidon;
|
use crate::hash::poseidon::Poseidon;
|
||||||
use crate::iop::generator::generate_partial_witness;
|
use crate::iop::generator::generate_partial_witness;
|
||||||
use crate::iop::wire::Wire;
|
use crate::iop::wire::Wire;
|
||||||
use crate::iop::witness::{PartialWitness, Witness};
|
use crate::iop::witness::{PartialWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||||
|
|||||||
@ -16,7 +16,7 @@ use crate::hash::poseidon::Poseidon;
|
|||||||
use crate::iop::ext_target::{ExtensionAlgebraTarget, ExtensionTarget};
|
use crate::iop::ext_target::{ExtensionAlgebraTarget, ExtensionTarget};
|
||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
use crate::iop::witness::{PartitionWitness, Witness};
|
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ use crate::iop::ext_target::ExtensionTarget;
|
|||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
use crate::iop::wire::Wire;
|
use crate::iop::wire::Wire;
|
||||||
use crate::iop::witness::{PartitionWitness, Witness};
|
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::vars::{
|
use crate::plonk::vars::{
|
||||||
|
|||||||
@ -11,7 +11,7 @@ use crate::hash::hash_types::RichField;
|
|||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
use crate::iop::witness::{PartitionWitness, Witness};
|
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ use crate::hash::hash_types::RichField;
|
|||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use crate::iop::target::Target;
|
use crate::iop::target::Target;
|
||||||
use crate::iop::witness::{PartitionWitness, Witness};
|
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ use alloc::vec;
|
|||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
use anyhow::{ensure, Result};
|
use anyhow::{ensure, Result};
|
||||||
|
use itertools::Itertools;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::field::extension::Extendable;
|
use crate::field::extension::Extendable;
|
||||||
@ -145,6 +146,12 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
self.connect(x.elements[i], y.elements[i]);
|
self.connect(x.elements[i], y.elements[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn connect_merkle_caps(&mut self, x: &MerkleCapTarget, y: &MerkleCapTarget) {
|
||||||
|
for (h0, h1) in x.0.iter().zip_eq(&y.0) {
|
||||||
|
self.connect_hashes(*h0, *h1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -156,7 +163,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::field::types::Field;
|
use crate::field::types::Field;
|
||||||
use crate::hash::merkle_tree::MerkleTree;
|
use crate::hash::merkle_tree::MerkleTree;
|
||||||
use crate::iop::witness::{PartialWitness, Witness};
|
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||||
|
|||||||
@ -3,13 +3,13 @@ use alloc::vec::Vec;
|
|||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use crate::field::extension::{Extendable, FieldExtension};
|
use crate::field::extension::Extendable;
|
||||||
use crate::field::types::Field;
|
use crate::field::types::Field;
|
||||||
use crate::hash::hash_types::{HashOut, HashOutTarget, RichField};
|
use crate::hash::hash_types::RichField;
|
||||||
use crate::iop::ext_target::ExtensionTarget;
|
use crate::iop::ext_target::ExtensionTarget;
|
||||||
use crate::iop::target::{BoolTarget, Target};
|
use crate::iop::target::Target;
|
||||||
use crate::iop::wire::Wire;
|
use crate::iop::wire::Wire;
|
||||||
use crate::iop::witness::{PartialWitness, PartitionWitness, Witness};
|
use crate::iop::witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite};
|
||||||
use crate::plonk::circuit_data::{CommonCircuitData, ProverOnlyCircuitData};
|
use crate::plonk::circuit_data::{CommonCircuitData, ProverOnlyCircuitData};
|
||||||
use crate::plonk::config::GenericConfig;
|
use crate::plonk::config::GenericConfig;
|
||||||
|
|
||||||
@ -120,6 +120,12 @@ impl<F: Field> From<Vec<(Target, F)>> for GeneratedValues<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<F: Field> WitnessWrite<F> for GeneratedValues<F> {
|
||||||
|
fn set_target(&mut self, target: Target, value: F) {
|
||||||
|
self.target_values.push((target, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<F: Field> GeneratedValues<F> {
|
impl<F: Field> GeneratedValues<F> {
|
||||||
pub fn with_capacity(capacity: usize) -> Self {
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
Vec::with_capacity(capacity).into()
|
Vec::with_capacity(capacity).into()
|
||||||
@ -137,10 +143,6 @@ impl<F: Field> GeneratedValues<F> {
|
|||||||
vec![(target, value)].into()
|
vec![(target, value)].into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.target_values.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn singleton_extension_target<const D: usize>(
|
pub fn singleton_extension_target<const D: usize>(
|
||||||
et: ExtensionTarget<D>,
|
et: ExtensionTarget<D>,
|
||||||
value: F::Extension,
|
value: F::Extension,
|
||||||
@ -152,56 +154,6 @@ impl<F: Field> GeneratedValues<F> {
|
|||||||
witness.set_extension_target(et, value);
|
witness.set_extension_target(et, value);
|
||||||
witness
|
witness
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_target(&mut self, target: Target, value: F) {
|
|
||||||
self.target_values.push((target, value))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_bool_target(&mut self, target: BoolTarget, value: bool) {
|
|
||||||
self.set_target(target.target, F::from_bool(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_hash_target(&mut self, ht: HashOutTarget, value: HashOut<F>) {
|
|
||||||
ht.elements
|
|
||||||
.iter()
|
|
||||||
.zip(value.elements)
|
|
||||||
.for_each(|(&t, x)| self.set_target(t, x));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_extension_target<const D: usize>(
|
|
||||||
&mut self,
|
|
||||||
et: ExtensionTarget<D>,
|
|
||||||
value: F::Extension,
|
|
||||||
) where
|
|
||||||
F: RichField + Extendable<D>,
|
|
||||||
{
|
|
||||||
let limbs = value.to_basefield_array();
|
|
||||||
(0..D).for_each(|i| {
|
|
||||||
self.set_target(et.0[i], limbs[i]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_wire(&mut self, wire: Wire, value: F) {
|
|
||||||
self.set_target(Target::Wire(wire), value)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_wires<W>(&mut self, wires: W, values: &[F])
|
|
||||||
where
|
|
||||||
W: IntoIterator<Item = Wire>,
|
|
||||||
{
|
|
||||||
// If we used itertools, we could use zip_eq for extra safety.
|
|
||||||
for (wire, &value) in wires.into_iter().zip(values) {
|
|
||||||
self.set_wire(wire, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_ext_wires<W, const D: usize>(&mut self, wires: W, value: F::Extension)
|
|
||||||
where
|
|
||||||
F: RichField + Extendable<D>,
|
|
||||||
W: IntoIterator<Item = Wire>,
|
|
||||||
{
|
|
||||||
self.set_wires(wires, &value.to_basefield_array());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A generator which runs once after a list of dependencies is present in the witness.
|
/// A generator which runs once after a list of dependencies is present in the witness.
|
||||||
|
|||||||
@ -17,71 +17,9 @@ use crate::plonk::circuit_data::{VerifierCircuitTarget, VerifierOnlyCircuitData}
|
|||||||
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
|
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
|
||||||
use crate::plonk::proof::{Proof, ProofTarget, ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
use crate::plonk::proof::{Proof, ProofTarget, ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
||||||
|
|
||||||
/// A witness holds information on the values of targets in a circuit.
|
pub trait WitnessWrite<F: Field> {
|
||||||
pub trait Witness<F: Field> {
|
|
||||||
fn try_get_target(&self, target: Target) -> Option<F>;
|
|
||||||
|
|
||||||
fn set_target(&mut self, target: Target, value: F);
|
fn set_target(&mut self, target: Target, value: F);
|
||||||
|
|
||||||
fn get_target(&self, target: Target) -> F {
|
|
||||||
self.try_get_target(target).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_targets(&self, targets: &[Target]) -> Vec<F> {
|
|
||||||
targets.iter().map(|&t| self.get_target(t)).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_extension_target<const D: usize>(&self, et: ExtensionTarget<D>) -> F::Extension
|
|
||||||
where
|
|
||||||
F: RichField + Extendable<D>,
|
|
||||||
{
|
|
||||||
F::Extension::from_basefield_array(
|
|
||||||
self.get_targets(&et.to_target_array()).try_into().unwrap(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_extension_targets<const D: usize>(&self, ets: &[ExtensionTarget<D>]) -> Vec<F::Extension>
|
|
||||||
where
|
|
||||||
F: RichField + Extendable<D>,
|
|
||||||
{
|
|
||||||
ets.iter()
|
|
||||||
.map(|&et| self.get_extension_target(et))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_bool_target(&self, target: BoolTarget) -> bool {
|
|
||||||
let value = self.get_target(target.target);
|
|
||||||
if value.is_zero() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if value.is_one() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
panic!("not a bool")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_hash_target(&self, ht: HashOutTarget) -> HashOut<F> {
|
|
||||||
HashOut {
|
|
||||||
elements: self.get_targets(&ht.elements).try_into().unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_wire(&self, wire: Wire) -> F {
|
|
||||||
self.get_target(Target::Wire(wire))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_get_wire(&self, wire: Wire) -> Option<F> {
|
|
||||||
self.try_get_target(Target::Wire(wire))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn contains(&self, target: Target) -> bool {
|
|
||||||
self.try_get_target(target).is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn contains_all(&self, targets: &[Target]) -> bool {
|
|
||||||
targets.iter().all(|&t| self.contains(t))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_hash_target(&mut self, ht: HashOutTarget, value: HashOut<F>) {
|
fn set_hash_target(&mut self, ht: HashOutTarget, value: HashOut<F>) {
|
||||||
ht.elements
|
ht.elements
|
||||||
.iter()
|
.iter()
|
||||||
@ -239,6 +177,70 @@ pub trait Witness<F: Field> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A witness holds information on the values of targets in a circuit.
|
||||||
|
pub trait Witness<F: Field>: WitnessWrite<F> {
|
||||||
|
fn try_get_target(&self, target: Target) -> Option<F>;
|
||||||
|
|
||||||
|
fn get_target(&self, target: Target) -> F {
|
||||||
|
self.try_get_target(target).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_targets(&self, targets: &[Target]) -> Vec<F> {
|
||||||
|
targets.iter().map(|&t| self.get_target(t)).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_extension_target<const D: usize>(&self, et: ExtensionTarget<D>) -> F::Extension
|
||||||
|
where
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
{
|
||||||
|
F::Extension::from_basefield_array(
|
||||||
|
self.get_targets(&et.to_target_array()).try_into().unwrap(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_extension_targets<const D: usize>(&self, ets: &[ExtensionTarget<D>]) -> Vec<F::Extension>
|
||||||
|
where
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
{
|
||||||
|
ets.iter()
|
||||||
|
.map(|&et| self.get_extension_target(et))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_bool_target(&self, target: BoolTarget) -> bool {
|
||||||
|
let value = self.get_target(target.target);
|
||||||
|
if value.is_zero() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if value.is_one() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
panic!("not a bool")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_hash_target(&self, ht: HashOutTarget) -> HashOut<F> {
|
||||||
|
HashOut {
|
||||||
|
elements: self.get_targets(&ht.elements).try_into().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_wire(&self, wire: Wire) -> F {
|
||||||
|
self.get_target(Target::Wire(wire))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_get_wire(&self, wire: Wire) -> Option<F> {
|
||||||
|
self.try_get_target(Target::Wire(wire))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains(&self, target: Target) -> bool {
|
||||||
|
self.try_get_target(target).is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_all(&self, targets: &[Target]) -> bool {
|
||||||
|
targets.iter().all(|&t| self.contains(t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MatrixWitness<F: Field> {
|
pub struct MatrixWitness<F: Field> {
|
||||||
pub(crate) wire_values: Vec<Vec<F>>,
|
pub(crate) wire_values: Vec<Vec<F>>,
|
||||||
@ -263,23 +265,25 @@ impl<F: Field> PartialWitness<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Field> Witness<F> for PartialWitness<F> {
|
impl<F: Field> WitnessWrite<F> for PartialWitness<F> {
|
||||||
fn try_get_target(&self, target: Target) -> Option<F> {
|
|
||||||
self.target_values.get(&target).copied()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_target(&mut self, target: Target, value: F) {
|
fn set_target(&mut self, target: Target, value: F) {
|
||||||
let opt_old_value = self.target_values.insert(target, value);
|
let opt_old_value = self.target_values.insert(target, value);
|
||||||
if let Some(old_value) = opt_old_value {
|
if let Some(old_value) = opt_old_value {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
old_value, value,
|
value, old_value,
|
||||||
"Target {:?} was set twice with different values",
|
"Target {:?} was set twice with different values: {} != {}",
|
||||||
target
|
target, old_value, value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<F: Field> Witness<F> for PartialWitness<F> {
|
||||||
|
fn try_get_target(&self, target: Target) -> Option<F> {
|
||||||
|
self.target_values.get(&target).copied()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// `PartitionWitness` holds a disjoint-set forest of the targets respecting a circuit's copy constraints.
|
/// `PartitionWitness` holds a disjoint-set forest of the targets respecting a circuit's copy constraints.
|
||||||
/// The value of a target is defined to be the value of its root in the forest.
|
/// The value of a target is defined to be the value of its root in the forest.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -308,8 +312,8 @@ impl<'a, F: Field> PartitionWitness<'a, F> {
|
|||||||
if let Some(old_value) = *rep_value {
|
if let Some(old_value) = *rep_value {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
value, old_value,
|
value, old_value,
|
||||||
"Partition containing {:?} was set twice with different values",
|
"Partition containing {:?} was set twice with different values: {} != {}",
|
||||||
target
|
target, old_value, value
|
||||||
);
|
);
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@ -337,13 +341,15 @@ impl<'a, F: Field> PartitionWitness<'a, F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, F: Field> WitnessWrite<F> for PartitionWitness<'a, F> {
|
||||||
|
fn set_target(&mut self, target: Target, value: F) {
|
||||||
|
self.set_target_returning_rep(target, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, F: Field> Witness<F> for PartitionWitness<'a, F> {
|
impl<'a, F: Field> Witness<F> for PartitionWitness<'a, F> {
|
||||||
fn try_get_target(&self, target: Target) -> Option<F> {
|
fn try_get_target(&self, target: Target) -> Option<F> {
|
||||||
let rep_index = self.representative_map[self.target_index(target)];
|
let rep_index = self.representative_map[self.target_index(target)];
|
||||||
self.values[rep_index]
|
self.values[rep_index]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_target(&mut self, target: Target, value: F) {
|
|
||||||
self.set_target_returning_rep(target, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,7 @@ use crate::plonk::circuit_data::{
|
|||||||
CircuitConfig, CircuitData, CommonCircuitData, ProverCircuitData, ProverOnlyCircuitData,
|
CircuitConfig, CircuitData, CommonCircuitData, ProverCircuitData, ProverOnlyCircuitData,
|
||||||
VerifierCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData,
|
VerifierCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData,
|
||||||
};
|
};
|
||||||
use crate::plonk::config::{GenericConfig, GenericHashOut, Hasher};
|
use crate::plonk::config::{AlgebraicHasher, GenericConfig, GenericHashOut, Hasher};
|
||||||
use crate::plonk::copy_constraint::CopyConstraint;
|
use crate::plonk::copy_constraint::CopyConstraint;
|
||||||
use crate::plonk::permutation_argument::Forest;
|
use crate::plonk::permutation_argument::Forest;
|
||||||
use crate::plonk::plonk_common::PlonkOracle;
|
use crate::plonk::plonk_common::PlonkOracle;
|
||||||
@ -247,7 +247,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
|
|
||||||
/// Add a virtual verifier data, register it as a public input and set it to `self.verifier_data_public_input`.
|
/// Add a virtual verifier data, register it as a public input and set it to `self.verifier_data_public_input`.
|
||||||
/// WARNING: Do not register any public input after calling this! TODO: relax this
|
/// WARNING: Do not register any public input after calling this! TODO: relax this
|
||||||
pub fn add_verifier_data_public_inputs(&mut self) {
|
pub fn add_verifier_data_public_inputs(&mut self) -> VerifierCircuitTarget {
|
||||||
assert!(
|
assert!(
|
||||||
self.verifier_data_public_input.is_none(),
|
self.verifier_data_public_input.is_none(),
|
||||||
"add_verifier_data_public_inputs only needs to be called once"
|
"add_verifier_data_public_inputs only needs to be called once"
|
||||||
@ -263,7 +263,8 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
self.register_public_inputs(&verifier_data.constants_sigmas_cap.0[i].elements);
|
self.register_public_inputs(&verifier_data.constants_sigmas_cap.0[i].elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.verifier_data_public_input = Some(verifier_data);
|
self.verifier_data_public_input = Some(verifier_data.clone());
|
||||||
|
verifier_data
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a gate to the circuit, and returns its index.
|
/// Adds a gate to the circuit, and returns its index.
|
||||||
@ -436,6 +437,19 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
MerkleCapTarget(cap.0.iter().map(|h| self.constant_hash(*h)).collect())
|
MerkleCapTarget(cap.0.iter().map(|h| self.constant_hash(*h)).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn constant_verifier_data<C: GenericConfig<D, F = F>>(
|
||||||
|
&mut self,
|
||||||
|
verifier_data: &VerifierOnlyCircuitData<C, D>,
|
||||||
|
) -> VerifierCircuitTarget
|
||||||
|
where
|
||||||
|
C::Hasher: AlgebraicHasher<F>,
|
||||||
|
{
|
||||||
|
VerifierCircuitTarget {
|
||||||
|
constants_sigmas_cap: self.constant_merkle_cap(&verifier_data.constants_sigmas_cap),
|
||||||
|
circuit_digest: self.constant_hash(verifier_data.circuit_digest),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// If the given target is a constant (i.e. it was created by the `constant(F)` method), returns
|
/// If the given target is a constant (i.e. it was created by the `constant(F)` method), returns
|
||||||
/// its constant value. Otherwise, returns `None`.
|
/// its constant value. Otherwise, returns `None`.
|
||||||
pub fn target_as_constant(&self, target: Target) -> Option<F> {
|
pub fn target_as_constant(&self, target: Target) -> Option<F> {
|
||||||
|
|||||||
@ -470,7 +470,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CommonCircuitData<F, D> {
|
|||||||
/// is intentionally missing certain fields, such as `CircuitConfig`, because we support only a
|
/// is intentionally missing certain fields, such as `CircuitConfig`, because we support only a
|
||||||
/// limited form of dynamic inner circuits. We can't practically make things like the wire count
|
/// limited form of dynamic inner circuits. We can't practically make things like the wire count
|
||||||
/// dynamic, at least not without setting a maximum wire count and paying for the worst case.
|
/// dynamic, at least not without setting a maximum wire count and paying for the worst case.
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct VerifierCircuitTarget {
|
pub struct VerifierCircuitTarget {
|
||||||
/// A commitment to each constant polynomial and each permutation polynomial.
|
/// A commitment to each constant polynomial and each permutation polynomial.
|
||||||
pub constants_sigmas_cap: MerkleCapTarget,
|
pub constants_sigmas_cap: MerkleCapTarget,
|
||||||
|
|||||||
@ -31,6 +31,55 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
) where
|
) where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F>,
|
||||||
{
|
{
|
||||||
|
let selected_proof =
|
||||||
|
self.select_proof_with_pis(condition, proof_with_pis0, proof_with_pis1);
|
||||||
|
let selected_verifier_data = VerifierCircuitTarget {
|
||||||
|
constants_sigmas_cap: self.select_cap(
|
||||||
|
condition,
|
||||||
|
&inner_verifier_data0.constants_sigmas_cap,
|
||||||
|
&inner_verifier_data1.constants_sigmas_cap,
|
||||||
|
),
|
||||||
|
circuit_digest: self.select_hash(
|
||||||
|
condition,
|
||||||
|
inner_verifier_data0.circuit_digest,
|
||||||
|
inner_verifier_data1.circuit_digest,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.verify_proof::<C>(&selected_proof, &selected_verifier_data, inner_common_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Conditionally verify a proof with a new generated dummy proof.
|
||||||
|
pub fn conditionally_verify_proof_or_dummy<C: GenericConfig<D, F = F> + 'static>(
|
||||||
|
&mut self,
|
||||||
|
condition: BoolTarget,
|
||||||
|
proof_with_pis: &ProofWithPublicInputsTarget<D>,
|
||||||
|
inner_verifier_data: &VerifierCircuitTarget,
|
||||||
|
inner_common_data: &CommonCircuitData<F, D>,
|
||||||
|
) -> anyhow::Result<()>
|
||||||
|
where
|
||||||
|
C::Hasher: AlgebraicHasher<F>,
|
||||||
|
{
|
||||||
|
let (dummy_proof_with_pis_target, dummy_verifier_data_target) =
|
||||||
|
self.dummy_proof_and_vk::<C>(inner_common_data)?;
|
||||||
|
self.conditionally_verify_proof::<C>(
|
||||||
|
condition,
|
||||||
|
proof_with_pis,
|
||||||
|
inner_verifier_data,
|
||||||
|
&dummy_proof_with_pis_target,
|
||||||
|
&dummy_verifier_data_target,
|
||||||
|
inner_common_data,
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes `if b { proof_with_pis0 } else { proof_with_pis1 }`.
|
||||||
|
fn select_proof_with_pis(
|
||||||
|
&mut self,
|
||||||
|
b: BoolTarget,
|
||||||
|
proof_with_pis0: &ProofWithPublicInputsTarget<D>,
|
||||||
|
proof_with_pis1: &ProofWithPublicInputsTarget<D>,
|
||||||
|
) -> ProofWithPublicInputsTarget<D> {
|
||||||
let ProofWithPublicInputsTarget {
|
let ProofWithPublicInputsTarget {
|
||||||
proof:
|
proof:
|
||||||
ProofTarget {
|
ProofTarget {
|
||||||
@ -53,20 +102,19 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
},
|
},
|
||||||
public_inputs: public_inputs1,
|
public_inputs: public_inputs1,
|
||||||
} = proof_with_pis1;
|
} = proof_with_pis1;
|
||||||
|
with_context!(self, "select proof", {
|
||||||
let selected_proof = with_context!(self, "select proof", {
|
let selected_wires_cap = self.select_cap(b, wires_cap0, wires_cap1);
|
||||||
let selected_wires_cap = self.select_cap(condition, wires_cap0, wires_cap1);
|
|
||||||
let selected_plonk_zs_partial_products_cap = self.select_cap(
|
let selected_plonk_zs_partial_products_cap = self.select_cap(
|
||||||
condition,
|
b,
|
||||||
plonk_zs_partial_products_cap0,
|
plonk_zs_partial_products_cap0,
|
||||||
plonk_zs_partial_products_cap1,
|
plonk_zs_partial_products_cap1,
|
||||||
);
|
);
|
||||||
let selected_quotient_polys_cap =
|
let selected_quotient_polys_cap =
|
||||||
self.select_cap(condition, quotient_polys_cap0, quotient_polys_cap1);
|
self.select_cap(b, quotient_polys_cap0, quotient_polys_cap1);
|
||||||
let selected_openings = self.select_opening_set(condition, openings0, openings1);
|
let selected_openings = self.select_opening_set(b, openings0, openings1);
|
||||||
let selected_opening_proof =
|
let selected_opening_proof =
|
||||||
self.select_opening_proof(condition, opening_proof0, opening_proof1);
|
self.select_opening_proof(b, opening_proof0, opening_proof1);
|
||||||
let selected_public_inputs = self.select_vec(condition, public_inputs0, public_inputs1);
|
let selected_public_inputs = self.select_vec(b, public_inputs0, public_inputs1);
|
||||||
ProofWithPublicInputsTarget {
|
ProofWithPublicInputsTarget {
|
||||||
proof: ProofTarget {
|
proof: ProofTarget {
|
||||||
wires_cap: selected_wires_cap,
|
wires_cap: selected_wires_cap,
|
||||||
@ -77,52 +125,10 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
},
|
},
|
||||||
public_inputs: selected_public_inputs,
|
public_inputs: selected_public_inputs,
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
let selected_verifier_data = VerifierCircuitTarget {
|
|
||||||
constants_sigmas_cap: self.select_cap(
|
|
||||||
condition,
|
|
||||||
&inner_verifier_data0.constants_sigmas_cap,
|
|
||||||
&inner_verifier_data1.constants_sigmas_cap,
|
|
||||||
),
|
|
||||||
circuit_digest: self.select_hash(
|
|
||||||
condition,
|
|
||||||
inner_verifier_data0.circuit_digest,
|
|
||||||
inner_verifier_data1.circuit_digest,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.verify_proof::<C>(&selected_proof, &selected_verifier_data, inner_common_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Conditionally verify a proof with a new generated dummy proof.
|
|
||||||
pub fn conditionally_verify_proof_or_dummy<C: GenericConfig<D, F = F>>(
|
|
||||||
&mut self,
|
|
||||||
condition: BoolTarget,
|
|
||||||
proof_with_pis: &ProofWithPublicInputsTarget<D>,
|
|
||||||
inner_verifier_data: &VerifierCircuitTarget,
|
|
||||||
inner_common_data: &CommonCircuitData<F, D>,
|
|
||||||
) -> (ProofWithPublicInputsTarget<D>, VerifierCircuitTarget)
|
|
||||||
where
|
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
|
||||||
{
|
|
||||||
let dummy_proof = self.add_virtual_proof_with_pis::<C>(inner_common_data);
|
|
||||||
let dummy_verifier_data = VerifierCircuitTarget {
|
|
||||||
constants_sigmas_cap: self
|
|
||||||
.add_virtual_cap(inner_common_data.config.fri_config.cap_height),
|
|
||||||
circuit_digest: self.add_virtual_hash(),
|
|
||||||
};
|
|
||||||
self.conditionally_verify_proof::<C>(
|
|
||||||
condition,
|
|
||||||
proof_with_pis,
|
|
||||||
inner_verifier_data,
|
|
||||||
&dummy_proof,
|
|
||||||
&dummy_verifier_data,
|
|
||||||
inner_common_data,
|
|
||||||
);
|
|
||||||
|
|
||||||
(dummy_proof, dummy_verifier_data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes `if b { v0 } else { v1 }`.
|
||||||
fn select_vec(&mut self, b: BoolTarget, v0: &[Target], v1: &[Target]) -> Vec<Target> {
|
fn select_vec(&mut self, b: BoolTarget, v0: &[Target], v1: &[Target]) -> Vec<Target> {
|
||||||
v0.iter()
|
v0.iter()
|
||||||
.zip_eq(v1)
|
.zip_eq(v1)
|
||||||
@ -130,6 +136,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes `if b { h0 } else { h1 }`.
|
||||||
pub(crate) fn select_hash(
|
pub(crate) fn select_hash(
|
||||||
&mut self,
|
&mut self,
|
||||||
b: BoolTarget,
|
b: BoolTarget,
|
||||||
@ -141,6 +148,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes `if b { cap0 } else { cap1 }`.
|
||||||
fn select_cap(
|
fn select_cap(
|
||||||
&mut self,
|
&mut self,
|
||||||
b: BoolTarget,
|
b: BoolTarget,
|
||||||
@ -157,6 +165,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes `if b { v0 } else { v1 }`.
|
||||||
fn select_vec_cap(
|
fn select_vec_cap(
|
||||||
&mut self,
|
&mut self,
|
||||||
b: BoolTarget,
|
b: BoolTarget,
|
||||||
@ -169,6 +178,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes `if b { os0 } else { os1 }`.
|
||||||
fn select_opening_set(
|
fn select_opening_set(
|
||||||
&mut self,
|
&mut self,
|
||||||
b: BoolTarget,
|
b: BoolTarget,
|
||||||
@ -186,6 +196,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes `if b { v0 } else { v1 }`.
|
||||||
fn select_vec_ext(
|
fn select_vec_ext(
|
||||||
&mut self,
|
&mut self,
|
||||||
b: BoolTarget,
|
b: BoolTarget,
|
||||||
@ -198,6 +209,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes `if b { proof0 } else { proof1 }`.
|
||||||
fn select_opening_proof(
|
fn select_opening_proof(
|
||||||
&mut self,
|
&mut self,
|
||||||
b: BoolTarget,
|
b: BoolTarget,
|
||||||
@ -224,6 +236,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes `if b { qr0 } else { qr1 }`.
|
||||||
fn select_query_round(
|
fn select_query_round(
|
||||||
&mut self,
|
&mut self,
|
||||||
b: BoolTarget,
|
b: BoolTarget,
|
||||||
@ -240,6 +253,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes `if b { v0 } else { v1 }`.
|
||||||
fn select_vec_query_round(
|
fn select_vec_query_round(
|
||||||
&mut self,
|
&mut self,
|
||||||
b: BoolTarget,
|
b: BoolTarget,
|
||||||
@ -252,6 +266,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes `if b { proof0 } else { proof1 }`.
|
||||||
fn select_initial_tree_proof(
|
fn select_initial_tree_proof(
|
||||||
&mut self,
|
&mut self,
|
||||||
b: BoolTarget,
|
b: BoolTarget,
|
||||||
@ -273,6 +288,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes `if b { proof0 } else { proof1 }`.
|
||||||
fn select_merkle_proof(
|
fn select_merkle_proof(
|
||||||
&mut self,
|
&mut self,
|
||||||
b: BoolTarget,
|
b: BoolTarget,
|
||||||
@ -289,6 +305,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes `if b { qs0 } else { qs01 }`.
|
||||||
fn select_query_step(
|
fn select_query_step(
|
||||||
&mut self,
|
&mut self,
|
||||||
b: BoolTarget,
|
b: BoolTarget,
|
||||||
@ -301,6 +318,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes `if b { v0 } else { v1 }`.
|
||||||
fn select_vec_query_step(
|
fn select_vec_query_step(
|
||||||
&mut self,
|
&mut self,
|
||||||
b: BoolTarget,
|
b: BoolTarget,
|
||||||
@ -322,7 +340,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::field::types::Sample;
|
use crate::field::types::Sample;
|
||||||
use crate::gates::noop::NoopGate;
|
use crate::gates::noop::NoopGate;
|
||||||
use crate::iop::witness::{PartialWitness, Witness};
|
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||||
use crate::recursion::dummy_circuit::{dummy_circuit, dummy_proof};
|
use crate::recursion::dummy_circuit::{dummy_circuit, dummy_proof};
|
||||||
|
|||||||
@ -1,48 +1,17 @@
|
|||||||
#![allow(clippy::int_plus_one)] // Makes more sense for some inequalities below.
|
#![allow(clippy::int_plus_one)] // Makes more sense for some inequalities below.
|
||||||
|
|
||||||
use alloc::vec;
|
|
||||||
|
|
||||||
use anyhow::{ensure, Result};
|
use anyhow::{ensure, Result};
|
||||||
use hashbrown::HashMap;
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use crate::field::extension::Extendable;
|
use crate::field::extension::Extendable;
|
||||||
use crate::gates::noop::NoopGate;
|
|
||||||
use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget, RichField};
|
use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget, RichField};
|
||||||
use crate::hash::merkle_tree::MerkleCap;
|
use crate::hash::merkle_tree::MerkleCap;
|
||||||
use crate::iop::target::{BoolTarget, Target};
|
use crate::iop::target::{BoolTarget, Target};
|
||||||
use crate::iop::witness::{PartialWitness, Witness};
|
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::{
|
use crate::plonk::circuit_data::{
|
||||||
CircuitData, CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData,
|
CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData,
|
||||||
};
|
};
|
||||||
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
|
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
|
||||||
use crate::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
use crate::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
||||||
use crate::recursion::dummy_circuit::{dummy_circuit, dummy_proof};
|
|
||||||
|
|
||||||
pub struct CyclicRecursionData<
|
|
||||||
'a,
|
|
||||||
F: RichField + Extendable<D>,
|
|
||||||
C: GenericConfig<D, F = F>,
|
|
||||||
const D: usize,
|
|
||||||
> {
|
|
||||||
proof: &'a Option<ProofWithPublicInputs<F, C, D>>,
|
|
||||||
verifier_data: &'a VerifierOnlyCircuitData<C, D>,
|
|
||||||
common_data: &'a CommonCircuitData<F, D>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CyclicRecursionTarget<F, C, const D: usize>
|
|
||||||
where
|
|
||||||
F: RichField + Extendable<D>,
|
|
||||||
C: GenericConfig<D, F = F>,
|
|
||||||
{
|
|
||||||
pub(crate) proof: ProofWithPublicInputsTarget<D>,
|
|
||||||
pub(crate) verifier_data: VerifierCircuitTarget,
|
|
||||||
pub(crate) dummy_proof: ProofWithPublicInputsTarget<D>,
|
|
||||||
pub(crate) dummy_verifier_data: VerifierCircuitTarget,
|
|
||||||
pub(crate) condition: BoolTarget,
|
|
||||||
pub(crate) dummy_circuit: CircuitData<F, C, D>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: GenericConfig<D>, const D: usize> VerifierOnlyCircuitData<C, D> {
|
impl<C: GenericConfig<D>, const D: usize> VerifierOnlyCircuitData<C, D> {
|
||||||
fn from_slice(slice: &[C::F], common_data: &CommonCircuitData<C::F, D>) -> Result<Self>
|
fn from_slice(slice: &[C::F], common_data: &CommonCircuitData<C::F, D>) -> Result<Self>
|
||||||
@ -98,7 +67,7 @@ impl VerifierCircuitTarget {
|
|||||||
|
|
||||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||||
/// If `condition` is true, recursively verify a proof for the same circuit as the one we're
|
/// If `condition` is true, recursively verify a proof for the same circuit as the one we're
|
||||||
/// currently building.
|
/// currently building. Otherwise, verify `other_proof_with_pis`.
|
||||||
///
|
///
|
||||||
/// For a typical IVC use case, `condition` will be false for the very first proof in a chain,
|
/// For a typical IVC use case, `condition` will be false for the very first proof in a chain,
|
||||||
/// i.e. the base case.
|
/// i.e. the base case.
|
||||||
@ -110,12 +79,14 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
/// that the verification key matches.
|
/// that the verification key matches.
|
||||||
///
|
///
|
||||||
/// WARNING: Do not register any public input after calling this! TODO: relax this
|
/// WARNING: Do not register any public input after calling this! TODO: relax this
|
||||||
pub fn cyclic_recursion<C: GenericConfig<D, F = F>>(
|
pub fn conditionally_verify_cyclic_proof<C: GenericConfig<D, F = F>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
condition: BoolTarget,
|
condition: BoolTarget,
|
||||||
proof_with_pis: &ProofWithPublicInputsTarget<D>,
|
cyclic_proof_with_pis: &ProofWithPublicInputsTarget<D>,
|
||||||
|
other_proof_with_pis: &ProofWithPublicInputsTarget<D>,
|
||||||
|
other_verifier_data: &VerifierCircuitTarget,
|
||||||
common_data: &CommonCircuitData<F, D>,
|
common_data: &CommonCircuitData<F, D>,
|
||||||
) -> Result<CyclicRecursionTarget<F, C, D>>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F>,
|
||||||
{
|
{
|
||||||
@ -123,131 +94,67 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
|||||||
.verifier_data_public_input
|
.verifier_data_public_input
|
||||||
.clone()
|
.clone()
|
||||||
.expect("Must call add_verifier_data_public_inputs before cyclic recursion");
|
.expect("Must call add_verifier_data_public_inputs before cyclic recursion");
|
||||||
self.goal_common_data = Some(common_data.clone());
|
|
||||||
|
|
||||||
let dummy_verifier_data = VerifierCircuitTarget {
|
if let Some(existing_common_data) = self.goal_common_data.as_ref() {
|
||||||
constants_sigmas_cap: self.add_virtual_cap(self.config.fri_config.cap_height),
|
assert_eq!(existing_common_data, common_data);
|
||||||
circuit_digest: self.add_virtual_hash(),
|
} else {
|
||||||
};
|
self.goal_common_data = Some(common_data.clone());
|
||||||
|
}
|
||||||
|
|
||||||
let dummy_proof = self.add_virtual_proof_with_pis::<C>(common_data);
|
let inner_cyclic_pis = VerifierCircuitTarget::from_slice::<F, C, D>(
|
||||||
|
&cyclic_proof_with_pis.public_inputs,
|
||||||
let pis = VerifierCircuitTarget::from_slice::<F, C, D>(
|
|
||||||
&proof_with_pis.public_inputs,
|
|
||||||
common_data,
|
common_data,
|
||||||
)?;
|
)?;
|
||||||
// Connect previous verifier data to current one. This guarantees that every proof in the cycle uses the same verifier data.
|
// Connect previous verifier data to current one. This guarantees that every proof in the cycle uses the same verifier data.
|
||||||
self.connect_hashes(pis.circuit_digest, verifier_data.circuit_digest);
|
self.connect_hashes(
|
||||||
for (h0, h1) in pis
|
inner_cyclic_pis.circuit_digest,
|
||||||
.constants_sigmas_cap
|
verifier_data.circuit_digest,
|
||||||
.0
|
);
|
||||||
.iter()
|
self.connect_merkle_caps(
|
||||||
.zip_eq(&verifier_data.constants_sigmas_cap.0)
|
&inner_cyclic_pis.constants_sigmas_cap,
|
||||||
{
|
&verifier_data.constants_sigmas_cap,
|
||||||
self.connect_hashes(*h0, *h1);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
// Verify the real proof if `condition` is set to true, otherwise verify the dummy proof.
|
// Verify the cyclic proof if `condition` is set to true, otherwise verify the other proof.
|
||||||
self.conditionally_verify_proof::<C>(
|
self.conditionally_verify_proof::<C>(
|
||||||
condition,
|
condition,
|
||||||
proof_with_pis,
|
cyclic_proof_with_pis,
|
||||||
&verifier_data,
|
&verifier_data,
|
||||||
&dummy_proof,
|
other_proof_with_pis,
|
||||||
&dummy_verifier_data,
|
other_verifier_data,
|
||||||
common_data,
|
common_data,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Make sure we have enough gates to match `common_data`.
|
|
||||||
while self.num_gates() < (common_data.degree() / 2) {
|
|
||||||
self.add_gate(NoopGate, vec![]);
|
|
||||||
}
|
|
||||||
// Make sure we have every gate to match `common_data`.
|
// Make sure we have every gate to match `common_data`.
|
||||||
for g in &common_data.gates {
|
for g in &common_data.gates {
|
||||||
self.add_gate_to_gate_set(g.clone());
|
self.add_gate_to_gate_set(g.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CyclicRecursionTarget {
|
Ok(())
|
||||||
proof: proof_with_pis.clone(),
|
}
|
||||||
verifier_data,
|
|
||||||
dummy_proof,
|
pub fn conditionally_verify_cyclic_proof_or_dummy<C: GenericConfig<D, F = F> + 'static>(
|
||||||
dummy_verifier_data,
|
&mut self,
|
||||||
|
condition: BoolTarget,
|
||||||
|
cyclic_proof_with_pis: &ProofWithPublicInputsTarget<D>,
|
||||||
|
common_data: &CommonCircuitData<F, D>,
|
||||||
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
C::Hasher: AlgebraicHasher<F>,
|
||||||
|
{
|
||||||
|
let (dummy_proof_with_pis_target, dummy_verifier_data_target) =
|
||||||
|
self.dummy_proof_and_vk::<C>(common_data)?;
|
||||||
|
self.conditionally_verify_cyclic_proof::<C>(
|
||||||
condition,
|
condition,
|
||||||
dummy_circuit: dummy_circuit(common_data),
|
cyclic_proof_with_pis,
|
||||||
})
|
&dummy_proof_with_pis_target,
|
||||||
|
&dummy_verifier_data_target,
|
||||||
|
common_data,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the targets in a `CyclicRecursionTarget` to their corresponding values in a `CyclicRecursionData`.
|
|
||||||
/// The `public_inputs` parameter let the caller specify certain public inputs (identified by their
|
|
||||||
/// indices) which should be given specific values. The rest will default to zero.
|
|
||||||
pub fn set_cyclic_recursion_data_target<
|
|
||||||
F: RichField + Extendable<D>,
|
|
||||||
C: GenericConfig<D, F = F>,
|
|
||||||
const D: usize,
|
|
||||||
>(
|
|
||||||
pw: &mut PartialWitness<F>,
|
|
||||||
cyclic_recursion_data_target: &CyclicRecursionTarget<F, C, D>,
|
|
||||||
cyclic_recursion_data: &CyclicRecursionData<F, C, D>,
|
|
||||||
// Public inputs to set in the base case to seed some initial data.
|
|
||||||
mut public_inputs: HashMap<usize, F>,
|
|
||||||
) -> Result<()>
|
|
||||||
where
|
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
|
||||||
{
|
|
||||||
if let Some(proof) = cyclic_recursion_data.proof {
|
|
||||||
pw.set_bool_target(cyclic_recursion_data_target.condition, true);
|
|
||||||
pw.set_proof_with_pis_target(&cyclic_recursion_data_target.proof, proof);
|
|
||||||
pw.set_verifier_data_target(
|
|
||||||
&cyclic_recursion_data_target.verifier_data,
|
|
||||||
cyclic_recursion_data.verifier_data,
|
|
||||||
);
|
|
||||||
pw.set_proof_with_pis_target(&cyclic_recursion_data_target.dummy_proof, proof);
|
|
||||||
pw.set_verifier_data_target(
|
|
||||||
&cyclic_recursion_data_target.dummy_verifier_data,
|
|
||||||
cyclic_recursion_data.verifier_data,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
pw.set_bool_target(cyclic_recursion_data_target.condition, false);
|
|
||||||
|
|
||||||
let pis_len = cyclic_recursion_data_target
|
|
||||||
.dummy_circuit
|
|
||||||
.common
|
|
||||||
.num_public_inputs;
|
|
||||||
let cap_elements = cyclic_recursion_data
|
|
||||||
.common_data
|
|
||||||
.config
|
|
||||||
.fri_config
|
|
||||||
.num_cap_elements();
|
|
||||||
let start_vk_pis = pis_len - 4 - 4 * cap_elements;
|
|
||||||
|
|
||||||
// The circuit checks that the verifier data is the same throughout the cycle, so
|
|
||||||
// we set the verifier data to the "real" verifier data even though it's unused in the base case.
|
|
||||||
let verifier_data = &cyclic_recursion_data.verifier_data;
|
|
||||||
public_inputs.extend((start_vk_pis..).zip(verifier_data.circuit_digest.elements));
|
|
||||||
|
|
||||||
for i in 0..cap_elements {
|
|
||||||
let start = start_vk_pis + 4 + 4 * i;
|
|
||||||
public_inputs.extend((start..).zip(verifier_data.constants_sigmas_cap.0[i].elements));
|
|
||||||
}
|
|
||||||
|
|
||||||
let proof = dummy_proof(&cyclic_recursion_data_target.dummy_circuit, public_inputs)?;
|
|
||||||
pw.set_proof_with_pis_target(&cyclic_recursion_data_target.proof, &proof);
|
|
||||||
pw.set_verifier_data_target(
|
|
||||||
&cyclic_recursion_data_target.verifier_data,
|
|
||||||
cyclic_recursion_data.verifier_data,
|
|
||||||
);
|
|
||||||
|
|
||||||
let dummy_p = dummy_proof(&cyclic_recursion_data_target.dummy_circuit, HashMap::new())?;
|
|
||||||
pw.set_proof_with_pis_target(&cyclic_recursion_data_target.dummy_proof, &dummy_p);
|
|
||||||
pw.set_verifier_data_target(
|
|
||||||
&cyclic_recursion_data_target.dummy_verifier_data,
|
|
||||||
&cyclic_recursion_data_target.dummy_circuit.verifier_only,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Additional checks to be performed on a cyclic recursive proof in addition to verifying the proof.
|
/// Additional checks to be performed on a cyclic recursive proof in addition to verifying the proof.
|
||||||
/// Checks that the purported verifier data in the public inputs match the real verifier data.
|
/// Checks that the purported verifier data in the public inputs match the real verifier data.
|
||||||
pub fn check_cyclic_proof_verifier_data<
|
pub fn check_cyclic_proof_verifier_data<
|
||||||
@ -272,7 +179,6 @@ where
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use hashbrown::HashMap;
|
|
||||||
|
|
||||||
use crate::field::extension::Extendable;
|
use crate::field::extension::Extendable;
|
||||||
use crate::field::types::{Field, PrimeField64};
|
use crate::field::types::{Field, PrimeField64};
|
||||||
@ -280,13 +186,12 @@ mod tests {
|
|||||||
use crate::hash::hash_types::{HashOutTarget, RichField};
|
use crate::hash::hash_types::{HashOutTarget, RichField};
|
||||||
use crate::hash::hashing::hash_n_to_hash_no_pad;
|
use crate::hash::hashing::hash_n_to_hash_no_pad;
|
||||||
use crate::hash::poseidon::{PoseidonHash, PoseidonPermutation};
|
use crate::hash::poseidon::{PoseidonHash, PoseidonPermutation};
|
||||||
use crate::iop::witness::PartialWitness;
|
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::{CircuitConfig, CommonCircuitData, VerifierCircuitTarget};
|
use crate::plonk::circuit_data::{CircuitConfig, CommonCircuitData, VerifierCircuitTarget};
|
||||||
use crate::plonk::config::{AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig};
|
||||||
use crate::recursion::cyclic_recursion::{
|
use crate::recursion::cyclic_recursion::check_cyclic_proof_verifier_data;
|
||||||
check_cyclic_proof_verifier_data, set_cyclic_recursion_data_target, CyclicRecursionData,
|
use crate::recursion::dummy_circuit::cyclic_base_proof;
|
||||||
};
|
|
||||||
|
|
||||||
// Generates `CommonCircuitData` usable for recursion.
|
// Generates `CommonCircuitData` usable for recursion.
|
||||||
fn common_data_for_recursion<
|
fn common_data_for_recursion<
|
||||||
@ -341,8 +246,8 @@ mod tests {
|
|||||||
let one = builder.one();
|
let one = builder.one();
|
||||||
|
|
||||||
// Circuit that computes a repeated hash.
|
// Circuit that computes a repeated hash.
|
||||||
let initial_hash = builder.add_virtual_hash();
|
let initial_hash_target = builder.add_virtual_hash();
|
||||||
builder.register_public_inputs(&initial_hash.elements);
|
builder.register_public_inputs(&initial_hash_target.elements);
|
||||||
let current_hash_in = builder.add_virtual_hash();
|
let current_hash_in = builder.add_virtual_hash();
|
||||||
let current_hash_out =
|
let current_hash_out =
|
||||||
builder.hash_n_to_hash_no_pad::<PoseidonHash>(current_hash_in.elements.to_vec());
|
builder.hash_n_to_hash_no_pad::<PoseidonHash>(current_hash_in.elements.to_vec());
|
||||||
@ -350,97 +255,84 @@ mod tests {
|
|||||||
let counter = builder.add_virtual_public_input();
|
let counter = builder.add_virtual_public_input();
|
||||||
|
|
||||||
let mut common_data = common_data_for_recursion::<F, C, D>();
|
let mut common_data = common_data_for_recursion::<F, C, D>();
|
||||||
builder.add_verifier_data_public_inputs();
|
let verifier_data_target = builder.add_verifier_data_public_inputs();
|
||||||
common_data.num_public_inputs = builder.num_public_inputs();
|
common_data.num_public_inputs = builder.num_public_inputs();
|
||||||
|
|
||||||
let condition = builder.add_virtual_bool_target_safe();
|
let condition = builder.add_virtual_bool_target_safe();
|
||||||
|
|
||||||
// Unpack inner proof's public inputs.
|
// Unpack inner proof's public inputs.
|
||||||
let inner_proof_with_pis = builder.add_virtual_proof_with_pis::<C>(&common_data);
|
let inner_cyclic_proof_with_pis = builder.add_virtual_proof_with_pis::<C>(&common_data);
|
||||||
let inner_pis = &inner_proof_with_pis.public_inputs;
|
let inner_cyclic_pis = &inner_cyclic_proof_with_pis.public_inputs;
|
||||||
let inner_initial_hash = HashOutTarget::try_from(&inner_pis[0..4]).unwrap();
|
let inner_cyclic_initial_hash = HashOutTarget::try_from(&inner_cyclic_pis[0..4]).unwrap();
|
||||||
let inner_latest_hash = HashOutTarget::try_from(&inner_pis[4..8]).unwrap();
|
let inner_cyclic_latest_hash = HashOutTarget::try_from(&inner_cyclic_pis[4..8]).unwrap();
|
||||||
let inner_counter = inner_pis[8];
|
let inner_cyclic_counter = inner_cyclic_pis[8];
|
||||||
|
|
||||||
// Connect our initial hash to that of our inner proof. (If there is no inner proof, the
|
// Connect our initial hash to that of our inner proof. (If there is no inner proof, the
|
||||||
// initial hash will be unconstrained, which is intentional.)
|
// initial hash will be unconstrained, which is intentional.)
|
||||||
builder.connect_hashes(initial_hash, inner_initial_hash);
|
builder.connect_hashes(initial_hash_target, inner_cyclic_initial_hash);
|
||||||
|
|
||||||
// The input hash is the previous hash output if we have an inner proof, or the initial hash
|
// The input hash is the previous hash output if we have an inner proof, or the initial hash
|
||||||
// if this is the base case.
|
// if this is the base case.
|
||||||
let actual_hash_in = builder.select_hash(condition, inner_latest_hash, initial_hash);
|
let actual_hash_in =
|
||||||
|
builder.select_hash(condition, inner_cyclic_latest_hash, initial_hash_target);
|
||||||
builder.connect_hashes(current_hash_in, actual_hash_in);
|
builder.connect_hashes(current_hash_in, actual_hash_in);
|
||||||
|
|
||||||
// Our chain length will be inner_counter + 1 if we have an inner proof, or 1 if not.
|
// Our chain length will be inner_counter + 1 if we have an inner proof, or 1 if not.
|
||||||
let new_counter = builder.mul_add(condition.target, inner_counter, one);
|
let new_counter = builder.mul_add(condition.target, inner_cyclic_counter, one);
|
||||||
builder.connect(counter, new_counter);
|
builder.connect(counter, new_counter);
|
||||||
|
|
||||||
let cyclic_data_target =
|
builder.conditionally_verify_cyclic_proof_or_dummy::<C>(
|
||||||
builder.cyclic_recursion::<C>(condition, &inner_proof_with_pis, &common_data)?;
|
condition,
|
||||||
|
&inner_cyclic_proof_with_pis,
|
||||||
|
&common_data,
|
||||||
|
)?;
|
||||||
|
|
||||||
let cyclic_circuit_data = builder.build::<C>();
|
let cyclic_circuit_data = builder.build::<C>();
|
||||||
|
|
||||||
let mut pw = PartialWitness::new();
|
let mut pw = PartialWitness::new();
|
||||||
let cyclic_recursion_data = CyclicRecursionData {
|
|
||||||
proof: &None, // Base case: We don't have a proof to put here yet.
|
|
||||||
verifier_data: &cyclic_circuit_data.verifier_only,
|
|
||||||
common_data: &cyclic_circuit_data.common,
|
|
||||||
};
|
|
||||||
let initial_hash = [F::ZERO, F::ONE, F::TWO, F::from_canonical_usize(3)];
|
let initial_hash = [F::ZERO, F::ONE, F::TWO, F::from_canonical_usize(3)];
|
||||||
let initial_hash_pis = initial_hash.into_iter().enumerate().collect();
|
let initial_hash_pis = initial_hash.into_iter().enumerate().collect();
|
||||||
set_cyclic_recursion_data_target(
|
pw.set_bool_target(condition, false);
|
||||||
&mut pw,
|
pw.set_proof_with_pis_target::<C, D>(
|
||||||
&cyclic_data_target,
|
&inner_cyclic_proof_with_pis,
|
||||||
&cyclic_recursion_data,
|
&cyclic_base_proof(
|
||||||
initial_hash_pis,
|
&common_data,
|
||||||
)?;
|
&cyclic_circuit_data.verifier_only,
|
||||||
|
initial_hash_pis,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
pw.set_verifier_data_target(&verifier_data_target, &cyclic_circuit_data.verifier_only);
|
||||||
let proof = cyclic_circuit_data.prove(pw)?;
|
let proof = cyclic_circuit_data.prove(pw)?;
|
||||||
check_cyclic_proof_verifier_data(
|
check_cyclic_proof_verifier_data(
|
||||||
&proof,
|
&proof,
|
||||||
cyclic_recursion_data.verifier_data,
|
&cyclic_circuit_data.verifier_only,
|
||||||
cyclic_recursion_data.common_data,
|
&cyclic_circuit_data.common,
|
||||||
)?;
|
)?;
|
||||||
cyclic_circuit_data.verify(proof.clone())?;
|
cyclic_circuit_data.verify(proof.clone())?;
|
||||||
|
|
||||||
// 1st recursive layer.
|
// 1st recursive layer.
|
||||||
let mut pw = PartialWitness::new();
|
let mut pw = PartialWitness::new();
|
||||||
let cyclic_recursion_data = CyclicRecursionData {
|
pw.set_bool_target(condition, true);
|
||||||
proof: &Some(proof), // Input previous proof.
|
pw.set_proof_with_pis_target(&inner_cyclic_proof_with_pis, &proof);
|
||||||
verifier_data: &cyclic_circuit_data.verifier_only,
|
pw.set_verifier_data_target(&verifier_data_target, &cyclic_circuit_data.verifier_only);
|
||||||
common_data: &cyclic_circuit_data.common,
|
|
||||||
};
|
|
||||||
set_cyclic_recursion_data_target(
|
|
||||||
&mut pw,
|
|
||||||
&cyclic_data_target,
|
|
||||||
&cyclic_recursion_data,
|
|
||||||
HashMap::new(),
|
|
||||||
)?;
|
|
||||||
let proof = cyclic_circuit_data.prove(pw)?;
|
let proof = cyclic_circuit_data.prove(pw)?;
|
||||||
check_cyclic_proof_verifier_data(
|
check_cyclic_proof_verifier_data(
|
||||||
&proof,
|
&proof,
|
||||||
cyclic_recursion_data.verifier_data,
|
&cyclic_circuit_data.verifier_only,
|
||||||
cyclic_recursion_data.common_data,
|
&cyclic_circuit_data.common,
|
||||||
)?;
|
)?;
|
||||||
cyclic_circuit_data.verify(proof.clone())?;
|
cyclic_circuit_data.verify(proof.clone())?;
|
||||||
|
|
||||||
// 2nd recursive layer.
|
// 2nd recursive layer.
|
||||||
let mut pw = PartialWitness::new();
|
let mut pw = PartialWitness::new();
|
||||||
let cyclic_recursion_data = CyclicRecursionData {
|
pw.set_bool_target(condition, true);
|
||||||
proof: &Some(proof), // Input previous proof.
|
pw.set_proof_with_pis_target(&inner_cyclic_proof_with_pis, &proof);
|
||||||
verifier_data: &cyclic_circuit_data.verifier_only,
|
pw.set_verifier_data_target(&verifier_data_target, &cyclic_circuit_data.verifier_only);
|
||||||
common_data: &cyclic_circuit_data.common,
|
|
||||||
};
|
|
||||||
set_cyclic_recursion_data_target(
|
|
||||||
&mut pw,
|
|
||||||
&cyclic_data_target,
|
|
||||||
&cyclic_recursion_data,
|
|
||||||
HashMap::new(),
|
|
||||||
)?;
|
|
||||||
let proof = cyclic_circuit_data.prove(pw)?;
|
let proof = cyclic_circuit_data.prove(pw)?;
|
||||||
check_cyclic_proof_verifier_data(
|
check_cyclic_proof_verifier_data(
|
||||||
&proof,
|
&proof,
|
||||||
cyclic_recursion_data.verifier_data,
|
&cyclic_circuit_data.verifier_only,
|
||||||
cyclic_recursion_data.common_data,
|
&cyclic_circuit_data.common,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Verify that the proof correctly computes a repeated hash.
|
// Verify that the proof correctly computes a repeated hash.
|
||||||
|
|||||||
@ -6,11 +6,47 @@ use plonky2_util::ceil_div_usize;
|
|||||||
|
|
||||||
use crate::gates::noop::NoopGate;
|
use crate::gates::noop::NoopGate;
|
||||||
use crate::hash::hash_types::RichField;
|
use crate::hash::hash_types::RichField;
|
||||||
use crate::iop::witness::{PartialWitness, Witness};
|
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||||
|
use crate::iop::target::Target;
|
||||||
|
use crate::iop::witness::{PartialWitness, PartitionWitness, WitnessWrite};
|
||||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||||
use crate::plonk::circuit_data::{CircuitData, CommonCircuitData};
|
use crate::plonk::circuit_data::{
|
||||||
use crate::plonk::config::GenericConfig;
|
CircuitData, CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData,
|
||||||
use crate::plonk::proof::ProofWithPublicInputs;
|
};
|
||||||
|
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
|
||||||
|
use crate::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
||||||
|
|
||||||
|
/// Creates a dummy proof which is suitable for use as a base proof in a cyclic recursion tree.
|
||||||
|
/// Such a base proof will not actually be verified, so most of its data is arbitrary. However, its
|
||||||
|
/// public inputs which encode the cyclic verification key must be set properly, and this method
|
||||||
|
/// takes care of that. It also allows the user to specify any other public inputs which should be
|
||||||
|
/// set in this base proof.
|
||||||
|
pub fn cyclic_base_proof<F, C, const D: usize>(
|
||||||
|
common_data: &CommonCircuitData<F, D>,
|
||||||
|
verifier_data: &VerifierOnlyCircuitData<C, D>,
|
||||||
|
mut nonzero_public_inputs: HashMap<usize, F>,
|
||||||
|
) -> ProofWithPublicInputs<F, C, D>
|
||||||
|
where
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
C: GenericConfig<D, F = F>,
|
||||||
|
C::Hasher: AlgebraicHasher<C::F>,
|
||||||
|
{
|
||||||
|
let pis_len = common_data.num_public_inputs;
|
||||||
|
let cap_elements = common_data.config.fri_config.num_cap_elements();
|
||||||
|
let start_vk_pis = pis_len - 4 - 4 * cap_elements;
|
||||||
|
|
||||||
|
// Add the cyclic verifier data public inputs.
|
||||||
|
nonzero_public_inputs.extend((start_vk_pis..).zip(verifier_data.circuit_digest.elements));
|
||||||
|
for i in 0..cap_elements {
|
||||||
|
let start = start_vk_pis + 4 + 4 * i;
|
||||||
|
nonzero_public_inputs
|
||||||
|
.extend((start..).zip(verifier_data.constants_sigmas_cap.0[i].elements));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: A bit wasteful to build a dummy circuit here. We could potentially use a proof that
|
||||||
|
// just consists of zeros, apart from public inputs.
|
||||||
|
dummy_proof(&dummy_circuit(common_data), nonzero_public_inputs).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate a proof for a dummy circuit. The `public_inputs` parameter let the caller specify
|
/// Generate a proof for a dummy circuit. The `public_inputs` parameter let the caller specify
|
||||||
/// certain public inputs (identified by their indices) which should be given specific values.
|
/// certain public inputs (identified by their indices) which should be given specific values.
|
||||||
@ -65,3 +101,59 @@ pub(crate) fn dummy_circuit<
|
|||||||
assert_eq!(&circuit.common, common_data);
|
assert_eq!(&circuit.common, common_data);
|
||||||
circuit
|
circuit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||||
|
pub(crate) fn dummy_proof_and_vk<C: GenericConfig<D, F = F> + 'static>(
|
||||||
|
&mut self,
|
||||||
|
common_data: &CommonCircuitData<F, D>,
|
||||||
|
) -> anyhow::Result<(ProofWithPublicInputsTarget<D>, VerifierCircuitTarget)>
|
||||||
|
where
|
||||||
|
C::Hasher: AlgebraicHasher<F>,
|
||||||
|
{
|
||||||
|
let dummy_circuit = dummy_circuit::<F, C, D>(common_data);
|
||||||
|
let dummy_proof_with_pis = dummy_proof(&dummy_circuit, HashMap::new())?;
|
||||||
|
let dummy_proof_with_pis_target = self.add_virtual_proof_with_pis::<C>(common_data);
|
||||||
|
|
||||||
|
let dummy_verifier_data_target = VerifierCircuitTarget {
|
||||||
|
constants_sigmas_cap: self.add_virtual_cap(self.config.fri_config.cap_height),
|
||||||
|
circuit_digest: self.add_virtual_hash(),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.add_simple_generator(DummyProofGenerator {
|
||||||
|
proof_with_pis_target: dummy_proof_with_pis_target.clone(),
|
||||||
|
proof_with_pis: dummy_proof_with_pis,
|
||||||
|
verifier_data_target: dummy_verifier_data_target.clone(),
|
||||||
|
verifier_data: dummy_circuit.verifier_only,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok((dummy_proof_with_pis_target, dummy_verifier_data_target))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct DummyProofGenerator<F, C, const D: usize>
|
||||||
|
where
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
C: GenericConfig<D, F = F>,
|
||||||
|
{
|
||||||
|
pub(crate) proof_with_pis_target: ProofWithPublicInputsTarget<D>,
|
||||||
|
pub(crate) proof_with_pis: ProofWithPublicInputs<F, C, D>,
|
||||||
|
pub(crate) verifier_data_target: VerifierCircuitTarget,
|
||||||
|
pub(crate) verifier_data: VerifierOnlyCircuitData<C, D>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F, C, const D: usize> SimpleGenerator<F> for DummyProofGenerator<F, C, D>
|
||||||
|
where
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
C: GenericConfig<D, F = F> + 'static,
|
||||||
|
C::Hasher: AlgebraicHasher<F>,
|
||||||
|
{
|
||||||
|
fn dependencies(&self) -> Vec<Target> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_once(&self, _witness: &PartitionWitness<F>, out_buffer: &mut GeneratedValues<F>) {
|
||||||
|
out_buffer.set_proof_with_pis_target(&self.proof_with_pis_target, &self.proof_with_pis);
|
||||||
|
out_buffer.set_verifier_data_target(&self.verifier_data_target, &self.verifier_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
pub mod conditional_recursive_verifier;
|
pub mod conditional_recursive_verifier;
|
||||||
pub mod cyclic_recursion;
|
pub mod cyclic_recursion;
|
||||||
pub(crate) mod dummy_circuit;
|
pub mod dummy_circuit;
|
||||||
pub mod recursive_verifier;
|
pub mod recursive_verifier;
|
||||||
|
|||||||
@ -191,7 +191,7 @@ mod tests {
|
|||||||
use crate::fri::reduction_strategies::FriReductionStrategy;
|
use crate::fri::reduction_strategies::FriReductionStrategy;
|
||||||
use crate::fri::FriConfig;
|
use crate::fri::FriConfig;
|
||||||
use crate::gates::noop::NoopGate;
|
use crate::gates::noop::NoopGate;
|
||||||
use crate::iop::witness::{PartialWitness, Witness};
|
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use crate::plonk::circuit_data::{CircuitConfig, VerifierOnlyCircuitData};
|
use crate::plonk::circuit_data::{CircuitConfig, VerifierOnlyCircuitData};
|
||||||
use crate::plonk::config::{GenericConfig, KeccakGoldilocksConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, KeccakGoldilocksConfig, PoseidonGoldilocksConfig};
|
||||||
use crate::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInputs};
|
use crate::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInputs};
|
||||||
|
|||||||
@ -278,7 +278,7 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::field::types::Sample;
|
use crate::field::types::Sample;
|
||||||
use crate::iop::witness::{PartialWitness, Witness};
|
use crate::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use crate::plonk::circuit_data::CircuitConfig;
|
use crate::plonk::circuit_data::CircuitConfig;
|
||||||
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
|
||||||
use crate::plonk::verifier::verify;
|
use crate::plonk::verifier::verify;
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use plonky2::field::extension::{Extendable, FieldExtension};
|
|||||||
use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues};
|
use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues};
|
||||||
use plonky2::field::types::{Field, Sample};
|
use plonky2::field::types::{Field, Sample};
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
use plonky2::iop::witness::{PartialWitness, Witness};
|
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||||
use plonky2::plonk::config::{GenericConfig, Hasher};
|
use plonky2::plonk::config::{GenericConfig, Hasher};
|
||||||
|
|||||||
@ -14,7 +14,7 @@ use plonky2::iop::ext_target::ExtensionTarget;
|
|||||||
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use plonky2::iop::target::Target;
|
use plonky2::iop::target::Target;
|
||||||
use plonky2::iop::wire::Wire;
|
use plonky2::iop::wire::Wire;
|
||||||
use plonky2::iop::witness::{PartitionWitness, Witness};
|
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||||
use plonky2::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
use plonky2::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||||
|
|||||||
@ -16,7 +16,7 @@ use plonky2::iop::ext_target::ExtensionTarget;
|
|||||||
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use plonky2::iop::target::Target;
|
use plonky2::iop::target::Target;
|
||||||
use plonky2::iop::wire::Wire;
|
use plonky2::iop::wire::Wire;
|
||||||
use plonky2::iop::witness::{PartitionWitness, Witness};
|
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||||
use plonky2::plonk::vars::{
|
use plonky2::plonk::vars::{
|
||||||
|
|||||||
@ -15,7 +15,7 @@ use plonky2::iop::ext_target::ExtensionTarget;
|
|||||||
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use plonky2::iop::target::Target;
|
use plonky2::iop::target::Target;
|
||||||
use plonky2::iop::wire::Wire;
|
use plonky2::iop::wire::Wire;
|
||||||
use plonky2::iop::witness::{PartitionWitness, Witness};
|
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_circuit};
|
use plonky2::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_circuit};
|
||||||
use plonky2::plonk::vars::{
|
use plonky2::plonk::vars::{
|
||||||
|
|||||||
@ -12,7 +12,7 @@ use plonky2::hash::hash_types::RichField;
|
|||||||
use plonky2::iop::ext_target::ExtensionTarget;
|
use plonky2::iop::ext_target::ExtensionTarget;
|
||||||
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use plonky2::iop::target::Target;
|
use plonky2::iop::target::Target;
|
||||||
use plonky2::iop::witness::{PartitionWitness, Witness};
|
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_circuit};
|
use plonky2::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_circuit};
|
||||||
use plonky2::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
use plonky2::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
|
||||||
|
|||||||
@ -15,7 +15,7 @@ use plonky2::iop::ext_target::ExtensionTarget;
|
|||||||
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use plonky2::iop::target::Target;
|
use plonky2::iop::target::Target;
|
||||||
use plonky2::iop::wire::Wire;
|
use plonky2::iop::wire::Wire;
|
||||||
use plonky2::iop::witness::{PartitionWitness, Witness};
|
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||||
use plonky2::plonk::vars::{
|
use plonky2::plonk::vars::{
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use plonky2::field::types::{Field, PrimeField64};
|
use plonky2::field::types::{Field, PrimeField64};
|
||||||
use plonky2::iop::generator::GeneratedValues;
|
use plonky2::iop::generator::GeneratedValues;
|
||||||
use plonky2::iop::witness::Witness;
|
use plonky2::iop::witness::{Witness, WitnessWrite};
|
||||||
|
|
||||||
use crate::gadgets::arithmetic_u32::U32Target;
|
use crate::gadgets::arithmetic_u32::U32Target;
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ use plonky2::iop::ext_target::ExtensionTarget;
|
|||||||
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
|
||||||
use plonky2::iop::target::Target;
|
use plonky2::iop::target::Target;
|
||||||
use plonky2::iop::wire::Wire;
|
use plonky2::iop::wire::Wire;
|
||||||
use plonky2::iop::witness::{PartitionWitness, Witness};
|
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_circuit};
|
use plonky2::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_circuit};
|
||||||
use plonky2::plonk::vars::{
|
use plonky2::plonk::vars::{
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use plonky2::iop::ext_target::ExtensionTarget;
|
|||||||
use plonky2::iop::generator::{GeneratedValues, WitnessGenerator};
|
use plonky2::iop::generator::{GeneratedValues, WitnessGenerator};
|
||||||
use plonky2::iop::target::Target;
|
use plonky2::iop::target::Target;
|
||||||
use plonky2::iop::wire::Wire;
|
use plonky2::iop::wire::Wire;
|
||||||
use plonky2::iop::witness::{PartitionWitness, Witness};
|
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::plonk::circuit_data::CircuitConfig;
|
use plonky2::plonk::circuit_data::CircuitConfig;
|
||||||
use plonky2::plonk::vars::{
|
use plonky2::plonk::vars::{
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use plonky2::field::types::Field;
|
|||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
|
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||||
use plonky2::iop::target::Target;
|
use plonky2::iop::target::Target;
|
||||||
use plonky2::iop::witness::{PartitionWitness, Witness};
|
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
|
|
||||||
use crate::bimap::bimap_from_lists;
|
use crate::bimap::bimap_from_lists;
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use plonky2::field::types::Field;
|
|||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
|
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
|
||||||
use plonky2::iop::target::{BoolTarget, Target};
|
use plonky2::iop::target::{BoolTarget, Target};
|
||||||
use plonky2::iop::witness::{PartitionWitness, Witness};
|
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2_util::ceil_div_usize;
|
use plonky2_util::ceil_div_usize;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user