diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index a0821ce..e02e83b 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -19,3 +19,5 @@ jobs: components: clippy - name: Check lints with clippy run: cargo clippy + - name: Check lints with clippy (examples) + run: cargo clippy --examples diff --git a/examples/main_pod_points.rs b/examples/main_pod_points.rs new file mode 100644 index 0000000..4312f2c --- /dev/null +++ b/examples/main_pod_points.rs @@ -0,0 +1,159 @@ +//! Example of building main pods that verify signed pods and other main pods using custom +//! predicates +//! +//! The example follows a scenario where a game issues signed pods to players with the points +//! accumulated after finishing each game level. Then we build a custom predicate to prove that +//! the sum of points from level 1 and 2 for a player is over 9000. +//! +//! Run in real mode: `cargo run --release --example main_pod_points` +//! Run in mock mode: `cargo run --release --example main_pod_points -- --mock` +use std::env; + +use pod2::{ + backends::plonky2::{ + basetypes::DEFAULT_VD_SET, mainpod::Prover, mock::mainpod::MockProver, + primitives::ec::schnorr::SecretKey, signedpod::Signer, + }, + frontend::{MainPodBuilder, SignedPodBuilder}, + lang::parse, + middleware::{Params, PodProver, PodType, VDSet, Value, KEY_SIGNER, KEY_TYPE}, + op, +}; + +fn main() -> Result<(), Box> { + let args: Vec = env::args().collect(); + let mock = args.get(1).is_some_and(|arg1| arg1 == "--mock"); + if mock { + println!("Using MockMainPod") + } else { + println!("Using MainPod") + } + + let params = Params::default(); + + let mock_prover = MockProver {}; + let real_prover = Prover {}; + let (vd_set, prover): (_, &dyn PodProver) = if mock { + (&VDSet::new(8, &[])?, &mock_prover) + } else { + println!("Prebuilding circuits to calculate vd_set..."); + let vd_set = &*DEFAULT_VD_SET; + println!("vd_set calculation complete"); + (vd_set, &real_prover) + }; + + // Create a schnorr key pair to sign pods + let game_sk = SecretKey::new_rand(); + let game_pk = game_sk.public_key(); + + let mut game_signer = Signer(game_sk); + + // Build 2 signed pods where the game assigns points to a player that has completed a level. + let mut builder = SignedPodBuilder::new(¶ms); + builder.insert("player", "Alice"); + builder.insert("level", 1); + builder.insert("points", 3512); + let pod_points_lvl_1 = builder.sign(&mut game_signer)?; + pod_points_lvl_1.verify()?; + println!("# pod_points_lvl_1:\n{}", pod_points_lvl_1); + + let mut builder = SignedPodBuilder::new(¶ms); + builder.insert("player", "Alice"); + builder.insert("level", 2); + builder.insert("points", 5771); + let pod_points_lvl_2 = builder.sign(&mut game_signer)?; + pod_points_lvl_2.verify()?; + println!("# pod_points_lvl_2:\n{}", pod_points_lvl_2); + + // Build a MainPod to prove >9000 points from sum of level 1 and 2 + + // Declare the custom predicate + let input = format!( + r#" + points(player, level, points, private: points_pod) = AND( + Equal(?points_pod["{key_type}"], {pod_type}) + Equal(?points_pod["{key_signer}"], {game_pk:#}) + Equal(?points_pod["player"], ?player) + Equal(?points_pod["level"], ?level) + Equal(?points_pod["points"], ?points) + ) + + over_9000(player, private: points_lvl_1, points_lvl_2, points_total) = AND( + points(?player, 1, ?points_lvl_1) + points(?player, 2, ?points_lvl_2) + SumOf(?points_total, ?points_lvl_1, ?points_lvl_2) + Gt(?points_total, 9000) + ) + "#, + key_type = KEY_TYPE, + key_signer = KEY_SIGNER, + pod_type = PodType::Signed as usize, + game_pk = Value::from(game_pk).raw(), + ); + println!("# custom predicate batch:{}", input); + let batch = parse(&input, ¶ms, &[])?.custom_batch; + let points_pred = batch.predicate_ref_by_name("points").unwrap(); + let over_9000_pred = batch.predicate_ref_by_name("over_9000").unwrap(); + + // Build a pod to prove the statement `points("Alice", 1, 3512)` + let mut builder = MainPodBuilder::new(¶ms, vd_set); + builder.add_signed_pod(&pod_points_lvl_1); + let st_type = builder.priv_op(op!(eq, (&pod_points_lvl_1, KEY_TYPE), PodType::Signed))?; + let st_signer = builder.priv_op(op!(eq, (&pod_points_lvl_1, KEY_SIGNER), game_pk))?; + let st_player = builder.priv_op(op!(eq, (&pod_points_lvl_1, "player"), "Alice"))?; + let st_level = builder.priv_op(op!(eq, (&pod_points_lvl_1, "level"), 1))?; + let st_points = builder.priv_op(op!(eq, (&pod_points_lvl_1, "points"), 3512))?; + let st_points_lvl_1 = builder.pub_op(op!( + custom, + points_pred.clone(), + st_type, + st_signer, + st_player, + st_level, + st_points + ))?; + let pod_alice_lvl_1_points = builder.prove(prover, ¶ms).unwrap(); + println!("# pod_alice_lvl_1_points\n:{}", pod_alice_lvl_1_points); + pod_alice_lvl_1_points.pod.verify().unwrap(); + + // Build a pod to prove the statement `points("Alice", 2, 5771)` + let mut builder = MainPodBuilder::new(¶ms, vd_set); + builder.add_signed_pod(&pod_points_lvl_2); + let st_type = builder.priv_op(op!(eq, (&pod_points_lvl_2, KEY_TYPE), PodType::Signed))?; + let st_signer = builder.priv_op(op!(eq, (&pod_points_lvl_2, KEY_SIGNER), game_pk))?; + let st_player = builder.priv_op(op!(eq, (&pod_points_lvl_2, "player"), "Alice"))?; + let st_level = builder.priv_op(op!(eq, (&pod_points_lvl_2, "level"), 2))?; + let st_points = builder.priv_op(op!(eq, (&pod_points_lvl_2, "points"), 5771))?; + let st_points_lvl_2 = builder.pub_op(op!( + custom, + points_pred, + st_type, + st_signer, + st_player, + st_level, + st_points + ))?; + let pod_alice_lvl_2_points = builder.prove(prover, ¶ms).unwrap(); + println!("# pod_alice_lvl_2_points\n:{}", pod_alice_lvl_2_points); + pod_alice_lvl_2_points.pod.verify().unwrap(); + + // Build a pod to prove the statement `over_9000("Alice")` + let mut builder = MainPodBuilder::new(¶ms, vd_set); + builder.add_recursive_pod(pod_alice_lvl_1_points); + builder.add_recursive_pod(pod_alice_lvl_2_points); + let st_points_total = builder.priv_op(op!(sum_of, 3512 + 5771, 3512, 5771))?; + let st_gt_9000 = builder.priv_op(op!(gt, 3512 + 5771, 9000))?; + let _st_over_9000 = builder.pub_op(op!( + custom, + over_9000_pred, + st_points_lvl_1, + st_points_lvl_2, + st_points_total, + st_gt_9000 + )); + let pod_alice_over_9000 = builder.prove(prover, ¶ms).unwrap(); + println!("# pod_alice_over_9000\n:{}", pod_alice_over_9000); + pod_alice_over_9000.pod.verify().unwrap(); + + Ok(()) +} diff --git a/examples/signed_pod.rs b/examples/signed_pod.rs new file mode 100644 index 0000000..1081aa6 --- /dev/null +++ b/examples/signed_pod.rs @@ -0,0 +1,44 @@ +//! Simple example of building a signed pod and verifying it +//! +//! Run: `cargo run --release --example signed_pod` +use std::collections::HashSet; + +use pod2::{ + backends::plonky2::{primitives::ec::schnorr::SecretKey, signedpod::Signer}, + frontend::SignedPodBuilder, + middleware::{containers::Set, Params, Value}, +}; + +fn main() -> Result<(), Box> { + let params = Params::default(); + + // Create a schnorr key pair to sign the pod + let sk = SecretKey::new_rand(); + let pk = sk.public_key(); + println!("Public key: {:?}\n", pk); + + let mut signer = Signer(sk); + + // Build the signed pod + let mut builder = SignedPodBuilder::new(¶ms); + // The values can be String, i64, bool, Array, Set, Dictionary, ... + builder.insert("name", "Alice"); + builder.insert("lucky_number", 42); + builder.insert("human", true); + let friends_set: HashSet = ["Bob", "Charlie", "Dave"] + .into_iter() + .map(Value::from) + .collect(); + builder.insert( + "friends", + Set::new(params.max_merkle_proofs_containers, friends_set)?, + ); + + // Sign the pod and verify it + let pod = builder.sign(&mut signer)?; + pod.verify()?; + + println!("{}", pod); + + Ok(()) +} diff --git a/src/examples/mod.rs b/src/examples/mod.rs index 10213d2..5d8ed28 100644 --- a/src/examples/mod.rs +++ b/src/examples/mod.rs @@ -155,7 +155,7 @@ impl EthDosHelper { let mut pod = MainPodBuilder::new(&self.params, &self.vd_set); pod.add_signed_pod(int_attestation); - pod.add_main_pod(eth_dos_src_to_int_pod.clone()); + pod.add_recursive_pod(eth_dos_src_to_int_pod.clone()); let eth_dos_int_to_dst = eth_dos_src_to_int_pod .pod diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs index 4111854..316ed50 100644 --- a/src/frontend/mod.rs +++ b/src/frontend/mod.rs @@ -120,7 +120,7 @@ pub struct MainPodBuilder { pub params: Params, pub vd_set: VDSet, pub input_signed_pods: Vec, - pub input_main_pods: Vec, + pub input_recursive_pods: Vec, pub statements: Vec, pub operations: Vec, pub public_statements: Vec, @@ -139,7 +139,7 @@ impl fmt::Display for MainPodBuilder { writeln!(f, " - {}", in_pod.id())?; } writeln!(f, " input_main_pods:")?; - for in_pod in &self.input_main_pods { + for in_pod in &self.input_recursive_pods { writeln!(f, " - {}", in_pod.id())?; } writeln!(f, " statements:")?; @@ -158,7 +158,7 @@ impl MainPodBuilder { params: params.clone(), vd_set: vd_set.clone(), input_signed_pods: Vec::new(), - input_main_pods: Vec::new(), + input_recursive_pods: Vec::new(), statements: Vec::new(), operations: Vec::new(), public_statements: Vec::new(), @@ -169,8 +169,8 @@ impl MainPodBuilder { pub fn add_signed_pod(&mut self, pod: &SignedPod) { self.input_signed_pods.push(pod.clone()); } - pub fn add_main_pod(&mut self, pod: MainPod) { - self.input_main_pods.push(pod); + pub fn add_recursive_pod(&mut self, pod: MainPod) { + self.input_recursive_pods.push(pod); } pub fn insert(&mut self, public: bool, st_op: (Statement, Operation)) { // TODO: Do error handling instead of panic @@ -538,7 +538,7 @@ impl MainPodBuilder { self.public_statements.push(st.clone()); } - pub fn prove(&self, prover: &mut P, params: &Params) -> Result { + pub fn prove(&self, prover: &dyn PodProver, params: &Params) -> Result { let compiler = MainPodCompiler::new(&self.params); let inputs = MainPodCompilerInputs { // signed_pods: &self.input_signed_pods, @@ -557,7 +557,7 @@ impl MainPodBuilder { .map(|p| p.pod.as_ref()) .collect_vec(), recursive_pods: &self - .input_main_pods + .input_recursive_pods .iter() .map(|p| p.pod.as_ref()) .collect_vec(), diff --git a/src/frontend/operation.rs b/src/frontend/operation.rs index 0c4aaf8..abbfbf7 100644 --- a/src/frontend/operation.rs +++ b/src/frontend/operation.rs @@ -45,9 +45,9 @@ impl fmt::Display for OperationArg { } } -impl From for OperationArg { - fn from(v: Value) -> Self { - Self::Literal(v) +impl> From for OperationArg { + fn from(value: V) -> Self { + Self::Literal(value.into()) } } @@ -57,24 +57,6 @@ impl From<&Value> for OperationArg { } } -impl From<&str> for OperationArg { - fn from(s: &str) -> Self { - Self::Literal(Value::from(s)) - } -} - -impl From for OperationArg { - fn from(v: i64) -> Self { - Self::Literal(Value::from(v)) - } -} - -impl From for OperationArg { - fn from(b: bool) -> Self { - Self::Literal(Value::from(b)) - } -} - impl From<(&SignedPod, &str)> for OperationArg { fn from((pod, key): (&SignedPod, &str)) -> Self { // TODO: TryFrom. diff --git a/src/middleware/basetypes.rs b/src/middleware/basetypes.rs index f54a413..86cf2b6 100644 --- a/src/middleware/basetypes.rs +++ b/src/middleware/basetypes.rs @@ -121,16 +121,7 @@ impl From for RawValue { impl fmt::Display for RawValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.0[2].is_zero() && self.0[3].is_zero() { - // Assume this is an integer - let (l0, l1) = (self.0[0].to_canonical_u64(), self.0[1].to_canonical_u64()); - assert!(l0 < (1 << 32)); - assert!(l1 < (1 << 32)); - write!(f, "{}", l0 + l1 * (1 << 32)) - } else { - // Assume this is a hash - Hash(self.0).fmt(f) - } + Hash(self.0).fmt(f) } } @@ -206,14 +197,19 @@ impl Ord for Hash { } } -// TODO: In alternate mode, don't shorten the hash impl fmt::Display for Hash { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let v0 = self.0[0].to_canonical_u64(); - for i in 0..HASH_SIZE { - write!(f, "{:02x}", (v0 >> (i * 8)) & 0xff)?; + if f.alternate() { + write!(f, "0x{}", self.encode_hex::()) + } else { + // display first hex digit in big endian + write!(f, "0x")?; + let v3 = self.0[3].to_canonical_u64(); + for i in 0..4 { + write!(f, "{:02x}", (v3 >> ((7 - i) * 8)) & 0xff)?; + } + write!(f, "…") } - write!(f, "…") } }