mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-03 14:23:07 +00:00
Keccak memory stark
This commit is contained in:
parent
54862cb71c
commit
522cac5e15
@ -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"
|
||||
|
||||
@ -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(),
|
||||
)?;
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
));
|
||||
|
||||
|
||||
@ -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>>,
|
||||
|
||||
@ -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![],
|
||||
}
|
||||
|
||||
29
evm/src/keccak_memory/columns.rs
Normal file
29
evm/src/keccak_memory/columns.rs
Normal 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;
|
||||
240
evm/src/keccak_memory/keccak_memory_stark.rs
Normal file
240
evm/src/keccak_memory/keccak_memory_stark.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
2
evm/src/keccak_memory/mod.rs
Normal file
2
evm/src/keccak_memory/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod columns;
|
||||
pub mod keccak_memory_stark;
|
||||
@ -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;
|
||||
|
||||
@ -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 })
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user