Skip to content

Commit c43aed7

Browse files
committed
feat: add MainPod circuit skeleton
1 parent 04f1b88 commit c43aed7

File tree

3 files changed

+315
-8
lines changed

3 files changed

+315
-8
lines changed

src/backends/plonky2/main.rs

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
use crate::backends::plonky2::basetypes::{Hash, Value, D, EMPTY_HASH, EMPTY_VALUE, F, VALUE_SIZE};
2+
use crate::backends::plonky2::mock_signed::MockSignedPod;
3+
use crate::backends::plonky2::primitives::merkletree::MerkleProofCircuit;
4+
use crate::backends::plonky2::primitives::merkletree::{MerkleProof, MerkleTree};
5+
use crate::middleware::{Operation, Params, Statement, STATEMENT_ARG_F_LEN};
6+
use anyhow::Result;
7+
use itertools::Itertools;
8+
use plonky2::field::extension::Extendable;
9+
use plonky2::hash::hash_types::RichField;
10+
use plonky2::{
11+
field::types::Field,
12+
hash::{
13+
hash_types::{HashOut, HashOutTarget},
14+
poseidon::PoseidonHash,
15+
},
16+
iop::{
17+
target::{BoolTarget, Target},
18+
witness::{PartialWitness, WitnessWrite},
19+
},
20+
plonk::circuit_builder::CircuitBuilder,
21+
};
22+
use std::collections::HashMap;
23+
24+
//
25+
// Common functionality to build Pod circuits with plonky2
26+
//
27+
28+
#[derive(Clone)]
29+
struct ValueTarget {
30+
pub elements: [Target; 4],
31+
}
32+
33+
#[derive(Clone)]
34+
struct StatementTarget {
35+
pub code: [Target; 6],
36+
pub args: Vec<[Target; STATEMENT_ARG_F_LEN]>,
37+
}
38+
39+
// TODO: Implement Operation::to_field to determine the size of each element
40+
#[derive(Clone)]
41+
struct OperationTarget {
42+
pub code: [Target; 6],
43+
pub args: Vec<[Target; STATEMENT_ARG_F_LEN]>,
44+
}
45+
46+
pub trait CircuitBuilderPod<F: RichField + Extendable<D>, const D: usize> {
47+
fn connect_values(&mut self, x: ValueTarget, y: ValueTarget);
48+
fn add_virtual_value(&mut self) -> ValueTarget;
49+
fn add_virtual_statement(&mut self, params: &Params) -> StatementTarget;
50+
fn add_virtual_operation(&mut self, params: &Params) -> OperationTarget;
51+
}
52+
53+
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilderPod<F, D>
54+
for CircuitBuilder<F, D>
55+
{
56+
fn connect_values(&mut self, x: ValueTarget, y: ValueTarget) {
57+
for i in 0..4 {
58+
self.connect(x.elements[i], y.elements[i]);
59+
}
60+
}
61+
62+
fn add_virtual_value(&mut self) -> ValueTarget {
63+
ValueTarget {
64+
elements: self.add_virtual_target_arr::<4>(),
65+
}
66+
}
67+
68+
fn add_virtual_statement(&mut self, params: &Params) -> StatementTarget {
69+
StatementTarget {
70+
code: self.add_virtual_target_arr::<6>(),
71+
args: (0..params.max_statement_args)
72+
.map(|_| self.add_virtual_target_arr::<STATEMENT_ARG_F_LEN>())
73+
.collect(),
74+
}
75+
}
76+
77+
fn add_virtual_operation(&mut self, params: &Params) -> OperationTarget {
78+
todo!()
79+
}
80+
}
81+
82+
const MD: usize = 32;
83+
84+
//
85+
// SignedPod verification
86+
//
87+
88+
struct SignedPodVerifyGate {
89+
params: Params,
90+
}
91+
92+
impl SignedPodVerifyGate {
93+
fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<SignedPodVerifyTarget> {
94+
let id = builder.add_virtual_hash();
95+
let mut mt_proofs = Vec::new();
96+
for _ in 0..self.params.max_signed_pod_values {
97+
let mt_proof = MerkleProofCircuit::<MD>::add_targets(builder)?;
98+
builder.connect_hashes(id, mt_proof.root);
99+
mt_proofs.push(mt_proof);
100+
}
101+
102+
Ok(SignedPodVerifyTarget {
103+
params: self.params.clone(),
104+
id,
105+
mt_proofs,
106+
})
107+
}
108+
}
109+
110+
struct SignedPodVerifyTarget {
111+
params: Params,
112+
id: HashOutTarget,
113+
mt_proofs: Vec<MerkleProofCircuit<MD>>,
114+
}
115+
116+
struct SignedPodVerifyInput {
117+
kvs: HashMap<Value, Value>,
118+
}
119+
120+
impl SignedPodVerifyTarget {
121+
fn kvs(&self) -> Vec<(HashOutTarget, ValueTarget)> {
122+
let mut kvs = Vec::new();
123+
for mt_proof in &self.mt_proofs {
124+
let key = HashOutTarget {
125+
elements: mt_proof.key.clone().try_into().expect("4 elements"),
126+
};
127+
let value = ValueTarget {
128+
elements: mt_proof.value.clone().try_into().expect("4 elements"),
129+
};
130+
kvs.push((key, value));
131+
}
132+
// TODO: when the slot is unused, do we force the kv to be (EMPTY, EMPTY), and then from
133+
// it get a ValueOf((id, EMPTY), EMPTY)? Or should we keep some boolean flags for unused
134+
// slots and translate them to Statement::None instead?
135+
kvs
136+
}
137+
138+
fn pub_statements(&self) -> Vec<StatementTarget> {
139+
todo!()
140+
}
141+
142+
fn set_targets(&self, pw: &mut PartialWitness<F>, input: &SignedPodVerifyInput) -> Result<()> {
143+
assert!(input.kvs.len() <= self.params.max_signed_pod_values);
144+
let tree = MerkleTree::new(MD, &input.kvs)?;
145+
for (i, (k, v)) in input.kvs.iter().sorted_by_key(|kv| kv.0).enumerate() {
146+
let (_, proof) = tree.prove(&k)?;
147+
self.mt_proofs[i].set_targets(pw, proof.existence, tree.root(), proof, *k, *v)?;
148+
}
149+
// Padding
150+
for i in input.kvs.len()..self.params.max_signed_pod_values {
151+
// TODO: We need to disable the proofs for the unused slots. We could add a flag
152+
// "enable" to the MerkleTree proof circuit that skips the verification when false.
153+
// self.mt_proofs[i].set_targets(pw, false, EMPTY_HASH, proof, *k, *v)?;
154+
}
155+
Ok(())
156+
}
157+
}
158+
159+
//
160+
// MainPod verification
161+
//
162+
163+
struct OperationVerifyGate {
164+
params: Params,
165+
}
166+
167+
impl OperationVerifyGate {
168+
fn eval(
169+
&self,
170+
builder: &mut CircuitBuilder<F, D>,
171+
st: &StatementTarget,
172+
op: &OperationTarget,
173+
prev_statements: &[StatementTarget],
174+
) -> Result<OperationVerifyTarget> {
175+
// Verify that the operation `op` correctly generates the statement `st`. The operation
176+
// can reference any of the `prev_statements`.
177+
// The verification may require aux data which needs to be stored in the
178+
// `OperationVerifyTarget` so that we can set during witness generation.
179+
todo!()
180+
}
181+
}
182+
183+
struct OperationVerifyTarget {
184+
// TODO
185+
}
186+
187+
struct OperationVerifyInputs {
188+
// TODO
189+
}
190+
191+
impl OperationVerifyTarget {
192+
fn set_targets(&self, pw: &mut PartialWitness<F>, input: &OperationVerifyInputs) -> Result<()> {
193+
todo!()
194+
}
195+
}
196+
197+
struct MainPodVerifyGate {
198+
params: Params,
199+
}
200+
201+
impl MainPodVerifyGate {
202+
fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<MainPodVerifyTarget> {
203+
let params = &self.params;
204+
// Verify all input signed pods
205+
let mut signed_pods = Vec::new();
206+
for _ in 0..params.max_input_signed_pods {
207+
let signed_pod = SignedPodVerifyGate {
208+
params: params.clone(),
209+
}
210+
.eval(builder)?;
211+
signed_pods.push(signed_pod);
212+
}
213+
214+
// Build the statement array
215+
let mut statements = Vec::new();
216+
for signed_pod in &signed_pods {
217+
statements.extend_from_slice(signed_pod.pub_statements().as_slice());
218+
}
219+
220+
// Add the input (private and public) statements and corresponding operations
221+
let mut operations = Vec::new();
222+
let input_statements_offset = statements.len();
223+
for _ in 0..params.max_statements {
224+
statements.push(builder.add_virtual_statement(params));
225+
operations.push(builder.add_virtual_operation(params));
226+
}
227+
let input_statements = &statements[input_statements_offset..];
228+
229+
// Verify input statements
230+
let mut op_verifications = Vec::new();
231+
for (i, (st, op)) in input_statements.iter().zip(operations.iter()).enumerate() {
232+
let prev_statements = &statements[..input_statements_offset + i - 1];
233+
let op_verification = OperationVerifyGate {
234+
params: params.clone(),
235+
}
236+
.eval(builder, st, op, prev_statements)?;
237+
op_verifications.push(op_verification);
238+
}
239+
240+
// Calculate the Pod Id from the public statements
241+
let pub_statements = &input_statements[statements.len() - params.max_public_statements..];
242+
let pub_statements_flat = pub_statements
243+
.iter()
244+
.map(|s| s.code.iter().chain(s.args.iter().flatten()))
245+
.flatten()
246+
.cloned()
247+
.collect();
248+
let id = builder.hash_n_to_hash_no_pad::<PoseidonHash>(pub_statements_flat);
249+
250+
Ok(MainPodVerifyTarget {
251+
params: params.clone(),
252+
id,
253+
signed_pods,
254+
statements: input_statements.to_vec(),
255+
operations,
256+
op_verifications,
257+
})
258+
}
259+
}
260+
261+
struct MainPodVerifyTarget {
262+
params: Params,
263+
id: HashOutTarget,
264+
signed_pods: Vec<SignedPodVerifyTarget>,
265+
statements: Vec<StatementTarget>,
266+
operations: Vec<OperationTarget>,
267+
op_verifications: Vec<OperationVerifyTarget>,
268+
}
269+
270+
struct MainPodVerifyInput {
271+
signed_pods: Vec<SignedPodVerifyInput>,
272+
}
273+
274+
impl MainPodVerifyTarget {
275+
fn set_targets(&self, pw: &mut PartialWitness<F>, input: &MainPodVerifyInput) -> Result<()> {
276+
assert!(input.signed_pods.len() <= self.params.max_input_signed_pods);
277+
for (i, signed_pod) in input.signed_pods.iter().enumerate() {
278+
self.signed_pods[i].set_targets(pw, signed_pod)?;
279+
}
280+
// Padding
281+
for i in input.signed_pods.len()..self.params.max_input_signed_pods {
282+
// TODO: We need to disable the verification for the unused slots.
283+
// self.signed_pods[i].set_targets(pw, signed_pod)?;
284+
}
285+
// TODO: set_targets for:
286+
// - statements
287+
// - operations
288+
// - op_verifications
289+
Ok(())
290+
}
291+
}
292+
293+
struct MainPodVerifyCircuit {
294+
params: Params,
295+
}
296+
297+
impl MainPodVerifyCircuit {
298+
fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<MainPodVerifyTarget> {
299+
let main_pod = MainPodVerifyGate {
300+
params: self.params.clone(),
301+
}
302+
.eval(builder)?;
303+
builder.register_public_inputs(&main_pod.id.elements);
304+
Ok(main_pod)
305+
}
306+
}

