Skip to content

Commit 3385423

Browse files
authored
[precompile] part1 integrate keccak precompile into e2e flow (#976)
This raised PR is not fully sound yet, however it serve as a step milestone for easier to review ### change scope - define new `ComposedConstrainSystem` to wrap `original constrain system + gkr-iop circuit`. I decide for our first version, tower argument will remain in original constrain system because its dynamism layers not fit well into gkr-iop design. So I tend to keep both constrain system separately, then invoke css in proving flow accordingly. - define `Keccak` as a new (ecall-)opcode followed our conventions, and clean up previous workaround as `LargeDummy<KccakSpec>` which is not used for this purpose - refactor lookup tables utilization from zkvm crate to gkr-iop so both crate could be shared. ### TODOs in followup PR(s) - build correct circuit of `keccak` ecall opcode to deal with state(memory) consistency => existing ceno zkvm circuit builder has few good utilization to wrap boilerplate code. But gkr-iop define it's own constrain system so it's hard to leverage existing utilization. - assign witness mechanism - integrate into main prover flow + mpcs ### benchmarks On fibonacci e2e against master branch | Benchmark | Median Time (s) | Median Change (%) | |----------------------------------|------------------|----------------------------------------------| | fibonacci_max_steps_1048576 | 2.0902 | -1.1437% (No change in performance detected) | | fibonacci_max_steps_2097152 | 3.5378 | -0.5016% (No change in performance detected) | | fibonacci_max_steps_4194304 | 6.8585 | -1.9079% (No change in performance detected) | and lookup keccak | Benchmark | Median Time (s) | Median Change (%) | |----------------------------------|------------------|----------------------------------------------| | keccak_lookup_f_4096 | 0.88786 | +1.8583% (No change in performance detected) | | keccak_lookup_f_8192 | 1.7728 | +0.9121% (No change in performance detected) | both benchmark remain unchanged
1 parent d6d2c87 commit 3385423

39 files changed

+760
-942
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ strum_macros = "0.26"
7777
substrate-bn = { version = "0.6.0" }
7878
sumcheck = { path = "sumcheck" }
7979
thiserror = "1" # do we need this?
80+
thread_local = "1.1"
8081
tiny-keccak = { version = "2.0.2", features = ["keccak"] }
8182
tracing = { version = "0.1", features = [
8283
"attributes",

ceno_zkvm/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ generic_static = "0.2"
4747
parse-size = "1.1"
4848
rand.workspace = true
4949
tempfile = "3.14"
50-
thread_local = "1.1"
50+
thread_local.workspace = true
5151
tiny-keccak.workspace = true
5252

5353
[target.'cfg(unix)'.dependencies]

ceno_zkvm/benches/riscv_add.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ fn bench_add(c: &mut Criterion) {
6161
.circuit_pks
6262
.get(&AddInstruction::<E>::name())
6363
.unwrap();
64-
let num_witin = circuit_pk.get_cs().num_witin;
64+
let num_witin = circuit_pk.get_cs().num_witin();
6565

6666
for instance_num_vars in 20..22 {
6767
// expand more input size once runtime is acceptable
@@ -79,7 +79,7 @@ fn bench_add(c: &mut Criterion) {
7979
let num_instances = 1 << instance_num_vars;
8080
let rmms = BTreeMap::from([(
8181
0,
82-
RowMajorMatrix::rand(&mut OsRng, num_instances, num_witin as usize),
82+
RowMajorMatrix::rand(&mut OsRng, num_instances, num_witin),
8383
)]);
8484

8585
let instant = std::time::Instant::now();

ceno_zkvm/src/circuit_builder.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use ff_ext::ExtensionField;
1010
use crate::{
1111
ROMType,
1212
error::ZKVMError,
13-
structs::{ProgramParams, ProvingKey, RAMType, VerifyingKey},
13+
structs::{ProgramParams, RAMType},
1414
};
1515
use multilinear_extensions::monomial::Term;
1616
use p3::field::FieldAlgebra;
@@ -189,12 +189,6 @@ impl<E: ExtensionField> ConstraintSystem<E> {
189189
}
190190
}
191191

192-
pub fn key_gen(self) -> ProvingKey<E> {
193-
ProvingKey {
194-
vk: VerifyingKey { cs: self },
195-
}
196-
}
197-
198192
pub fn create_witin<NR: Into<String>, N: FnOnce() -> NR>(&mut self, n: N) -> WitIn {
199193
let wit_in = WitIn { id: self.num_witin };
200194
self.num_witin = self.num_witin.strict_add(1);

ceno_zkvm/src/instructions.rs

Lines changed: 10 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
use ceno_emul::StepRecord;
22
use ff_ext::ExtensionField;
3-
use gkr_iop::{
4-
ProtocolBuilder, ProtocolWitnessGenerator,
5-
gkr::{GKRCircuit, GKRCircuitOutput, GKRCircuitWitness},
6-
hal::ProverBackend,
7-
};
3+
use gkr_iop::gkr::GKRCircuit;
84
use multilinear_extensions::util::max_usable_threads;
95
use rayon::{
106
iter::{IndexedParallelIterator, ParallelIterator},
@@ -25,10 +21,19 @@ pub trait Instruction<E: ExtensionField> {
2521
}
2622

2723
fn name() -> String;
24+
25+
/// construct circuit and manipulate circuit builder, then return the respective config
2826
fn construct_circuit(
2927
circuit_builder: &mut CircuitBuilder<E>,
3028
) -> Result<Self::InstructionConfig, ZKVMError>;
3129

30+
/// giving config, extract optional gkr circuit
31+
fn extract_gkr_iop_circuit(
32+
_config: &mut Self::InstructionConfig,
33+
) -> Result<Option<GKRCircuit<E>>, ZKVMError> {
34+
Ok(None)
35+
}
36+
3237
// assign single instance giving step from trace
3338
fn assign_instance(
3439
config: &Self::InstructionConfig,
@@ -72,178 +77,3 @@ pub trait Instruction<E: ExtensionField> {
7277
Ok((raw_witin, lk_multiplicity))
7378
}
7479
}
75-
76-
pub struct GKRinfo {
77-
pub and_lookups: usize,
78-
pub xor_lookups: usize,
79-
pub range_lookups: usize,
80-
pub aux_wits: usize,
81-
}
82-
83-
impl GKRinfo {
84-
#[allow(dead_code)]
85-
fn lookup_total(&self) -> usize {
86-
self.and_lookups + self.xor_lookups + self.range_lookups
87-
}
88-
}
89-
90-
// Trait that should be implemented by opcodes which use the
91-
// GKR-IOP prover. Presently, such opcodes should also implement
92-
// the Instruction trait and in general methods of GKRIOPInstruction
93-
// will call corresponding methods of Instruction and do something extra
94-
// with respect to syncing state between GKR-IOP and old-style circuits/witnesses
95-
pub trait GKRIOPInstruction<E: ExtensionField>
96-
where
97-
Self: Instruction<E>,
98-
{
99-
type Layout: ProtocolWitnessGenerator<E> + ProtocolBuilder<E>;
100-
101-
/// Similar to Instruction::construct_circuit; generally
102-
/// meant to extend InstructionConfig with GKR-specific
103-
/// fields
104-
#[allow(unused_variables)]
105-
fn construct_circuit_with_gkr_iop(
106-
cb: &mut CircuitBuilder<E>,
107-
) -> Result<Self::InstructionConfig, ZKVMError> {
108-
unimplemented!();
109-
}
110-
111-
/// Should generate phase1 witness for GKR from step records
112-
fn phase1_witness_from_steps(
113-
layout: &Self::Layout,
114-
steps: &[StepRecord],
115-
) -> RowMajorMatrix<E::BaseField>;
116-
117-
/// Similar to Instruction::assign_instance, but with access to GKR lookups and wits
118-
fn assign_instance_with_gkr_iop(
119-
config: &Self::InstructionConfig,
120-
instance: &mut [E::BaseField],
121-
lk_multiplicity: &mut LkMultiplicity,
122-
step: &StepRecord,
123-
lookups: &[E::BaseField],
124-
aux_wits: &[E::BaseField],
125-
) -> Result<(), ZKVMError>;
126-
127-
/// Similar to Instruction::assign_instances, but with access to the GKR layout.
128-
#[allow(clippy::type_complexity)]
129-
fn assign_instances_with_gkr_iop<'a, PB: ProverBackend<E = E>>(
130-
_config: &Self::InstructionConfig,
131-
_num_witin: usize,
132-
_steps: Vec<StepRecord>,
133-
_gkr_circuit: &GKRCircuit<E>,
134-
_gkr_layout: &Self::Layout,
135-
) -> Result<
136-
(
137-
RowMajorMatrix<E::BaseField>,
138-
GKRCircuitWitness<'a, PB>,
139-
GKRCircuitOutput<'a, PB>,
140-
LkMultiplicity,
141-
),
142-
ZKVMError,
143-
> {
144-
todo!()
145-
// let nthreads = max_usable_threads();
146-
// let num_instance_per_batch = if steps.len() > 256 {
147-
// steps.len().div_ceil(nthreads)
148-
// } else {
149-
// steps.len()
150-
// }
151-
// .max(1);
152-
// let lk_multiplicity = LkMultiplicity::default();
153-
// let mut raw_witin =
154-
// RowMajorMatrix::<E::BaseField>::new(steps.len(), num_witin, Self::padding_strategy());
155-
156-
// let raw_witin_iter = raw_witin.par_batch_iter_mut(num_instance_per_batch);
157-
158-
// let (gkr_witness, gkr_output) = gkr_layout.gkr_witness(
159-
// gkr_circuit,
160-
// &Self::phase1_witness_from_steps(gkr_layout, &steps),
161-
// &[],
162-
// );
163-
164-
// let (lookups, aux_wits) = {
165-
// // Extract lookups and auxiliary witnesses from GKR protocol
166-
// // Here we assume that the gkr_witness's last layer is a convenience layer which holds
167-
// // the output records for all instances; further, we assume that the last ```Self::lookup_count()```
168-
// // elements of this layer are the lookup arguments.
169-
// let mut lookups = vec![vec![]; steps.len()];
170-
// let mut aux_wits: Vec<Vec<E::BaseField>> = vec![vec![]; steps.len()];
171-
// let last_layer = gkr_witness.layers.last().unwrap().bases.clone();
172-
// let len = last_layer.len();
173-
// let lookup_total = Self::gkr_info().lookup_total();
174-
175-
// let non_lookups = if len == 0 {
176-
// 0
177-
// } else {
178-
// assert!(len >= lookup_total);
179-
// len - lookup_total
180-
// };
181-
182-
// for witness in last_layer.iter().skip(non_lookups) {
183-
// for i in 0..witness.len() {
184-
// lookups[i].push(witness[i]);
185-
// }
186-
// }
187-
188-
// let n_layers = gkr_witness.layers.len();
189-
190-
// for i in 0..steps.len() {
191-
// // Omit last layer, which stores outputs and not real witnesses
192-
// for layer in gkr_witness.layers[..n_layers - 1].iter() {
193-
// for base in layer.bases.iter() {
194-
// aux_wits[i].push(base[i]);
195-
// }
196-
// }
197-
// }
198-
199-
// (lookups, aux_wits)
200-
// };
201-
202-
// raw_witin_iter
203-
// .zip(
204-
// steps
205-
// .iter()
206-
// .enumerate()
207-
// .collect_vec()
208-
// .par_chunks(num_instance_per_batch),
209-
// )
210-
// .flat_map(|(instances, steps)| {
211-
// let mut lk_multiplicity = lk_multiplicity.clone();
212-
// instances
213-
// .chunks_mut(num_witin)
214-
// .zip(steps)
215-
// .map(|(instance, (i, step))| {
216-
// Self::assign_instance_with_gkr_iop(
217-
// config,
218-
// instance,
219-
// &mut lk_multiplicity,
220-
// step,
221-
// &lookups[*i],
222-
// &aux_wits[*i],
223-
// )
224-
// })
225-
// .collect::<Vec<_>>()
226-
// })
227-
// .collect::<Result<(), ZKVMError>>()?;
228-
229-
// raw_witin.padding_by_strategy();
230-
// Ok((raw_witin, gkr_witness, gkr_output, lk_multiplicity))
231-
}
232-
233-
/// Lookup and witness counts used by GKR proof
234-
fn gkr_info() -> GKRinfo;
235-
236-
/// Returns corresponding column in RMM for the i-th
237-
/// output evaluation of the GKR proof
238-
#[allow(unused_variables)]
239-
fn output_evals_map(i: usize) -> usize {
240-
unimplemented!();
241-
}
242-
243-
/// Returns corresponding column in RMM for the i-th
244-
/// witness of the GKR proof
245-
#[allow(unused_variables)]
246-
fn witness_map(i: usize) -> usize {
247-
unimplemented!();
248-
}
249-
}

ceno_zkvm/src/instructions/riscv/dummy/dummy_circuit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ pub struct DummyConfig<E: ExtensionField> {
9090

9191
impl<E: ExtensionField> DummyConfig<E> {
9292
#[allow(clippy::too_many_arguments)]
93-
pub(super) fn construct_circuit(
93+
pub fn construct_circuit(
9494
circuit_builder: &mut CircuitBuilder<E>,
9595
kind: InsnKind,
9696
with_rs1: bool,

0 commit comments

Comments
 (0)