Skip to content

Commit ba053c2

Browse files
committed
Frontend and backend for building circuits
Remove buffers and replace the underlying util functions. Add comments and fix some tiny bugs Suggestions for 'Frontend and backend for building circuits' (#801) Suggestions for #799 Feel free to pick and choose from the suggestions. I talk about most of them on your PR. --------- Co-authored-by: dreamATD <[email protected]> Refine according to comments refine the protocol prover and verifier structs Add more comments Tiny fix according to the latest comments.
1 parent 62103a9 commit ba053c2

34 files changed

+4119
-9
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ members = [
1010
"multilinear_extensions",
1111
"sumcheck_macro",
1212
"poseidon",
13+
"gkr_iop",
14+
"subprotocols",
1315
"sumcheck",
1416
"transcript",
1517
"whir",

gkr_iop/Cargo.toml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[package]
2+
categories.workspace = true
3+
description = "GKR IOP protocol implementation"
4+
edition.workspace = true
5+
keywords.workspace = true
6+
license.workspace = true
7+
name = "gkr_iop"
8+
readme.workspace = true
9+
repository.workspace = true
10+
version.workspace = true
11+
12+
[dependencies]
13+
ark-std.workspace = true
14+
ff_ext = { path = "../ff_ext" }
15+
itertools.workspace = true
16+
multilinear_extensions = { version = "0.1.0", path = "../multilinear_extensions" }
17+
p3-field.workspace = true
18+
p3-goldilocks.workspace = true
19+
rand.workspace = true
20+
rayon.workspace = true
21+
subprotocols = { path = "../subprotocols" }
22+
thiserror = "1"
23+
transcript = { path = "../transcript" }

gkr_iop/examples/multi_layer_logup.rs

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
use std::{marker::PhantomData, mem, sync::Arc};
2+
3+
use ff_ext::ExtensionField;
4+
use gkr_iop::{
5+
ProtocolBuilder, ProtocolWitnessGenerator,
6+
chip::Chip,
7+
evaluation::{EvalExpression, PointAndEval},
8+
gkr::{
9+
GKRCircuitWitness, GKRProverOutput,
10+
layer::{Layer, LayerType, LayerWitness},
11+
},
12+
};
13+
use itertools::{Itertools, izip};
14+
use p3_field::{PrimeCharacteristicRing, extension::BinomialExtensionField};
15+
use p3_goldilocks::Goldilocks;
16+
use rand::{Rng, rngs::OsRng};
17+
use subprotocols::expression::{Constant, Expression};
18+
use transcript::{BasicTranscript, Transcript};
19+
20+
#[cfg(debug_assertions)]
21+
use gkr_iop::gkr::mock::MockProver;
22+
23+
#[cfg(debug_assertions)]
24+
use subprotocols::expression::VectorType;
25+
26+
type E = BinomialExtensionField<Goldilocks, 2>;
27+
28+
#[derive(Clone, Debug, Default)]
29+
struct TowerParams {
30+
height: usize,
31+
}
32+
33+
#[derive(Clone, Debug, Default)]
34+
struct TowerChipLayout<E> {
35+
params: TowerParams,
36+
37+
// Committed poly indices.
38+
committed_table_id: usize,
39+
committed_count_id: usize,
40+
41+
lookup_challenge: Constant,
42+
43+
output_cumulative_sum: [EvalExpression; 2],
44+
45+
_field: PhantomData<E>,
46+
}
47+
48+
impl<E: ExtensionField> ProtocolBuilder for TowerChipLayout<E> {
49+
type Params = TowerParams;
50+
51+
fn init(params: Self::Params) -> Self {
52+
Self {
53+
params,
54+
..Default::default()
55+
}
56+
}
57+
58+
fn build_commit_phase(&mut self, chip: &mut Chip) {
59+
[self.committed_table_id, self.committed_count_id] = chip.allocate_committed_base();
60+
[self.lookup_challenge] = chip.allocate_challenges();
61+
}
62+
63+
fn build_gkr_phase(&mut self, chip: &mut Chip) {
64+
let height = self.params.height;
65+
let lookup_challenge = Expression::Const(self.lookup_challenge.clone());
66+
67+
self.output_cumulative_sum = chip.allocate_output_evals();
68+
69+
// Tower layers
70+
let ([updated_table, count], challenges) = (0..height).fold(
71+
(self.output_cumulative_sum.clone(), vec![]),
72+
|([den, num], challenges), i| {
73+
let [den_0, den_1, num_0, num_1] = if i == height - 1 {
74+
// Allocate witnesses in the extension field, except numerator inputs in the base field.
75+
let ([num_0, num_1], [den_0, den_1]) = chip.allocate_wits_in_layer();
76+
[den_0, den_1, num_0, num_1]
77+
} else {
78+
let ([], [den_0, den_1, num_0, num_1]) = chip.allocate_wits_in_layer();
79+
[den_0, den_1, num_0, num_1]
80+
};
81+
82+
let [den_expr_0, den_expr_1, num_expr_0, num_expr_1]: [Expression; 4] = [
83+
den_0.0.into(),
84+
den_1.0.into(),
85+
num_0.0.into(),
86+
num_1.0.into(),
87+
];
88+
let (in_bases, in_exts) = if i == height - 1 {
89+
(vec![num_0.1.clone(), num_1.1.clone()], vec![
90+
den_0.1.clone(),
91+
den_1.1.clone(),
92+
])
93+
} else {
94+
(vec![], vec![
95+
den_0.1.clone(),
96+
den_1.1.clone(),
97+
num_0.1.clone(),
98+
num_1.1.clone(),
99+
])
100+
};
101+
chip.add_layer(Layer::new(
102+
format!("Tower_layer_{}", i),
103+
LayerType::Zerocheck,
104+
vec![
105+
den_expr_0.clone() * den_expr_1.clone(),
106+
den_expr_0 * num_expr_1 + den_expr_1 * num_expr_0,
107+
],
108+
challenges,
109+
in_bases,
110+
in_exts,
111+
vec![den, num],
112+
));
113+
let [challenge] = chip.allocate_challenges();
114+
(
115+
[
116+
EvalExpression::Partition(
117+
vec![Box::new(den_0.1), Box::new(den_1.1)],
118+
vec![(0, challenge.clone())],
119+
),
120+
EvalExpression::Partition(
121+
vec![Box::new(num_0.1), Box::new(num_1.1)],
122+
vec![(0, challenge.clone())],
123+
),
124+
],
125+
vec![challenge],
126+
)
127+
},
128+
);
129+
130+
// Preprocessing layer, compute table + challenge
131+
let ([table], []) = chip.allocate_wits_in_layer();
132+
133+
chip.add_layer(Layer::new(
134+
"Update_table".to_string(),
135+
LayerType::Linear,
136+
vec![lookup_challenge + table.0.into()],
137+
challenges,
138+
vec![table.1.clone()],
139+
vec![],
140+
vec![updated_table],
141+
));
142+
143+
chip.allocate_base_opening(self.committed_table_id, table.1);
144+
chip.allocate_base_opening(self.committed_count_id, count);
145+
}
146+
}
147+
148+
pub struct TowerChipTrace {
149+
pub table: Vec<u64>,
150+
pub multiplicity: Vec<u64>,
151+
}
152+
153+
impl<E> ProtocolWitnessGenerator<E> for TowerChipLayout<E>
154+
where
155+
E: ExtensionField,
156+
{
157+
type Trace = TowerChipTrace;
158+
159+
fn phase1_witness(&self, phase1: Self::Trace) -> Vec<Vec<E::BaseField>> {
160+
let mut res = vec![vec![]; 2];
161+
res[self.committed_table_id] = phase1
162+
.table
163+
.into_iter()
164+
.map(E::BaseField::from_u64)
165+
.collect();
166+
res[self.committed_count_id] = phase1
167+
.multiplicity
168+
.into_iter()
169+
.map(E::BaseField::from_u64)
170+
.collect();
171+
res
172+
}
173+
174+
fn gkr_witness(&self, phase1: &[Vec<E::BaseField>], challenges: &[E]) -> GKRCircuitWitness<E> {
175+
// Generate witnesses.
176+
let table = &phase1[self.committed_table_id];
177+
let count = &phase1[self.committed_count_id];
178+
let beta = self.lookup_challenge.entry(challenges);
179+
180+
// Compute table + beta.
181+
let n_layers = self.params.height + 1;
182+
let mut layer_wits = Vec::<LayerWitness<E>>::with_capacity(n_layers);
183+
layer_wits.push(LayerWitness::new(vec![table.clone()], vec![]));
184+
185+
// Compute den_0, den_1, num_0, num_1 for each layer.
186+
let updated_table = table.iter().cloned().map(|x| beta + x).collect_vec();
187+
188+
let (num_0, num_1): (Vec<E::BaseField>, Vec<E::BaseField>) = count.iter().tuples().unzip();
189+
let (den_0, den_1): (Vec<E>, Vec<E>) = updated_table.into_iter().tuples().unzip();
190+
let (mut last_den, mut last_num): (Vec<_>, Vec<_>) = izip!(&den_0, &den_1, &num_0, &num_1)
191+
.map(|(&den_0, &den_1, &num_0, &num_1)| (den_0 * den_1, den_0 * num_1 + den_1 * num_0))
192+
.unzip();
193+
194+
layer_wits.push(LayerWitness::new(vec![num_0, num_1], vec![den_0, den_1]));
195+
196+
layer_wits.extend((1..self.params.height).map(|_i| {
197+
let (den_0, den_1): (Vec<E>, Vec<E>) =
198+
mem::take(&mut last_den).into_iter().tuples().unzip();
199+
let (num_0, num_1): (Vec<E>, Vec<E>) =
200+
mem::take(&mut last_num).into_iter().tuples().unzip();
201+
202+
(last_den, last_num) = izip!(&den_0, &den_1, &num_0, &num_1)
203+
.map(|(&den_0, &den_1, &num_0, &num_1)| {
204+
(den_0 * den_1, den_0 * num_1 + den_1 * num_0)
205+
})
206+
.unzip();
207+
208+
LayerWitness::new(vec![], vec![den_0, den_1, num_0, num_1])
209+
}));
210+
layer_wits.reverse();
211+
212+
GKRCircuitWitness { layers: layer_wits }
213+
}
214+
}
215+
216+
fn main() {
217+
let log_size = 3;
218+
let params = TowerParams { height: log_size };
219+
let (layout, chip) = TowerChipLayout::build(params);
220+
let gkr_circuit = chip.gkr_circuit();
221+
222+
let (out_evals, gkr_proof) = {
223+
let table = (0..1 << log_size)
224+
.map(|_| OsRng.gen_range(0..1 << log_size as u64))
225+
.collect_vec();
226+
let count = (0..1 << log_size)
227+
.map(|_| OsRng.gen_range(0..1 << log_size as u64))
228+
.collect_vec();
229+
let phase1_witness = layout.phase1_witness(TowerChipTrace {
230+
table,
231+
multiplicity: count,
232+
});
233+
234+
let mut prover_transcript = BasicTranscript::<E>::new(b"protocol");
235+
236+
// Omit the commit phase1 and phase2.
237+
238+
let challenges = vec![
239+
prover_transcript
240+
.sample_and_append_challenge(b"lookup challenge")
241+
.elements,
242+
];
243+
let gkr_witness = layout.gkr_witness(&phase1_witness, &challenges);
244+
245+
#[cfg(debug_assertions)]
246+
{
247+
let last = gkr_witness.layers[0].exts.clone();
248+
MockProver::check(
249+
gkr_circuit.clone(),
250+
&gkr_witness,
251+
vec![
252+
VectorType::Ext(vec![last[0][0] * last[1][0]]),
253+
VectorType::Ext(vec![last[0][0] * last[3][0] + last[1][0] * last[2][0]]),
254+
],
255+
challenges.clone(),
256+
)
257+
.expect("Mock prover failed");
258+
}
259+
260+
let out_evals = {
261+
let last = gkr_witness.layers[0].exts.clone();
262+
let point = Arc::new(vec![]);
263+
assert_eq!(last[0].len(), 1);
264+
vec![
265+
PointAndEval {
266+
point: point.clone(),
267+
eval: last[0][0] * last[1][0],
268+
},
269+
PointAndEval {
270+
point,
271+
eval: last[0][0] * last[3][0] + last[1][0] * last[2][0],
272+
},
273+
]
274+
};
275+
let GKRProverOutput { gkr_proof, .. } = gkr_circuit
276+
.prove(gkr_witness, &out_evals, &challenges, &mut prover_transcript)
277+
.expect("Failed to prove phase");
278+
279+
// Omit the PCS opening phase.
280+
281+
(out_evals, gkr_proof)
282+
};
283+
284+
{
285+
let mut verifier_transcript = BasicTranscript::<E>::new(b"protocol");
286+
287+
// Omit the commit phase1 and phase2.
288+
let challenges = vec![
289+
verifier_transcript
290+
.sample_and_append_challenge(b"lookup challenge")
291+
.elements,
292+
];
293+
294+
gkr_circuit
295+
.verify(gkr_proof, &out_evals, &challenges, &mut verifier_transcript)
296+
.expect("GKR verify failed");
297+
298+
// Omit the PCS opening phase.
299+
}
300+
}

0 commit comments

Comments
 (0)