Keccak memory stark

This commit is contained in:
Daniel Lubarov 2022-08-14 16:36:07 -07:00
parent 54862cb71c
commit 522cac5e15
13 changed files with 539 additions and 28 deletions

View File

@ -23,6 +23,7 @@ rand_chacha = "0.3.1"
rlp = "0.5.1"
keccak-rust = { git = "https://github.com/npwardberkeley/keccak-rust" }
keccak-hash = "0.9.0"
tiny-keccak = "2.0.2"
[dev-dependencies]
criterion = "0.3.5"

View File

@ -8,6 +8,8 @@ use crate::cpu::cpu_stark::CpuStark;
use crate::cross_table_lookup::{CrossTableLookup, TableWithColumns};
use crate::keccak::keccak_stark;
use crate::keccak::keccak_stark::KeccakStark;
use crate::keccak_memory::keccak_memory_stark;
use crate::keccak_memory::keccak_memory_stark::KeccakMemoryStark;
use crate::logic;
use crate::logic::LogicStark;
use crate::memory::memory_stark::MemoryStark;
@ -18,6 +20,7 @@ use crate::stark::Stark;
pub struct AllStark<F: RichField + Extendable<D>, const D: usize> {
pub cpu_stark: CpuStark<F, D>,
pub keccak_stark: KeccakStark<F, D>,
pub keccak_memory_stark: KeccakMemoryStark<F, D>,
pub logic_stark: LogicStark<F, D>,
pub memory_stark: MemoryStark<F, D>,
pub cross_table_lookups: Vec<CrossTableLookup<F>>,
@ -28,6 +31,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Default for AllStark<F, D> {
Self {
cpu_stark: CpuStark::default(),
keccak_stark: KeccakStark::default(),
keccak_memory_stark: KeccakMemoryStark::default(),
logic_stark: LogicStark::default(),
memory_stark: MemoryStark::default(),
cross_table_lookups: all_cross_table_lookups(),
@ -40,6 +44,7 @@ impl<F: RichField + Extendable<D>, const D: usize> AllStark<F, D> {
let ans = vec![
self.cpu_stark.num_permutation_batches(config),
self.keccak_stark.num_permutation_batches(config),
self.keccak_memory_stark.num_permutation_batches(config),
self.logic_stark.num_permutation_batches(config),
self.memory_stark.num_permutation_batches(config),
];
@ -51,6 +56,7 @@ impl<F: RichField + Extendable<D>, const D: usize> AllStark<F, D> {
let ans = vec![
self.cpu_stark.permutation_batch_size(),
self.keccak_stark.permutation_batch_size(),
self.keccak_memory_stark.permutation_batch_size(),
self.logic_stark.permutation_batch_size(),
self.memory_stark.permutation_batch_size(),
];
@ -63,8 +69,9 @@ impl<F: RichField + Extendable<D>, const D: usize> AllStark<F, D> {
pub enum Table {
Cpu = 0,
Keccak = 1,
Logic = 2,
Memory = 3,
KeccakMemory = 2,
Logic = 3,
Memory = 4,
}
impl Table {
@ -75,16 +82,22 @@ impl Table {
#[allow(unused)] // TODO: Should be used soon.
pub(crate) fn all_cross_table_lookups<F: Field>() -> Vec<CrossTableLookup<F>> {
vec![ctl_keccak(), ctl_logic(), ctl_memory()]
vec![ctl_keccak(), ctl_logic(), ctl_memory(), ctl_keccak_memory()]
}
fn ctl_keccak<F: Field>() -> CrossTableLookup<F> {
let cpu_looking = TableWithColumns::new(
Table::Cpu,
cpu_stark::ctl_data_keccak(),
Some(cpu_stark::ctl_filter_keccak()),
);
let keccak_memory_looking = TableWithColumns::new(
Table::KeccakMemory,
keccak_memory_stark::ctl_looking_keccak(),
Some(keccak_memory_stark::ctl_filter()),
);
CrossTableLookup::new(
vec![TableWithColumns::new(
Table::Cpu,
cpu_stark::ctl_data_keccak(),
Some(cpu_stark::ctl_filter_keccak()),
)],
vec![cpu_looking, keccak_memory_looking],
TableWithColumns::new(
Table::Keccak,
keccak_stark::ctl_data(),
@ -94,6 +107,22 @@ fn ctl_keccak<F: Field>() -> CrossTableLookup<F> {
)
}
fn ctl_keccak_memory<F: Field>() -> CrossTableLookup<F> {
CrossTableLookup::new(
vec![TableWithColumns::new(
Table::Cpu,
cpu_stark::ctl_data_keccak_memory(),
Some(cpu_stark::ctl_filter_keccak_memory()),
)],
TableWithColumns::new(
Table::KeccakMemory,
keccak_memory_stark::ctl_looked_data(),
Some(keccak_memory_stark::ctl_filter()),
),
None,
)
}
fn ctl_logic<F: Field>() -> CrossTableLookup<F> {
CrossTableLookup::new(
vec![TableWithColumns::new(
@ -107,16 +136,33 @@ fn ctl_logic<F: Field>() -> CrossTableLookup<F> {
}
fn ctl_memory<F: Field>() -> CrossTableLookup<F> {
let cpu_memory_ops = (0..NUM_CHANNELS).map(|channel| {
TableWithColumns::new(
Table::Cpu,
cpu_stark::ctl_data_memory(channel),
Some(cpu_stark::ctl_filter_memory(channel)),
)
});
let keccak_memory_reads = (0..200).map(|i| {
TableWithColumns::new(
Table::KeccakMemory,
keccak_memory_stark::ctl_looking_memory_read(i),
Some(keccak_memory_stark::ctl_filter()),
)
});
let keccak_memory_writes = (0..200).map(|i| {
TableWithColumns::new(
Table::KeccakMemory,
keccak_memory_stark::ctl_looking_memory_write(i),
Some(keccak_memory_stark::ctl_filter()),
)
});
let all_lookers = cpu_memory_ops
.chain(keccak_memory_reads)
.chain(keccak_memory_writes)
.collect();
CrossTableLookup::new(
(0..NUM_CHANNELS)
.map(|channel| {
TableWithColumns::new(
Table::Cpu,
cpu_stark::ctl_data_memory(channel),
Some(cpu_stark::ctl_filter_memory(channel)),
)
})
.collect(),
all_lookers,
TableWithColumns::new(
Table::Memory,
memory_stark::ctl_data(),
@ -142,12 +188,13 @@ mod tests {
use plonky2::util::timing::TimingTree;
use rand::{thread_rng, Rng};
use crate::all_stark::AllStark;
use crate::all_stark::{AllStark, Table};
use crate::config::StarkConfig;
use crate::cpu::cpu_stark::CpuStark;
use crate::cpu::kernel::aggregator::KERNEL;
use crate::cross_table_lookup::testutils::check_ctls;
use crate::keccak::keccak_stark::{KeccakStark, NUM_INPUTS, NUM_ROUNDS};
use crate::keccak_memory::keccak_memory_stark::KeccakMemoryStark;
use crate::logic::{self, LogicStark, Operation};
use crate::memory::memory_stark::tests::generate_random_memory_ops;
use crate::memory::memory_stark::MemoryStark;
@ -177,6 +224,13 @@ mod tests {
keccak_stark.generate_trace(keccak_inputs)
}
fn make_keccak_memory_trace(
keccak_memory_stark: &KeccakMemoryStark<F, D>,
config: &StarkConfig,
) -> Vec<PolynomialValues<F>> {
keccak_memory_stark.generate_trace(vec![], config.fri_config.cap_height)
}
fn make_logic_trace<R: Rng>(
num_rows: usize,
logic_stark: &LogicStark<F, D>,
@ -607,6 +661,7 @@ mod tests {
let num_keccak_perms = 2;
let keccak_trace = make_keccak_trace(num_keccak_perms, &all_stark.keccak_stark, &mut rng);
let keccak_memory_trace = make_keccak_memory_trace(&all_stark.keccak_memory_stark, config);
let logic_trace = make_logic_trace(num_logic_rows, &all_stark.logic_stark, &mut rng);
let mem_trace = make_memory_trace(num_memory_ops, &all_stark.memory_stark, &mut rng);
let mut memory_trace = mem_trace.0;
@ -621,14 +676,20 @@ mod tests {
&mut memory_trace,
);
let traces = vec![cpu_trace, keccak_trace, logic_trace, memory_trace];
let traces = vec![
cpu_trace,
keccak_trace,
keccak_memory_trace,
logic_trace,
memory_trace,
];
check_ctls(&traces, &all_stark.cross_table_lookups);
let proof = prove::<F, C, D>(
&all_stark,
config,
traces,
vec![vec![]; 4],
vec![vec![]; Table::num_tables()],
&mut TimingTree::default(),
)?;

View File

@ -153,9 +153,12 @@ pub struct CpuColumnsView<T: Copy> {
/// If CPU cycle: the opcode, broken up into bits in little-endian order.
pub opcode_bits: [T; 8],
/// Filter. 1 iff a Keccak permutation is computed on this row.
/// Filter. 1 iff a Keccak lookup is performed on this row.
pub is_keccak: T,
/// Filter. 1 iff a Keccak memory lookup is performed on this row.
pub is_keccak_memory: T,
pub(crate) general: CpuGeneralColumnsView<T>,
pub(crate) clock: T,

View File

@ -22,10 +22,30 @@ pub fn ctl_data_keccak<F: Field>() -> Vec<Column<F>> {
res
}
pub fn ctl_data_keccak_memory<F: Field>() -> Vec<Column<F>> {
// When executing KECCAK_GENERAL, the memory channels are used as follows:
// channel 0: instruction
// channel 1: stack[-1] = context
// channel 2: stack[-2] = segment
// channel 3: stack[-3] = virtual
let context = Column::single(COL_MAP.mem_value[1][0]);
let segment = Column::single(COL_MAP.mem_value[2][0]);
let virt = Column::single(COL_MAP.mem_value[3][0]);
let num_channels = F::from_canonical_usize(NUM_CHANNELS);
let clock = Column::linear_combination([(COL_MAP.clock, num_channels)]);
vec![context, segment, virt, clock]
}
pub fn ctl_filter_keccak<F: Field>() -> Column<F> {
Column::single(COL_MAP.is_keccak)
}
pub fn ctl_filter_keccak_memory<F: Field>() -> Column<F> {
Column::single(COL_MAP.is_keccak_memory)
}
pub fn ctl_data_logic<F: Field>() -> Vec<Column<F>> {
let mut res = Column::singles([COL_MAP.is_and, COL_MAP.is_or, COL_MAP.is_xor]).collect_vec();
let logic = COL_MAP.general.logic();
@ -53,7 +73,7 @@ pub fn ctl_data_memory<F: Field>(channel: usize) -> Vec<Column<F>> {
let scalar = F::from_canonical_usize(NUM_CHANNELS);
let addend = F::from_canonical_usize(channel);
cols.push(Column::linear_combination_with_constant(
vec![(COL_MAP.clock, scalar)],
[(COL_MAP.clock, scalar)],
addend,
));

View File

@ -24,7 +24,7 @@ use crate::stark::Stark;
use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars};
/// Represent a linear combination of columns.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Column<F: Field> {
linear_combination: Vec<(usize, F)>,
constant: F,
@ -42,6 +42,17 @@ impl<F: Field> Column<F> {
cs.into_iter().map(Self::single)
}
pub fn constant(constant: F) -> Self {
Self {
linear_combination: vec![],
constant,
}
}
pub fn zero() -> Self {
Self::constant(F::ZERO)
}
pub fn linear_combination_with_constant<I: IntoIterator<Item = (usize, F)>>(
iter: I,
constant: F,
@ -67,6 +78,10 @@ impl<F: Field> Column<F> {
Self::linear_combination(cs.into_iter().zip(F::TWO.powers()))
}
pub fn le_bytes<I: IntoIterator<Item = usize>>(cs: I) -> Self {
Self::linear_combination(cs.into_iter().zip(F::from_canonical_u16(256).powers()))
}
pub fn sum<I: IntoIterator<Item = usize>>(cs: I) -> Self {
Self::linear_combination(cs.into_iter().zip(repeat(F::ONE)))
}
@ -115,7 +130,7 @@ impl<F: Field> Column<F> {
}
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct TableWithColumns<F: Field> {
table: Table,
columns: Vec<Column<F>>,

View File

@ -2,11 +2,14 @@ use std::mem;
use ethereum_types::U256;
use plonky2::field::types::Field;
use tiny_keccak::keccakf;
use crate::cpu::columns::{CpuColumnsView, NUM_CPU_COLUMNS};
use crate::generation::memory::MemoryState;
use crate::keccak_memory::keccak_memory_stark::KeccakMemoryOp;
use crate::memory::memory_stark::MemoryOp;
use crate::memory::segments::Segment;
use crate::memory::NUM_CHANNELS;
use crate::{keccak, logic};
#[derive(Debug)]
@ -18,6 +21,7 @@ pub(crate) struct GenerationState<F: Field> {
pub(crate) memory: MemoryState,
pub(crate) keccak_inputs: Vec<[u64; keccak::keccak_stark::NUM_INPUTS]>,
pub(crate) keccak_memory_inputs: Vec<KeccakMemoryOp>,
pub(crate) logic_ops: Vec<logic::Operation>,
/// Non-deterministic inputs provided by the prover.
@ -58,10 +62,21 @@ impl<F: Field> GenerationState<F> {
channel_index: usize,
segment: Segment,
virt: usize,
) -> U256 {
let context = self.current_context;
self.get_mem(channel_index, context, segment, virt)
}
/// Read some memory, and log the operation.
pub(crate) fn get_mem(
&mut self,
channel_index: usize,
context: usize,
segment: Segment,
virt: usize,
) -> U256 {
self.current_cpu_row.mem_channel_used[channel_index] = F::ONE;
let timestamp = self.cpu_rows.len();
let context = self.current_context;
let value = self.memory.contexts[context].segments[segment as usize].get(virt);
self.memory.log.push(MemoryOp {
filter: true,
@ -82,10 +97,23 @@ impl<F: Field> GenerationState<F> {
segment: Segment,
virt: usize,
value: U256,
) {
let context = self.current_context;
self.set_mem(channel_index, context, segment, virt, value);
}
/// Write some memory, and log the operation.
pub(crate) fn set_mem(
&mut self,
channel_index: usize,
context: usize,
segment: Segment,
virt: usize,
value: U256,
) {
self.current_cpu_row.mem_channel_used[channel_index] = F::ONE;
let timestamp = self.cpu_rows.len();
let context = self.current_context;
let timestamp = timestamp * NUM_CHANNELS + channel_index;
self.memory.log.push(MemoryOp {
filter: true,
timestamp,
@ -98,6 +126,52 @@ impl<F: Field> GenerationState<F> {
self.memory.contexts[context].segments[segment as usize].set(virt, value)
}
/// Evaluate the Keccak-f permutation in-place on some data in memory, and record the operations
/// for the purpose of witness generation.
#[allow(unused)] // TODO: Should be used soon.
pub(crate) fn keccak_memory(
&mut self,
context: usize,
segment: Segment,
virt: usize,
) -> [u64; keccak::keccak_stark::NUM_INPUTS] {
let read_timestamp = self.cpu_rows.len() * NUM_CHANNELS;
let input = (0..25)
.map(|i| {
let bytes = [0, 1, 2, 3, 4, 5, 6, 7].map(|j| {
let virt = virt + i * 8 + j;
let byte = self.get_mem(0, context, segment, virt);
debug_assert!(byte.bits() <= 8);
byte.as_u32() as u8
});
u64::from_le_bytes(bytes)
})
.collect::<Vec<_>>()
.try_into()
.unwrap();
let output = self.keccak(input);
self.keccak_memory_inputs.push(KeccakMemoryOp {
context,
segment,
virt,
read_timestamp,
input,
output,
});
output
}
/// Evaluate the Keccak-f permutation, and record the operation for the purpose of witness
/// generation.
pub(crate) fn keccak(
&mut self,
mut input: [u64; keccak::keccak_stark::NUM_INPUTS],
) -> [u64; keccak::keccak_stark::NUM_INPUTS] {
self.keccak_inputs.push(input);
keccakf(&mut input);
input
}
pub(crate) fn commit_cpu_row(&mut self) {
let mut swapped_row = [F::ZERO; NUM_CPU_COLUMNS].into();
mem::swap(&mut self.current_cpu_row, &mut swapped_row);
@ -115,6 +189,7 @@ impl<F: Field> Default for GenerationState<F> {
current_context: 0,
memory: MemoryState::default(),
keccak_inputs: vec![],
keccak_memory_inputs: vec![],
logic_ops: vec![],
prover_inputs: vec![],
}

View File

@ -0,0 +1,29 @@
pub(crate) const KECCAK_WIDTH_BYTES: usize = 200;
/// 1 if this row represents a real operation; 0 if it's a padding row.
pub(crate) const COL_IS_REAL: usize = 0;
// The address at which we will read inputs and write outputs.
pub(crate) const COL_CONTEXT: usize = 1;
pub(crate) const COL_SEGMENT: usize = 2;
pub(crate) const COL_VIRTUAL: usize = 3;
/// The timestamp at which inputs should be read from memory.
/// Outputs will be written at the following timestamp.
pub(crate) const COL_READ_TIMESTAMP: usize = 4;
const START_INPUT_LIMBS: usize = 5;
/// A byte of the input.
pub(crate) fn col_input_byte(i: usize) -> usize {
debug_assert!(i < KECCAK_WIDTH_BYTES);
START_INPUT_LIMBS + i
}
const START_OUTPUT_LIMBS: usize = START_INPUT_LIMBS + KECCAK_WIDTH_BYTES;
/// A byte of the output.
pub(crate) fn col_output_byte(i: usize) -> usize {
debug_assert!(i < KECCAK_WIDTH_BYTES);
START_OUTPUT_LIMBS + i
}
pub const NUM_COLUMNS: usize = START_OUTPUT_LIMBS + KECCAK_WIDTH_BYTES;

View File

@ -0,0 +1,240 @@
use std::io::Read;
use std::marker::PhantomData;
use itertools::Itertools;
use plonky2::field::extension::{Extendable, FieldExtension};
use plonky2::field::packed::PackedField;
use plonky2::field::polynomial::PolynomialValues;
use plonky2::field::types::Field;
use plonky2::hash::hash_types::RichField;
use plonky2::timed;
use plonky2::util::timing::TimingTree;
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
use crate::cross_table_lookup::Column;
use crate::keccak::keccak_stark::NUM_INPUTS;
use crate::keccak_memory::columns::*;
use crate::memory::segments::Segment;
use crate::stark::Stark;
use crate::util::trace_rows_to_poly_values;
use crate::vars::StarkEvaluationTargets;
use crate::vars::StarkEvaluationVars;
const NUM_PUBLIC_INPUTS: usize = 0;
pub(crate) fn ctl_looked_data<F: Field>() -> Vec<Column<F>> {
Column::singles([COL_CONTEXT, COL_SEGMENT, COL_VIRTUAL, COL_READ_TIMESTAMP]).collect()
}
pub(crate) fn ctl_looking_keccak<F: Field>() -> Vec<Column<F>> {
let input_cols = (0..50).map(|i| {
Column::le_bytes((0..4).map(|j| {
let byte_index = i * 4 + j;
col_input_byte(byte_index)
}))
});
let output_cols = (0..50).map(|i| {
Column::le_bytes((0..4).map(|j| {
let byte_index = i * 4 + j;
col_output_byte(byte_index)
}))
});
input_cols.chain(output_cols).collect()
}
pub(crate) fn ctl_looking_memory_read<F: Field>(i: usize) -> Vec<Column<F>> {
ctl_looking_memory(i, true)
}
pub(crate) fn ctl_looking_memory_write<F: Field>(i: usize) -> Vec<Column<F>> {
ctl_looking_memory(i, false)
}
fn ctl_looking_memory<F: Field>(i: usize, is_read: bool) -> Vec<Column<F>> {
let mut res = vec![Column::constant(F::from_bool(is_read))];
res.extend(Column::singles([COL_CONTEXT, COL_SEGMENT, COL_VIRTUAL]));
res.push(Column::single(col_input_byte(i)));
// Since we're reading or writing a single byte, the higher limbs must be zero.
res.extend((1..8).map(|_| Column::zero()));
// Since COL_READ_TIMESTAMP is the read time, we add 1 if this is a write.
let is_write_f = F::from_bool(!is_read);
res.push(Column::linear_combination_with_constant(
[(COL_READ_TIMESTAMP, F::ONE)],
is_write_f,
));
assert_eq!(
res.len(),
crate::memory::memory_stark::ctl_data::<F>().len()
);
res
}
/// CTL filter used for both directions (looked and looking).
pub(crate) fn ctl_filter<F: Field>() -> Column<F> {
Column::single(COL_IS_REAL)
}
/// Information about a Keccak memory operation needed for witness generation.
#[derive(Debug)]
pub(crate) struct KeccakMemoryOp {
// The address at which we will read inputs and write outputs.
pub(crate) context: usize,
pub(crate) segment: Segment,
pub(crate) virt: usize,
/// The timestamp at which inputs should be read from memory.
/// Outputs will be written at the following timestamp.
pub(crate) read_timestamp: usize,
/// The input that was read at that address.
pub(crate) input: [u64; NUM_INPUTS],
pub(crate) output: [u64; NUM_INPUTS],
}
#[derive(Copy, Clone, Default)]
pub struct KeccakMemoryStark<F, const D: usize> {
pub(crate) f: PhantomData<F>,
}
impl<F: RichField + Extendable<D>, const D: usize> KeccakMemoryStark<F, D> {
#[allow(unused)] // TODO: Should be used soon.
pub(crate) fn generate_trace(
&self,
operations: Vec<KeccakMemoryOp>,
min_rows: usize,
) -> Vec<PolynomialValues<F>> {
let mut timing = TimingTree::new("generate trace", log::Level::Debug);
// Generate the witness row-wise.
let trace_rows = timed!(
&mut timing,
"generate trace rows",
self.generate_trace_rows(operations, min_rows)
);
let trace_polys = timed!(
&mut timing,
"convert to PolynomialValues",
trace_rows_to_poly_values(trace_rows)
);
timing.print();
trace_polys
}
fn generate_trace_rows(
&self,
operations: Vec<KeccakMemoryOp>,
min_rows: usize,
) -> Vec<[F; NUM_COLUMNS]> {
let num_rows = operations.len().max(32).next_power_of_two();
let mut rows = Vec::with_capacity(num_rows);
for op in operations {
rows.push(self.generate_row_for_op(op));
}
let padding_row = self.generate_padding_row();
for _ in rows.len()..num_rows {
rows.push(padding_row);
}
rows
}
fn generate_row_for_op(&self, op: KeccakMemoryOp) -> [F; NUM_COLUMNS] {
let mut row = [F::ZERO; NUM_COLUMNS];
row[COL_IS_REAL] = F::ONE;
row[COL_CONTEXT] = F::from_canonical_usize(op.context);
row[COL_SEGMENT] = F::from_canonical_usize(op.segment as usize);
row[COL_VIRTUAL] = F::from_canonical_usize(op.virt);
row[COL_READ_TIMESTAMP] = F::from_canonical_usize(op.read_timestamp);
for i in 0..25 {
let input_u64 = op.input[i];
let output_u64 = op.output[i];
for j in 0..8 {
let byte_index = i * 8 + j;
row[col_input_byte(byte_index)] = F::from_canonical_u8(input_u64.to_le_bytes()[j]);
row[col_output_byte(byte_index)] =
F::from_canonical_u8(output_u64.to_le_bytes()[j]);
}
}
row
}
fn generate_padding_row(&self) -> [F; NUM_COLUMNS] {
// We just need COL_IS_REAL to be zero, which it is by default.
// The other fields will have no effect.
[F::ZERO; NUM_COLUMNS]
}
}
impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for KeccakMemoryStark<F, D> {
const COLUMNS: usize = NUM_COLUMNS;
const PUBLIC_INPUTS: usize = NUM_PUBLIC_INPUTS;
fn eval_packed_generic<FE, P, const D2: usize>(
&self,
vars: StarkEvaluationVars<FE, P, { Self::COLUMNS }, { Self::PUBLIC_INPUTS }>,
yield_constr: &mut ConstraintConsumer<P>,
) where
FE: FieldExtension<D2, BaseField = F>,
P: PackedField<Scalar = FE>,
{
// is_real must be 0 or 1.
let is_real = vars.local_values[COL_IS_REAL];
yield_constr.constraint(is_real * (is_real - P::ONES));
}
fn eval_ext_circuit(
&self,
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
vars: StarkEvaluationTargets<D, { Self::COLUMNS }, { Self::PUBLIC_INPUTS }>,
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
) {
// is_real must be 0 or 1.
let is_real = vars.local_values[COL_IS_REAL];
let constraint = builder.mul_sub_extension(is_real, is_real, is_real);
yield_constr.constraint(builder, constraint);
}
fn constraint_degree(&self) -> usize {
2
}
}
#[cfg(test)]
mod tests {
use anyhow::Result;
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use crate::keccak_memory::keccak_memory_stark::KeccakMemoryStark;
use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree};
#[test]
fn test_stark_degree() -> Result<()> {
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
type S = KeccakMemoryStark<F, D>;
let stark = S {
f: Default::default(),
};
test_stark_low_degree(stark)
}
#[test]
fn test_stark_circuit() -> Result<()> {
const D: usize = 2;
type C = PoseidonGoldilocksConfig;
type F = <C as GenericConfig<D>>::F;
type S = KeccakMemoryStark<F, D>;
let stark = S {
f: Default::default(),
};
test_stark_circuit_constraints::<F, C, S, D>(stark)
}
}

View File

@ -0,0 +1,2 @@
pub mod columns;
pub mod keccak_memory_stark;

View File

@ -13,6 +13,7 @@ pub mod cross_table_lookup;
pub mod generation;
mod get_challenges;
pub mod keccak;
pub mod keccak_memory;
pub mod logic;
pub mod lookup;
pub mod memory;

View File

@ -23,6 +23,7 @@ use crate::constraint_consumer::ConstraintConsumer;
use crate::cpu::cpu_stark::CpuStark;
use crate::cross_table_lookup::{cross_table_lookup_data, CtlCheckVars, CtlData};
use crate::keccak::keccak_stark::KeccakStark;
use crate::keccak_memory::keccak_memory_stark::KeccakMemoryStark;
use crate::logic::LogicStark;
use crate::memory::memory_stark::MemoryStark;
use crate::permutation::PermutationCheckVars;
@ -50,6 +51,8 @@ where
[(); CpuStark::<F, D>::PUBLIC_INPUTS]:,
[(); KeccakStark::<F, D>::COLUMNS]:,
[(); KeccakStark::<F, D>::PUBLIC_INPUTS]:,
[(); KeccakMemoryStark::<F, D>::COLUMNS]:,
[(); KeccakMemoryStark::<F, D>::PUBLIC_INPUTS]:,
[(); LogicStark::<F, D>::COLUMNS]:,
[(); LogicStark::<F, D>::PUBLIC_INPUTS]:,
[(); MemoryStark::<F, D>::COLUMNS]:,
@ -124,6 +127,19 @@ where
&mut challenger,
timing,
)?;
let keccak_memory_proof = prove_single_table(
&all_stark.keccak_memory_stark,
config,
&trace_poly_values[Table::KeccakMemory as usize],
&trace_commitments[Table::KeccakMemory as usize],
&ctl_data_per_table[Table::KeccakMemory as usize],
public_inputs[Table::KeccakMemory as usize]
.clone()
.try_into()
.unwrap(),
&mut challenger,
timing,
)?;
let logic_proof = prove_single_table(
&all_stark.logic_stark,
config,
@ -151,7 +167,13 @@ where
timing,
)?;
let stark_proofs = vec![cpu_proof, keccak_proof, logic_proof, memory_proof];
let stark_proofs = vec![
cpu_proof,
keccak_proof,
keccak_memory_proof,
logic_proof,
memory_proof,
];
debug_assert_eq!(stark_proofs.len(), num_starks);
Ok(AllProof { stark_proofs })

View File

@ -17,6 +17,7 @@ use crate::constraint_consumer::RecursiveConstraintConsumer;
use crate::cpu::cpu_stark::CpuStark;
use crate::cross_table_lookup::{verify_cross_table_lookups_circuit, CtlCheckVarsTarget};
use crate::keccak::keccak_stark::KeccakStark;
use crate::keccak_memory::keccak_memory_stark::KeccakMemoryStark;
use crate::logic::LogicStark;
use crate::memory::memory_stark::MemoryStark;
use crate::permutation::PermutationCheckDataTarget;
@ -43,6 +44,8 @@ pub fn verify_proof_circuit<
[(); CpuStark::<F, D>::PUBLIC_INPUTS]:,
[(); KeccakStark::<F, D>::COLUMNS]:,
[(); KeccakStark::<F, D>::PUBLIC_INPUTS]:,
[(); KeccakMemoryStark::<F, D>::COLUMNS]:,
[(); KeccakMemoryStark::<F, D>::PUBLIC_INPUTS]:,
[(); LogicStark::<F, D>::COLUMNS]:,
[(); LogicStark::<F, D>::PUBLIC_INPUTS]:,
[(); MemoryStark::<F, D>::COLUMNS]:,
@ -59,6 +62,7 @@ pub fn verify_proof_circuit<
let AllStark {
cpu_stark,
keccak_stark,
keccak_memory_stark,
logic_stark,
memory_stark,
cross_table_lookups,
@ -95,6 +99,18 @@ pub fn verify_proof_circuit<
inner_config,
)
);
with_context!(
builder,
"verify Keccak memory proof",
verify_stark_proof_with_challenges_circuit::<F, C, _, D>(
builder,
keccak_memory_stark,
&all_proof.stark_proofs[Table::KeccakMemory as usize],
&stark_challenges[Table::KeccakMemory as usize],
&ctl_vars_per_table[Table::KeccakMemory as usize],
inner_config,
)
);
with_context!(
builder,
"verify logic proof",
@ -309,6 +325,21 @@ pub fn add_virtual_all_proof<F: RichField + Extendable<D>, const D: usize>(
public_inputs,
}
},
{
let proof = add_virtual_stark_proof(
builder,
all_stark.keccak_memory_stark,
config,
degree_bits[Table::KeccakMemory as usize],
nums_ctl_zs[Table::KeccakMemory as usize],
);
let public_inputs =
builder.add_virtual_targets(KeccakMemoryStark::<F, D>::PUBLIC_INPUTS);
StarkProofWithPublicInputsTarget {
proof,
public_inputs,
}
},
{
let proof = add_virtual_stark_proof(
builder,
@ -331,7 +362,7 @@ pub fn add_virtual_all_proof<F: RichField + Extendable<D>, const D: usize>(
degree_bits[Table::Memory as usize],
nums_ctl_zs[Table::Memory as usize],
);
let public_inputs = builder.add_virtual_targets(KeccakStark::<F, D>::PUBLIC_INPUTS);
let public_inputs = builder.add_virtual_targets(MemoryStark::<F, D>::PUBLIC_INPUTS);
StarkProofWithPublicInputsTarget {
proof,
public_inputs,

View File

@ -12,6 +12,7 @@ use crate::constraint_consumer::ConstraintConsumer;
use crate::cpu::cpu_stark::CpuStark;
use crate::cross_table_lookup::{verify_cross_table_lookups, CtlCheckVars};
use crate::keccak::keccak_stark::KeccakStark;
use crate::keccak_memory::keccak_memory_stark::KeccakMemoryStark;
use crate::logic::LogicStark;
use crate::memory::memory_stark::MemoryStark;
use crate::permutation::PermutationCheckVars;
@ -32,6 +33,8 @@ where
[(); CpuStark::<F, D>::PUBLIC_INPUTS]:,
[(); KeccakStark::<F, D>::COLUMNS]:,
[(); KeccakStark::<F, D>::PUBLIC_INPUTS]:,
[(); KeccakMemoryStark::<F, D>::COLUMNS]:,
[(); KeccakMemoryStark::<F, D>::PUBLIC_INPUTS]:,
[(); LogicStark::<F, D>::COLUMNS]:,
[(); LogicStark::<F, D>::PUBLIC_INPUTS]:,
[(); MemoryStark::<F, D>::COLUMNS]:,
@ -48,6 +51,7 @@ where
let AllStark {
cpu_stark,
keccak_stark,
keccak_memory_stark,
logic_stark,
memory_stark,
cross_table_lookups,
@ -74,6 +78,13 @@ where
&ctl_vars_per_table[Table::Keccak as usize],
config,
)?;
verify_stark_proof_with_challenges(
keccak_memory_stark,
&all_proof.stark_proofs[Table::KeccakMemory as usize],
&stark_challenges[Table::KeccakMemory as usize],
&ctl_vars_per_table[Table::KeccakMemory as usize],
config,
)?;
verify_stark_proof_with_challenges(
memory_stark,
&all_proof.stark_proofs[Table::Memory as usize],