src/backends/plonky2/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod basetypes;
2+
pub mod main;
23
pub mod mock_main;
34
pub mod mock_signed;
45
pub mod primitives;

src/backends/plonky2/primitives/merkletree_circuit.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ use crate::backends::plonky2::basetypes::{Hash, Value, D, EMPTY_HASH, EMPTY_VALU
1717
use crate::backends::plonky2::primitives::merkletree::MerkleProof;
1818

1919
pub struct MerkleProofCircuit<const MAX_DEPTH: usize> {
20-
root: HashOutTarget,
21-
key: Vec<Target>,
22-
value: Vec<Target>,
23-
existence: BoolTarget,
24-
siblings: Vec<HashOutTarget>,
25-
case_ii_selector: BoolTarget, // for case ii)
26-
other_key: Vec<Target>,
27-
other_value: Vec<Target>,
20+
pub root: HashOutTarget,
21+
pub key: Vec<Target>,
22+
pub value: Vec<Target>,
23+
pub existence: BoolTarget,
24+
pub siblings: Vec<HashOutTarget>,
25+
pub case_ii_selector: BoolTarget, // for case ii)
26+
pub other_key: Vec<Target>,
27+
pub other_value: Vec<Target>,
2828
}
2929

3030
impl<const MAX_DEPTH: usize> MerkleProofCircuit<MAX_DEPTH> {

0 commit comments

Comments
 (0)