Skip to content

Commit 4f80cad

Browse files
committed
Merge branch 'main' into ec-point-compression
2 parents b80896e + b7ac54d commit 4f80cad

File tree

23 files changed

+760
-475
lines changed

23 files changed

+760
-475
lines changed

.github/workflows/clippy.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@ jobs:
1919
components: clippy
2020
- name: Check lints with clippy
2121
run: cargo clippy
22+
- name: Check lints with clippy (examples)
23+
run: cargo clippy --examples

examples/main_pod_points.rs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
//! Example of building main pods that verify signed pods and other main pods using custom
2+
//! predicates
3+
//!
4+
//! The example follows a scenario where a game issues signed pods to players with the points
5+
//! accumulated after finishing each game level. Then we build a custom predicate to prove that
6+
//! the sum of points from level 1 and 2 for a player is over 9000.
7+
//!
8+
//! Run in real mode: `cargo run --release --example main_pod_points`
9+
//! Run in mock mode: `cargo run --release --example main_pod_points -- --mock`
10+
use std::env;
11+
12+
use pod2::{
13+
backends::plonky2::{
14+
basetypes::DEFAULT_VD_SET, mainpod::Prover, mock::mainpod::MockProver,
15+
primitives::ec::schnorr::SecretKey, signedpod::Signer,
16+
},
17+
frontend::{MainPodBuilder, SignedPodBuilder},
18+
lang::parse,
19+
middleware::{Params, PodProver, PodType, VDSet, Value, KEY_SIGNER, KEY_TYPE},
20+
op,
21+
};
22+
23+
fn main() -> Result<(), Box<dyn std::error::Error>> {
24+
let args: Vec<String> = env::args().collect();
25+
let mock = args.get(1).is_some_and(|arg1| arg1 == "--mock");
26+
if mock {
27+
println!("Using MockMainPod")
28+
} else {
29+
println!("Using MainPod")
30+
}
31+
32+
let params = Params::default();
33+
34+
let mock_prover = MockProver {};
35+
let real_prover = Prover {};
36+
let (vd_set, prover): (_, &dyn PodProver) = if mock {
37+
(&VDSet::new(8, &[])?, &mock_prover)
38+
} else {
39+
println!("Prebuilding circuits to calculate vd_set...");
40+
let vd_set = &*DEFAULT_VD_SET;
41+
println!("vd_set calculation complete");
42+
(vd_set, &real_prover)
43+
};
44+
45+
// Create a schnorr key pair to sign pods
46+
let game_sk = SecretKey::new_rand();
47+
let game_pk = game_sk.public_key();
48+
49+
let mut game_signer = Signer(game_sk);
50+
51+
// Build 2 signed pods where the game assigns points to a player that has completed a level.
52+
let mut builder = SignedPodBuilder::new(&params);
53+
builder.insert("player", "Alice");
54+
builder.insert("level", 1);
55+
builder.insert("points", 3512);
56+
let pod_points_lvl_1 = builder.sign(&mut game_signer)?;
57+
pod_points_lvl_1.verify()?;
58+
println!("# pod_points_lvl_1:\n{}", pod_points_lvl_1);
59+
60+
let mut builder = SignedPodBuilder::new(&params);
61+
builder.insert("player", "Alice");
62+
builder.insert("level", 2);
63+
builder.insert("points", 5771);
64+
let pod_points_lvl_2 = builder.sign(&mut game_signer)?;
65+
pod_points_lvl_2.verify()?;
66+
println!("# pod_points_lvl_2:\n{}", pod_points_lvl_2);
67+
68+
// Build a MainPod to prove >9000 points from sum of level 1 and 2
69+
70+
// Declare the custom predicate
71+
let input = format!(
72+
r#"
73+
points(player, level, points, private: points_pod) = AND(
74+
Equal(?points_pod["{key_type}"], {pod_type})
75+
Equal(?points_pod["{key_signer}"], {game_pk:#})
76+
Equal(?points_pod["player"], ?player)
77+
Equal(?points_pod["level"], ?level)
78+
Equal(?points_pod["points"], ?points)
79+
)
80+
81+
over_9000(player, private: points_lvl_1, points_lvl_2, points_total) = AND(
82+
points(?player, 1, ?points_lvl_1)
83+
points(?player, 2, ?points_lvl_2)
84+
SumOf(?points_total, ?points_lvl_1, ?points_lvl_2)
85+
Gt(?points_total, 9000)
86+
)
87+
"#,
88+
key_type = KEY_TYPE,
89+
key_signer = KEY_SIGNER,
90+
pod_type = PodType::Signed as usize,
91+
game_pk = Value::from(game_pk).raw(),
92+
);
93+
println!("# custom predicate batch:{}", input);
94+
let batch = parse(&input, &params, &[])?.custom_batch;
95+
let points_pred = batch.predicate_ref_by_name("points").unwrap();
96+
let over_9000_pred = batch.predicate_ref_by_name("over_9000").unwrap();
97+
98+
// Build a pod to prove the statement `points("Alice", 1, 3512)`
99+
let mut builder = MainPodBuilder::new(&params, vd_set);
100+
builder.add_signed_pod(&pod_points_lvl_1);
101+
let st_type = builder.priv_op(op!(eq, (&pod_points_lvl_1, KEY_TYPE), PodType::Signed))?;
102+
let st_signer = builder.priv_op(op!(eq, (&pod_points_lvl_1, KEY_SIGNER), game_pk))?;
103+
let st_player = builder.priv_op(op!(eq, (&pod_points_lvl_1, "player"), "Alice"))?;
104+
let st_level = builder.priv_op(op!(eq, (&pod_points_lvl_1, "level"), 1))?;
105+
let st_points = builder.priv_op(op!(eq, (&pod_points_lvl_1, "points"), 3512))?;
106+
let st_points_lvl_1 = builder.pub_op(op!(
107+
custom,
108+
points_pred.clone(),
109+
st_type,
110+
st_signer,
111+
st_player,
112+
st_level,
113+
st_points
114+
))?;
115+
let pod_alice_lvl_1_points = builder.prove(prover, &params).unwrap();
116+
println!("# pod_alice_lvl_1_points\n:{}", pod_alice_lvl_1_points);
117+
pod_alice_lvl_1_points.pod.verify().unwrap();
118+
119+
// Build a pod to prove the statement `points("Alice", 2, 5771)`
120+
let mut builder = MainPodBuilder::new(&params, vd_set);
121+
builder.add_signed_pod(&pod_points_lvl_2);
122+
let st_type = builder.priv_op(op!(eq, (&pod_points_lvl_2, KEY_TYPE), PodType::Signed))?;
123+
let st_signer = builder.priv_op(op!(eq, (&pod_points_lvl_2, KEY_SIGNER), game_pk))?;
124+
let st_player = builder.priv_op(op!(eq, (&pod_points_lvl_2, "player"), "Alice"))?;
125+
let st_level = builder.priv_op(op!(eq, (&pod_points_lvl_2, "level"), 2))?;
126+
let st_points = builder.priv_op(op!(eq, (&pod_points_lvl_2, "points"), 5771))?;
127+
let st_points_lvl_2 = builder.pub_op(op!(
128+
custom,
129+
points_pred,
130+
st_type,
131+
st_signer,
132+
st_player,
133+
st_level,
134+
st_points
135+
))?;
136+
let pod_alice_lvl_2_points = builder.prove(prover, &params).unwrap();
137+
println!("# pod_alice_lvl_2_points\n:{}", pod_alice_lvl_2_points);
138+
pod_alice_lvl_2_points.pod.verify().unwrap();
139+
140+
// Build a pod to prove the statement `over_9000("Alice")`
141+
let mut builder = MainPodBuilder::new(&params, vd_set);
142+
builder.add_recursive_pod(pod_alice_lvl_1_points);
143+
builder.add_recursive_pod(pod_alice_lvl_2_points);
144+
let st_points_total = builder.priv_op(op!(sum_of, 3512 + 5771, 3512, 5771))?;
145+
let st_gt_9000 = builder.priv_op(op!(gt, 3512 + 5771, 9000))?;
146+
let _st_over_9000 = builder.pub_op(op!(
147+
custom,
148+
over_9000_pred,
149+
st_points_lvl_1,
150+
st_points_lvl_2,
151+
st_points_total,
152+
st_gt_9000
153+
));
154+
let pod_alice_over_9000 = builder.prove(prover, &params).unwrap();
155+
println!("# pod_alice_over_9000\n:{}", pod_alice_over_9000);
156+
pod_alice_over_9000.pod.verify().unwrap();
157+
158+
Ok(())
159+
}

examples/signed_pod.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//! Simple example of building a signed pod and verifying it
2+
//!
3+
//! Run: `cargo run --release --example signed_pod`
4+
use std::collections::HashSet;
5+
6+
use pod2::{
7+
backends::plonky2::{primitives::ec::schnorr::SecretKey, signedpod::Signer},
8+
frontend::SignedPodBuilder,
9+
middleware::{containers::Set, Params, Value},
10+
};
11+
12+
fn main() -> Result<(), Box<dyn std::error::Error>> {
13+
let params = Params::default();
14+
15+
// Create a schnorr key pair to sign the pod
16+
let sk = SecretKey::new_rand();
17+
let pk = sk.public_key();
18+
println!("Public key: {:?}\n", pk);
19+
20+
let mut signer = Signer(sk);
21+
22+
// Build the signed pod
23+
let mut builder = SignedPodBuilder::new(&params);
24+
// The values can be String, i64, bool, Array, Set, Dictionary, ...
25+
builder.insert("name", "Alice");
26+
builder.insert("lucky_number", 42);
27+
builder.insert("human", true);
28+
let friends_set: HashSet<Value> = ["Bob", "Charlie", "Dave"]
29+
.into_iter()
30+
.map(Value::from)
31+
.collect();
32+
builder.insert(
33+
"friends",
34+
Set::new(params.max_merkle_proofs_containers, friends_set)?,
35+
);
36+
37+
// Sign the pod and verify it
38+
let pod = builder.sign(&mut signer)?;
39+
pod.verify()?;
40+
41+
println!("{}", pod);
42+
43+
Ok(())
44+
}

src/backends/plonky2/basetypes.rs

Lines changed: 62 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
55
use plonky2::{
66
field::{extension::quadratic::QuadraticExtension, goldilocks_field::GoldilocksField},
7-
hash::poseidon::PoseidonHash,
7+
hash::{hash_types, poseidon::PoseidonHash},
88
plonk::{circuit_builder, circuit_data, config::GenericConfig, proof},
99
};
10-
use serde::Serialize;
10+
use schemars::JsonSchema;
11+
use serde::{Deserialize, Deserializer, Serialize};
1112

1213
/// F is the native field we use everywhere. Currently it's Goldilocks from plonky2
1314
pub type F = GoldilocksField;
@@ -36,12 +37,10 @@ pub type VerifierCircuitData = circuit_data::VerifierCircuitData<F, C, D>;
3637
pub type CircuitBuilder = circuit_builder::CircuitBuilder<F, D>;
3738
pub type Proof = proof::Proof<F, C, D>;
3839
pub type ProofWithPublicInputs = proof::ProofWithPublicInputs<F, C, D>;
40+
pub type HashOut = hash_types::HashOut<F>;
3941

4042
use std::{collections::HashMap, sync::LazyLock};
4143

42-
use itertools::Itertools;
43-
use plonky2::hash::hash_types::HashOut;
44-
4544
use crate::{
4645
backends::plonky2::{
4746
emptypod::STANDARD_EMPTY_POD_DATA, primitives::merkletree::MerkleClaimAndProof,
@@ -65,38 +64,32 @@ pub static DEFAULT_VD_SET: LazyLock<VDSet> = LazyLock::new(|| {
6564
/// verifying the recursive proofs of previous PODs appears in the VDSet.
6665
/// The VDSet struct that allows to get the specific merkle proofs for the given
6766
/// verifier_data.
68-
#[derive(Clone, Debug)]
67+
#[derive(Clone, Debug, Serialize, JsonSchema)]
6968
pub struct VDSet {
69+
#[serde(skip)]
70+
#[schemars(skip)]
7071
root: Hash,
7172
// (verifier_data's hash, merkleproof)
72-
proofs_map: HashMap<HashOut<F>, MerkleClaimAndProof>,
73+
#[serde(skip)]
74+
#[schemars(skip)]
75+
proofs_map: HashMap<Hash, MerkleClaimAndProof>,
76+
tree_depth: usize,
77+
vds_hashes: Vec<Hash>,
7378
}
74-
impl VDSet {
75-
/// builds the verifier_datas tree, and returns the root and the proofs
76-
pub fn new(tree_depth: usize, vds: &[VerifierOnlyCircuitData]) -> Result<Self> {
77-
// compute the verifier_data's hashes
78-
let vds_hashes: Vec<HashOut<F>> = vds
79-
.iter()
80-
.map(crate::backends::plonky2::recursion::circuit::hash_verifier_data)
81-
.collect::<Vec<_>>();
8279

80+
impl VDSet {
81+
fn new_from_vds_hashes(tree_depth: usize, mut vds_hashes: Vec<Hash>) -> Result<Self> {
8382
// before using the hash values, sort them, so that each set of
8483
// verifier_datas gets the same VDSet root
85-
let vds_hashes: Vec<&HashOut<F>> = vds_hashes
86-
.iter()
87-
.sorted_by_key(|vd| RawValue(vd.elements))
88-
.collect::<Vec<_>>();
84+
vds_hashes.sort();
8985

9086
let array = Array::new(
9187
tree_depth,
92-
vds_hashes
93-
.iter()
94-
.map(|vd| Value::from(RawValue(vd.elements)))
95-
.collect(),
88+
vds_hashes.iter().map(|vd| Value::from(*vd)).collect(),
9689
)?;
9790

9891
let root = array.commitment();
99-
let mut proofs_map = HashMap::<HashOut<F>, MerkleClaimAndProof>::new();
92+
let mut proofs_map = HashMap::<Hash, MerkleClaimAndProof>::new();
10093

10194
for (i, vd) in vds_hashes.iter().enumerate() {
10295
let (value, proof) = array.prove(i)?;
@@ -106,9 +99,29 @@ impl VDSet {
10699
value: value.raw(),
107100
proof,
108101
};
109-
proofs_map.insert(**vd, p);
102+
proofs_map.insert(*vd, p);
110103
}
111-
Ok(Self { root, proofs_map })
104+
Ok(Self {
105+
root,
106+
proofs_map,
107+
tree_depth,
108+
vds_hashes,
109+
})
110+
}
111+
/// builds the verifier_datas tree, and returns the root and the proofs
112+
pub fn new(tree_depth: usize, vds: &[VerifierOnlyCircuitData]) -> Result<Self> {
113+
// compute the verifier_data's hashes
114+
let vds_hashes: Vec<HashOut> = vds
115+
.iter()
116+
.map(crate::backends::plonky2::recursion::circuit::hash_verifier_data)
117+
.collect::<Vec<_>>();
118+
119+
let vds_hashes: Vec<Hash> = vds_hashes
120+
.into_iter()
121+
.map(|h| Hash(h.elements))
122+
.collect::<Vec<_>>();
123+
124+
Self::new_from_vds_hashes(tree_depth, vds_hashes)
112125
}
113126
pub fn root(&self) -> Hash {
114127
self.root
@@ -120,14 +133,36 @@ impl VDSet {
120133
) -> Result<Vec<MerkleClaimAndProof>> {
121134
let mut proofs: Vec<MerkleClaimAndProof> = vec![];
122135
for vd in vds {
136+
let verifier_data_hash =
137+
crate::backends::plonky2::recursion::circuit::hash_verifier_data(vd);
123138
let p = self
124139
.proofs_map
125-
.get(&crate::backends::plonky2::recursion::circuit::hash_verifier_data(vd))
140+
.get(&Hash(verifier_data_hash.elements))
126141
.ok_or(crate::middleware::Error::custom(
127142
"verifier_data not found in VDSet".to_string(),
128143
))?;
129144
proofs.push(p.clone());
130145
}
131146
Ok(proofs)
132147
}
148+
/// Returns true if the `verifier_data_hash` is in the set
149+
pub fn contains(&self, verifier_data_hash: HashOut) -> bool {
150+
self.proofs_map
151+
.contains_key(&Hash(verifier_data_hash.elements))
152+
}
153+
}
154+
155+
impl<'de> Deserialize<'de> for VDSet {
156+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
157+
where
158+
D: Deserializer<'de>,
159+
{
160+
#[derive(Deserialize)]
161+
struct Aux {
162+
tree_depth: usize,
163+
vds_hashes: Vec<Hash>,
164+
}
165+
let aux = Aux::deserialize(deserializer)?;
166+
VDSet::new_from_vds_hashes(aux.tree_depth, aux.vds_hashes).map_err(serde::de::Error::custom)
167+
}
133168
}

0 commit comments

Comments
 (0)