diff --git a/book/src/custom2.md b/book/src/custom2.md index 882b740..f333444 100644 --- a/book/src/custom2.md +++ b/book/src/custom2.md @@ -58,8 +58,8 @@ An example with keys, that expresses the same semantic meaning: ``` ValueOf(local_varname, "local_ssn") ValueOf(remote_varname, "ssn") -ValueOf(gov_id_pod_id, 0x4030) -ValueFromPodKey(local_varname, gov_id_pod_id, remote_varname) +ValueOf(gov_id_root, 0x4030) +ValueFromPodKey(local_varname, gov_id_root, remote_varname) ``` ## Summary of additional statements in this spec diff --git a/examples/main_pod_points.rs b/examples/main_pod_points.rs index e60e9ed..fb049be 100644 --- a/examples/main_pod_points.rs +++ b/examples/main_pod_points.rs @@ -13,11 +13,11 @@ use std::env; use pod2::{ backends::plonky2::{ basetypes::DEFAULT_VD_SET, mainpod::Prover, mock::mainpod::MockProver, - primitives::ec::schnorr::SecretKey, signedpod::Signer, + primitives::ec::schnorr::SecretKey, signer::Signer, }, - frontend::{MainPodBuilder, Operation, SignedPodBuilder}, + frontend::{MainPodBuilder, Operation, SignedDictBuilder}, lang::parse, - middleware::{Params, PodProver, PodType, VDSet, Value, KEY_SIGNER, KEY_TYPE}, + middleware::{MainPodProver, Params, VDSet}, }; fn main() -> Result<(), Box> { @@ -34,7 +34,7 @@ fn main() -> Result<(), Box> { let mock_prover = MockProver {}; let real_prover = Prover {}; - let (vd_set, prover): (_, &dyn PodProver) = if mock { + let (vd_set, prover): (_, &dyn MainPodProver) = if mock { (&VDSet::new(8, &[])?, &mock_prover) } else { println!("Prebuilding circuits to calculate vd_set..."); @@ -50,7 +50,7 @@ fn main() -> Result<(), Box> { let 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); + let mut builder = SignedDictBuilder::new(¶ms); builder.insert("player", "Alice"); builder.insert("level", 1); builder.insert("points", 3512); @@ -58,7 +58,7 @@ fn main() -> Result<(), Box> { pod_points_lvl_1.verify()?; println!("# pod_points_lvl_1:\n{}", pod_points_lvl_1); - let mut builder = SignedPodBuilder::new(¶ms); + let mut builder = SignedDictBuilder::new(¶ms); builder.insert("player", "Alice"); builder.insert("level", 2); builder.insert("points", 5771); @@ -71,12 +71,11 @@ fn main() -> Result<(), Box> { // 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) + points(player, level, points, private: points_dict) = AND( + SignedBy(?points_dict, PublicKey({game_pk})) + Contains(?points_dict, "player", ?player) + Contains(?points_dict, "level", ?level) + Contains(?points_dict, "points", ?points) ) over_9000(player, private: points_lvl_1, points_lvl_2, points_total) = AND( @@ -86,10 +85,7 @@ fn main() -> Result<(), Box> { 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(), + game_pk = game_pk, ); println!("# custom predicate batch:{}", input); let batch = parse(&input, ¶ms, &[])?.custom_batch; @@ -98,37 +94,53 @@ fn main() -> Result<(), Box> { // 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(Operation::eq( - (&pod_points_lvl_1, KEY_TYPE), - PodType::Signed, + let st_signed_by = builder.priv_op(Operation::dict_signed_by(&pod_points_lvl_1))?; + let st_player = builder.priv_op(Operation::dict_contains( + pod_points_lvl_1.dict.clone(), + "player", + "Alice", + ))?; + let st_level = builder.priv_op(Operation::dict_contains( + pod_points_lvl_1.dict.clone(), + "level", + 1, + ))?; + let st_points = builder.priv_op(Operation::dict_contains( + pod_points_lvl_1.dict.clone(), + "points", + 3512, ))?; - let st_signer = builder.priv_op(Operation::eq((&pod_points_lvl_1, KEY_SIGNER), game_pk))?; - let st_player = builder.priv_op(Operation::eq((&pod_points_lvl_1, "player"), "Alice"))?; - let st_level = builder.priv_op(Operation::eq((&pod_points_lvl_1, "level"), 1))?; - let st_points = builder.priv_op(Operation::eq((&pod_points_lvl_1, "points"), 3512))?; let st_points_lvl_1 = builder.pub_op(Operation::custom( points_pred.clone(), - [st_type, st_signer, st_player, st_level, st_points], + [st_signed_by, st_player, st_level, st_points], ))?; + let pod_alice_lvl_1_points = builder.prove(prover).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(Operation::eq( - (&pod_points_lvl_2, KEY_TYPE), - PodType::Signed, + let st_signed_by = builder.priv_op(Operation::dict_signed_by(&pod_points_lvl_2))?; + let st_player = builder.priv_op(Operation::dict_contains( + pod_points_lvl_2.dict.clone(), + "player", + "Alice", ))?; - let st_signer = builder.priv_op(Operation::eq((&pod_points_lvl_2, KEY_SIGNER), game_pk))?; - let st_player = builder.priv_op(Operation::eq((&pod_points_lvl_2, "player"), "Alice"))?; - let st_level = builder.priv_op(Operation::eq((&pod_points_lvl_2, "level"), 2))?; - let st_points = builder.priv_op(Operation::eq((&pod_points_lvl_2, "points"), 5771))?; + let st_level = builder.priv_op(Operation::dict_contains( + pod_points_lvl_2.dict.clone(), + "level", + 2, + ))?; + let st_points = builder.priv_op(Operation::dict_contains( + pod_points_lvl_2.dict.clone(), + "points", + 5771, + ))?; + let st_points_lvl_2 = builder.pub_op(Operation::custom( points_pred, - [st_type, st_signer, st_player, st_level, st_points], + [st_signed_by, st_player, st_level, st_points], ))?; let pod_alice_lvl_2_points = builder.prove(prover).unwrap(); println!("# pod_alice_lvl_2_points\n:{}", pod_alice_lvl_2_points); @@ -136,8 +148,8 @@ fn main() -> Result<(), Box> { // 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); + builder.add_pod(pod_alice_lvl_1_points); + builder.add_pod(pod_alice_lvl_2_points); let st_points_total = builder.priv_op(Operation::sum_of(3512 + 5771, 3512, 5771))?; let st_gt_9000 = builder.priv_op(Operation::gt(3512 + 5771, 9000))?; let _st_over_9000 = builder.pub_op(Operation::custom( diff --git a/examples/signed_pod.rs b/examples/signed_dict.rs similarity index 67% rename from examples/signed_pod.rs rename to examples/signed_dict.rs index 6b09661..f02b921 100644 --- a/examples/signed_pod.rs +++ b/examples/signed_dict.rs @@ -1,27 +1,27 @@ #![allow(clippy::uninlined_format_args)] // TODO: Remove this in another PR -//! Simple example of building a signed pod and verifying it +//! Simple example of building a signed dict and verifying it //! -//! Run: `cargo run --release --example signed_pod` +//! Run: `cargo run --release --example signed_dict` use std::collections::HashSet; use pod2::{ - backends::plonky2::{primitives::ec::schnorr::SecretKey, signedpod::Signer}, - frontend::SignedPodBuilder, + backends::plonky2::{primitives::ec::schnorr::SecretKey, signer::Signer}, + frontend::SignedDictBuilder, middleware::{containers::Set, Params, Value}, }; fn main() -> Result<(), Box> { let params = Params::default(); - // Create a schnorr key pair to sign the pod + // Create a schnorr key pair to sign the dict let sk = SecretKey::new_rand(); let pk = sk.public_key(); println!("Public key: {}\n", pk); let signer = Signer(sk); - // Build the signed pod - let mut builder = SignedPodBuilder::new(¶ms); + // Build the signed dict + let mut builder = SignedDictBuilder::new(¶ms); // The values can be String, i64, bool, Array, Set, Dictionary, ... builder.insert("name", "Alice"); builder.insert("lucky_number", 42); @@ -35,11 +35,11 @@ fn main() -> Result<(), Box> { Set::new(params.max_merkle_proofs_containers, friends_set)?, ); - // Sign the pod and verify it - let pod = builder.sign(&signer)?; - pod.verify()?; + // Sign the dict and verify it + let signed_dict = builder.sign(&signer)?; + signed_dict.verify()?; - println!("{}", pod); + println!("{}", signed_dict); Ok(()) } diff --git a/src/backends/plonky2/basetypes.rs b/src/backends/plonky2/basetypes.rs index 180d1cd..7a94551 100644 --- a/src/backends/plonky2/basetypes.rs +++ b/src/backends/plonky2/basetypes.rs @@ -37,12 +37,17 @@ pub type CircuitBuilder = circuit_builder::CircuitBuilder; pub type Proof = proof::Proof; pub type ProofWithPublicInputs = proof::ProofWithPublicInputs; pub type HashOut = hash_types::HashOut; - use std::{collections::HashMap, sync::LazyLock}; +pub use crate::backends::plonky2::{ + primitives::ec::{ + curve::Point as PublicKey, + schnorr::{SecretKey, Signature}, + }, + recursion::circuit::hash_verifier_data, +}; use crate::{ backends::plonky2::{ - emptypod::cache_get_standard_empty_pod_verifier_circuit_data, mainpod::cache_get_rec_main_pod_verifier_circuit_data, primitives::merkletree::MerkleClaimAndProof, }, @@ -51,14 +56,12 @@ use crate::{ pub static DEFAULT_VD_LIST: LazyLock> = LazyLock::new(|| { let params = Params::default(); - vec![ - cache_get_rec_main_pod_verifier_circuit_data(¶ms) - .verifier_only - .clone(), - cache_get_standard_empty_pod_verifier_circuit_data() - .verifier_only - .clone(), - ] + // NOTE: We only include the recursive MainPod with default parameters here. We don't need to + // include the verifying key of the EmptyPod because it's an Introduction pod and its verifying + // key appears in its statement in a self-describing way. + vec![cache_get_rec_main_pod_verifier_circuit_data(¶ms) + .verifier_only + .clone()] }); pub static DEFAULT_VD_SET: LazyLock = LazyLock::new(|| { @@ -144,23 +147,16 @@ impl VDSet { self.root } /// returns the vector of merkle proofs corresponding to the given verifier_datas - pub fn get_vds_proofs( - &self, - vds: &[VerifierOnlyCircuitData], - ) -> Result> { - let mut proofs: Vec = vec![]; - for vd in vds { - let verifier_data_hash = - crate::backends::plonky2::recursion::circuit::hash_verifier_data(vd); - let p = self - .proofs_map - .get(&Hash(verifier_data_hash.elements)) - .ok_or(crate::middleware::Error::custom( - "verifier_data not found in VDSet".to_string(), - ))?; - proofs.push(p.clone()); - } - Ok(proofs) + pub fn get_vds_proof(&self, vd: &VerifierOnlyCircuitData) -> Result { + let verifier_data_hash = + crate::backends::plonky2::recursion::circuit::hash_verifier_data(vd); + Ok(self + .proofs_map + .get(&Hash(verifier_data_hash.elements)) + .ok_or(crate::middleware::Error::custom( + "verifier_data not found in VDSet".to_string(), + ))? + .clone()) } /// Returns true if the `verifier_data_hash` is in the set pub fn contains(&self, verifier_data_hash: HashOut) -> bool { diff --git a/src/backends/plonky2/circuits/common.rs b/src/backends/plonky2/circuits/common.rs index 934fde9..9c7ccb8 100644 --- a/src/backends/plonky2/circuits/common.rs +++ b/src/backends/plonky2/circuits/common.rs @@ -111,10 +111,10 @@ impl StatementArgTarget { pub fn anchored_key( _builder: &mut CircuitBuilder, - pod_id: &ValueTarget, + dict: &ValueTarget, key: &ValueTarget, ) -> Self { - Self::new(*pod_id, *key) + Self::new(*dict, *key) } pub fn wildcard_literal(builder: &mut CircuitBuilder, value: &ValueTarget) -> Self { @@ -360,6 +360,20 @@ impl PredicateTarget { } } + pub fn new_intro(builder: &mut CircuitBuilder, vd_hash: HashOutTarget) -> Self { + let prefix = builder.constant(F::from(PredicatePrefix::Intro)); + let vh = vd_hash.elements; + let zero = builder.zero(); + Self { + elements: [prefix, vh[0], vh[1], vh[2], vh[3], zero], + } + } + + pub fn is_intro(&self, builder: &mut CircuitBuilder) -> BoolTarget { + let prefix = builder.constant(F::from(PredicatePrefix::Intro)); + builder.is_equal(prefix, self.elements[0]) + } + pub fn set_targets( &self, pw: &mut PartialWitness, diff --git a/src/backends/plonky2/circuits/mainpod.rs b/src/backends/plonky2/circuits/mainpod.rs index d68431b..763d13f 100644 --- a/src/backends/plonky2/circuits/mainpod.rs +++ b/src/backends/plonky2/circuits/mainpod.rs @@ -30,39 +30,40 @@ use crate::{ }, hash::{hash_from_state_circuit, precompute_hash_state}, mux_table::{MuxTableTarget, TableEntryTarget}, - signedpod::{verify_signed_pod_circuit, SignedPodVerifyTarget}, }, - emptypod::{cache_get_standard_empty_pod_circuit_data, EmptyPod}, + emptypod::EmptyPod, error::Result, - mainpod::{self, pad_statement}, + mainpod::{self, pad_statement, SignedBy}, primitives::{ ec::{ bits::{BigUInt320Target, CircuitBuilderBits}, - curve::{CircuitBuilderElliptic, Point, WitnessWriteCurve, GROUP_ORDER}, - schnorr::SecretKey, + curve::{ + CircuitBuilderElliptic, Point, PointTarget, WitnessWriteCurve, GROUP_ORDER, + }, + schnorr::{CircuitBuilderSchnorr, SecretKey, SignatureTarget, WitnessWriteSchnorr}, }, merkletree::{ verify_merkle_proof_circuit, verify_merkle_state_transition_circuit, MerkleClaimAndProof, MerkleClaimAndProofTarget, MerkleTreeOp, MerkleTreeStateTransitionProof, MerkleTreeStateTransitionProofTarget, }, + signature::{verify_signature_circuit, SignatureVerifyTarget}, }, recursion::{InnerCircuit, VerifiedProofTarget}, - signedpod::SignedPod, }, measure_gates_begin, measure_gates_end, middleware::{ - AnchoredKey, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, NativeOperation, - NativePredicate, Params, PodType, PredicatePrefix, Statement, StatementArg, ToFields, - Value, ValueRef, F, HASH_SIZE, KEY_TYPE, SELF, VALUE_SIZE, + CustomPredicate, CustomPredicateBatch, CustomPredicateRef, NativeOperation, + NativePredicate, Params, PredicatePrefix, RawValue, Statement, ToFields, Value, F, + HASH_SIZE, }, }; // // MainPod verification // -/// Offset in public inputs where we store the pod id -pub const PI_OFFSET_ID: usize = 0; +/// Offset in public inputs where we store the statements hash +pub const PI_OFFSET_STATEMENTS_HASH: usize = 0; /// Offset in public inputs where we store the verified data array root pub const PI_OFFSET_VDSROOT: usize = 4; @@ -108,14 +109,25 @@ impl StatementCache { let pred_is_none = op_args[i].has_native_type(builder, params, NativePredicate::None); let arg_is_value = builder.statement_arg_is_value(&st.args[i]); let is_literal = builder.and(pred_is_none, arg_is_value); - let pred_is_eq = op_args[i].has_native_type(builder, params, NativePredicate::Equal); - let ref_is_value = builder.statement_arg_is_value(&op_args[i].args[1]); - let is_reference = builder.and(pred_is_eq, ref_is_value); + let pred_is_contains = + op_args[i].has_native_type(builder, params, NativePredicate::Contains); + let ref_is_value_arg: [_; 3] = + array::from_fn(|j| builder.statement_arg_is_value(&op_args[i].args[j])); + let ref_is_value = builder.and(ref_is_value_arg[0], ref_is_value_arg[1]); + let ref_is_value = builder.and(ref_is_value, ref_is_value_arg[2]); + + let is_reference = builder.and(pred_is_contains, ref_is_value); let valid = builder.or(is_literal, is_reference); let rhs_literal = st.args[i].as_value(); - let rhs_reference = op_args[i].args[1].as_value(); + let rhs_reference = op_args[i].args[2].as_value(); let rhs = builder.select_value(pred_is_none, rhs_literal, rhs_reference); - let lhs = builder.select_statement_arg(pred_is_none, &st.args[i], &op_args[i].args[0]); + let lhs_literal = &st.args[i]; + let lhs_reference = StatementArgTarget::anchored_key( + builder, + &op_args[i].args[0].as_value(), + &op_args[i].args[1].as_value(), + ); + let lhs = builder.select_statement_arg(pred_is_none, lhs_literal, &lhs_reference); StatementArgCache { rhs, lhs, valid } }); let mut first_n_equations_valid = [equations[0].valid; MAX_VALUE_ARGS]; @@ -157,7 +169,6 @@ fn verify_operation_public_statement_circuit( st: &StatementTarget, op: &OperationTarget, prev_statements: &[StatementTarget], - input_statements_offset: usize, ) -> Result<()> { let measure = measure_gates_begin!(builder, "OpVerify"); @@ -170,14 +181,6 @@ fn verify_operation_public_statement_circuit( let op_checks = vec![ verify_none_circuit(params, builder, st, &op.op_type), - verify_new_entry_circuit( - params, - builder, - st, - &op.op_type, - prev_statements, - input_statements_offset, - ), verify_copy_circuit(builder, st, &op.op_type, &cache.op_args), ]; @@ -192,14 +195,16 @@ enum OperationAuxTableTag { None = 0, MerkleProof = 1, PublicKeyOf = 2, - MerkleTreeStateTransitionProof = 3, - CustomPredVerify = 4, + SignedBy = 3, + MerkleTreeStateTransitionProof = 4, + CustomPredVerify = 5, } fn max_operation_aux_entry_len(params: &Params) -> usize { [ (params.max_merkle_proofs_containers > 0).then(|| MerkleClaimTarget::size(params)), - (params.max_public_key_of > 0).then(|| KeyPairTarget::size(params)), + (params.max_public_key_of > 0).then(|| PubKeySecKeyTarget::size(params)), + (params.max_signed_by > 0).then(|| MsgPubKeyTarget::size(params)), (params.max_merkle_tree_state_transition_proofs_containers > 0) .then(|| MerkleTreeStateTransitionClaimTarget::size(params)), (params.max_custom_predicate_verifications > 0) @@ -212,36 +217,58 @@ fn max_operation_aux_entry_len(params: &Params) -> usize { } #[derive(Copy, Clone)] -struct KeyPairTarget { - pk_hash: HashOutTarget, - sk_hash: HashOutTarget, -} +struct HashPairTarget(HashOutTarget, HashOutTarget); -impl Flattenable for KeyPairTarget { +impl Flattenable for HashPairTarget { fn flatten(&self) -> Vec { - self.pk_hash - .elements - .into_iter() - .chain(self.sk_hash.elements) - .collect() + self.0.elements.into_iter().chain(self.1.elements).collect() } fn from_flattened(params: &Params, vs: &[Target]) -> Self { assert_eq!(vs.len(), Self::size(params)); - Self { - pk_hash: HashOutTarget::try_from(&vs[..4]).expect("len = 4"), - sk_hash: HashOutTarget::try_from(&vs[4..]).expect("len = 4"), - } + Self( + HashOutTarget::try_from(&vs[..4]).expect("len = 4"), + HashOutTarget::try_from(&vs[4..]).expect("len = 4"), + ) } fn size(_params: &Params) -> usize { 8 } } +type PubKeySecKeyTarget = HashPairTarget; // (public_key, secret_key) +type MsgPubKeyTarget = HashPairTarget; // (message, public_key) + +#[derive(Clone, Serialize, Deserialize)] +struct SignedByTarget { + msg: ValueTarget, + pk: PointTarget, + sig: SignatureTarget, +} + +impl SignedByTarget { + pub fn set_targets(&self, pw: &mut PartialWitness, signed_by: &SignedBy) -> Result<()> { + self.msg.set_targets(pw, &Value::from(signed_by.msg))?; + pw.set_point_target(&self.pk, &signed_by.pk)?; + pw.set_signature_target(&self.sig, &signed_by.sig)?; + Ok(()) + } + + pub fn new_virtual(builder: &mut CircuitBuilder) -> Self { + Self { + msg: builder.add_virtual_value(), + pk: builder.add_virtual_point_target(), + sig: builder.add_virtual_schnorr_signature_target(), + } + } +} + +#[allow(clippy::too_many_arguments)] fn build_operation_aux_table_circuit( params: &Params, builder: &mut CircuitBuilder, merkle_proofs: &[MerkleClaimAndProofTarget], public_key_of_sks: &[BigUInt320Target], + signed_bys: &[SignedByTarget], merkle_tree_state_transition_proofs: &[MerkleTreeStateTransitionProofTarget], custom_predicate_verifications: &[CustomPredicateVerifyEntryTarget], custom_predicate_table: &[HashOutTarget], @@ -286,12 +313,45 @@ fn build_operation_aux_table_circuit( pk.x.components.into_iter().chain(pk.u.components).collect(), ); - let entry = KeyPairTarget { pk_hash, sk_hash }; + let entry: PubKeySecKeyTarget = HashPairTarget(pk_hash, sk_hash); table.push(builder, OperationAuxTableTag::PublicKeyOf as u32, &entry); measure_gates_end!(builder, measure); } + // SignedBy: verify the Schnorr signature of a message with a public key + for signed_by in signed_bys { + let measure = measure_gates_begin!(builder, "SignedBy"); + + let signature_verify = SignatureVerifyTarget { + enabled: builder._true(), + pk: signed_by.pk.clone(), + msg: signed_by.msg, + sig: signed_by.sig.clone(), + }; + verify_signature_circuit(builder, &signature_verify); + + // TODO: Add a function to hash the public key + let pk_hash = builder.hash_n_to_hash_no_pad::( + signed_by + .pk + .x + .components + .into_iter() + .chain(signed_by.pk.u.components) + .collect(), + ); + let entry: MsgPubKeyTarget = HashPairTarget( + HashOutTarget { + elements: signed_by.msg.elements, + }, + pk_hash, + ); + + table.push(builder, OperationAuxTableTag::SignedBy as u32, &entry); + measure_gates_end!(builder, measure); + } + // Merkle state transition proofs: verify op proof (insert/update/delete) for merkle_tree_state_transition_proof in merkle_tree_state_transition_proofs { verify_merkle_state_transition_circuit(builder, merkle_tree_state_transition_proof); @@ -350,7 +410,6 @@ fn verify_operation_circuit( st: &StatementTarget, op: &OperationTarget, prev_statements: &[StatementTarget], - input_statements_offset: usize, aux_table: &MuxTableTarget, ) -> Result<()> { let measure = measure_gates_begin!(builder, "OpVerify"); @@ -376,17 +435,7 @@ fn verify_operation_circuit( // restricted to the op of type X, where the returned target is `false` if the input targets // lie outside of the domain. let mut op_checks = Vec::new(); - op_checks.extend_from_slice(&[ - verify_none_circuit(params, builder, st, &op.op_type), - verify_new_entry_circuit( - params, - builder, - st, - &op.op_type, - prev_statements, - input_statements_offset, - ), - ]); + op_checks.extend_from_slice(&[verify_none_circuit(params, builder, st, &op.op_type)]); // Skip these if there are no resolved op args if !cache.op_args.is_empty() { op_checks.extend_from_slice(&[ @@ -433,6 +482,16 @@ fn verify_operation_circuit( &cache, )); } + if params.max_signed_by > 0 { + op_checks.push(verify_signed_by_circuit( + params, + builder, + st, + &op.op_type, + &resolved_aux, + &cache, + )); + } if params.max_merkle_tree_state_transition_proofs_containers > 0 { op_checks.extend_from_slice(&[ verify_merkle_insert_circuit( @@ -985,23 +1044,17 @@ fn verify_public_key_of_circuit( cache: &StatementCache, ) -> BoolTarget { let measure = measure_gates_begin!(builder, "OpPublicKeyOf"); - let (aux_tag_ok, resolved_key_pair) = - aux.as_type::(builder, OperationAuxTableTag::PublicKeyOf as u32); + let (aux_tag_ok, resolved_pk_sk) = + aux.as_type::(builder, OperationAuxTableTag::PublicKeyOf as u32); let op_code_ok = op_type.has_native(builder, NativeOperation::PublicKeyOf); let (arg_types_ok, [arg1_value, arg2_value]) = cache.first_n_args_as_values(); // inputting public_key, secret_key - let public_key_hash = arg1_value; - let secret_key_hash = arg2_value; + let pk_hash = arg1_value; + let sk_hash = arg2_value; - let skey_hash_ok = builder.is_equal_slice( - &secret_key_hash.elements, - &resolved_key_pair.sk_hash.elements, - ); - let pkey_hash_ok = builder.is_equal_slice( - &public_key_hash.elements, - &resolved_key_pair.pk_hash.elements, - ); + let pk_ok = builder.is_equal_slice(&pk_hash.elements, &resolved_pk_sk.0.elements); + let sk_ok = builder.is_equal_slice(&sk_hash.elements, &resolved_pk_sk.1.elements); let arg1_expected = cache.equations[0].lhs.clone(); let arg2_expected = cache.equations[1].lhs.clone(); @@ -1013,14 +1066,43 @@ fn verify_public_key_of_circuit( ); let st_ok = builder.is_equal_flattenable(st, &expected_statement); - let ok = builder.all([ - op_code_ok, - aux_tag_ok, - arg_types_ok, - pkey_hash_ok, - skey_hash_ok, - st_ok, - ]); + let ok = builder.all([op_code_ok, aux_tag_ok, arg_types_ok, pk_ok, sk_ok, st_ok]); + measure_gates_end!(builder, measure); + ok +} + +fn verify_signed_by_circuit( + params: &Params, + builder: &mut CircuitBuilder, + st: &StatementTarget, + op_type: &OperationTypeTarget, + aux: &TableEntryTarget, + cache: &StatementCache, +) -> BoolTarget { + let measure = measure_gates_begin!(builder, "OpSignedBy"); + let (aux_tag_ok, resolved_msg_pk) = + aux.as_type::(builder, OperationAuxTableTag::SignedBy as u32); + + let op_code_ok = op_type.has_native(builder, NativeOperation::SignedBy); + let (arg_types_ok, [arg1_value, arg2_value]) = cache.first_n_args_as_values(); + // inputting msg, pub_key + let msg = arg1_value; + let pk_hash = arg2_value; + + let msg_ok = builder.is_equal_slice(&msg.elements, &resolved_msg_pk.0.elements); + let pk_ok = builder.is_equal_slice(&pk_hash.elements, &resolved_msg_pk.1.elements); + + let arg1_expected = cache.equations[0].lhs.clone(); + let arg2_expected = cache.equations[1].lhs.clone(); + let expected_statement = StatementTarget::new_native( + builder, + params, + NativePredicate::SignedBy, + &[arg1_expected, arg2_expected], + ); + let st_ok = builder.is_equal_flattenable(st, &expected_statement); + + let ok = builder.all([op_code_ok, aux_tag_ok, arg_types_ok, msg_ok, pk_ok, st_ok]); measure_gates_end!(builder, measure); ok } @@ -1198,37 +1280,6 @@ fn verify_none_circuit( ok } -fn verify_new_entry_circuit( - params: &Params, - builder: &mut CircuitBuilder, - st: &StatementTarget, - op_type: &OperationTypeTarget, - prev_statements: &[StatementTarget], - input_statements_offset: usize, -) -> BoolTarget { - let measure = measure_gates_begin!(builder, "OpNewEntry"); - let op_code_ok = op_type.has_native(builder, NativeOperation::NewEntry); - let st_code_ok = st.has_native_type(builder, params, NativePredicate::Equal); - - let expected_arg_prefix = builder.constants( - &StatementArg::Key(AnchoredKey::from((SELF, ""))).to_fields(params)[..VALUE_SIZE], - ); - let arg_prefix_ok = - builder.is_equal_slice(&st.args[0].elements[..VALUE_SIZE], &expected_arg_prefix); - - let input_statements = &prev_statements[input_statements_offset..]; - let individual_dupe_checks = input_statements - .iter() - .map(|ps| builder.is_equal_slice(&st.args[0].elements, &ps.args[0].elements)) - .collect::>(); - let dupe_check = builder.any(individual_dupe_checks); - let no_dupes_ok = builder.not(dupe_check); - - let ok = builder.all([op_code_ok, st_code_ok, arg_prefix_ok, no_dupes_ok]); - measure_gates_end!(builder, measure); - ok -} - fn verify_lt_to_neq_circuit( params: &Params, builder: &mut CircuitBuilder, @@ -1300,18 +1351,13 @@ fn make_statement_arg_from_template_circuit( // optimization: ak_id_wc_index and wc_index use the same signals, so we only need to do one // random access to resolve both of them assert_eq!(ak_id_wc_index, wc_index); - // optimization: the wildcard indices have an offset of +1. This allows us to set a fixed - // SELF in args[0] to resolve SelfOrWildcard::SELF encoded as a wildcard at index 0. - let value_self = ValueTarget::from_slice(&builder.constants(&SELF.0 .0)); - let args = iter::once(value_self) - .chain(args.iter().cloned()) - .collect_vec(); + // If the index is not used, use a 0 instead to still pass the range constraints from // vec_ref let first_index = ak_id_wc_index; let is_first_index_valid = builder.or(is_ak, is_wc_literal); let first_index = builder.select(is_first_index_valid, first_index, zero); - let resolved_ak_id = builder.vec_ref_small(params, &args, first_index); + let resolved_ak_id = builder.vec_ref_small(params, args, first_index); let resolved_wc = resolved_ak_id; // If the index is not used, use a 0 instead to still pass the range constraints from @@ -1319,7 +1365,7 @@ fn make_statement_arg_from_template_circuit( let second_index = ak_key_wc_index; let is_second_index_valid = builder.and(is_ak, is_ak_key_wc); let second_index = builder.select(is_second_index_valid, second_index, zero); - let resolved_ak_key = builder.vec_ref_small(params, &args, second_index); + let resolved_ak_key = builder.vec_ref_small(params, args, second_index); let ak_key = ak_key_lit; // is_ak_key_lit let ak_key = builder.select_flattenable(params, is_ak_key_wc, &resolved_ak_key, &ak_key); @@ -1427,47 +1473,45 @@ fn make_custom_statement_circuit( Ok((statement, op_type)) } -/// Replace references to SELF by `self_id` in a statement. +/// Replace the blank verifier_data_hash slots in intro predicates by `vd_hash` fn normalize_statement_circuit( params: &Params, builder: &mut CircuitBuilder, statement: &StatementTarget, - self_id: &ValueTarget, + vd_hash: &HashOutTarget, ) -> StatementTarget { - let self_value = builder.constant_value(SELF.0.into()); - let args = statement - .args - .iter() - .map(|arg| { - let first = ValueTarget::from_slice(&arg.elements[..VALUE_SIZE]); - let second = ValueTarget::from_slice(&arg.elements[VALUE_SIZE..]); - let is_self = builder.is_equal_flattenable(&self_value, &first); - let first_normalized = builder.select_flattenable(params, is_self, self_id, &first); - StatementArgTarget::new(first_normalized, second) - }) - .collect_vec(); + let is_intro = statement.predicate.is_intro(builder); + let old_pred = statement.predicate.elements; + let old = HashOutTarget::try_from(&old_pred[1..1 + HASH_SIZE]).expect("len = 4"); + let new = builder + .select_flattenable(params, is_intro, vd_hash, &old) + .elements; + StatementTarget { - predicate: statement.predicate.clone(), - args, + predicate: PredicateTarget { + elements: [old_pred[0], new[0], new[1], new[2], new[3], old_pred[5]], + }, + args: statement.args.clone(), } } -/// `params.num_public_statements_id` is the total number of statements that will be hashed. -/// The id is calculated with front-padded none-statements and then the input statements -/// reversed. The part of the hash from the front-padded none-statements is precomputed. -pub fn calculate_id_circuit( +/// `params.num_public_statements_hash` is the total number of statements that will be hashed. +/// The statements hash is calculated with front-padded none-statements and then the input +/// statements reversed. The part of the hash from the front-padded none-statements is +/// precomputed. +pub fn calculate_statements_hash_circuit( params: &Params, builder: &mut CircuitBuilder, // These statements will be padded to reach `num_statements` statements: &[StatementTarget], ) -> HashOutTarget { - assert!(statements.len() <= params.num_public_statements_id); + assert!(statements.len() <= params.num_public_statements_hash); let measure = measure_gates_begin!(builder, "CalculateId"); let statements_rev_flattened = statements.iter().rev().flat_map(|s| s.flatten()); let mut none_st = mainpod::Statement::from(Statement::None); pad_statement(params, &mut none_st); let front_pad_elts = iter::repeat(&none_st) - .take(params.num_public_statements_id - statements.len()) + .take(params.num_public_statements_hash - statements.len()) .flat_map(|s| s.to_fields(params)) .collect_vec(); let (perm, front_pad_elts_rem) = @@ -1479,11 +1523,11 @@ pub fn calculate_id_circuit( .map(|v| builder.constant(*v)) .chain(statements_rev_flattened) .collect_vec(); - let id = + let sts_hash = hash_from_state_circuit::>(builder, perm, &inputs); measure_gates_end!(builder, measure); - id + sts_hash } // Replace predicates of batch-self with the corresponding global custom predicate batch_id and @@ -1549,14 +1593,9 @@ fn verify_main_pod_circuit( verified_proofs: &[VerifiedProofTarget], ) -> Result { let params = &main_pod.params; - assert_eq!(params.max_input_recursive_pods, verified_proofs.len()); + assert_eq!(params.max_input_pods, verified_proofs.len()); let measure = measure_gates_begin!(builder, "MainPodVerify"); - // 1a. Verify all input signed pods - for signed_pod in &main_pod.signed_pods { - verify_signed_pod_circuit(builder, signed_pod)?; - builder.assert_one(signed_pod.signature.enabled.target); - } // Build the statement array let mut statements = Vec::new(); @@ -1564,15 +1603,8 @@ fn verify_main_pod_circuit( // predicate statements let st_none = StatementTarget::new_native(builder, params, NativePredicate::None, &[]); statements.push(st_none); - for signed_pod in &main_pod.signed_pods { - statements.extend_from_slice(signed_pod.pub_statements(builder, false).as_slice()); - } - debug_assert_eq!( - statements.len(), - 1 + params.max_input_signed_pods * params.max_signed_pod_values - ); - // 1b. Verify all input recursive pods + // 1a. Verify all input recursive pods for (verified_proof, vd_mt_proof, input_pod_self_statements) in izip!( verified_proofs, &main_pod.vd_mt_proofs, @@ -1581,33 +1613,52 @@ fn verify_main_pod_circuit( let measure_in_pod = measure_gates_begin!(builder, "VerifyInPod"); // - // Verify id from the statements + // Verify sts_hash from the statements // - let expected_id = HashOutTarget::try_from( - &verified_proof.public_inputs[PI_OFFSET_ID..PI_OFFSET_ID + HASH_SIZE], + let expected_sts_hash = HashOutTarget::try_from( + &verified_proof.public_inputs + [PI_OFFSET_STATEMENTS_HASH..PI_OFFSET_STATEMENTS_HASH + HASH_SIZE], ) .expect("4 elements"); - let id_value = ValueTarget { - elements: expected_id.elements, - }; + // NOTE: We use an EmptyPod for padding input pod slots. The EmptyPod is an introduction + // pod that declares a statement with no arguments. + let is_intro = input_pod_self_statements[0].predicate.is_intro(builder); + + // Introduction pods can only have Introduction or None statements + let mut intro_ok = is_intro; + for self_st in &input_pod_self_statements[1..] { + let st_is_intro = self_st.predicate.is_intro(builder); + let st_is_none = self_st.has_native_type(builder, params, NativePredicate::None); + let st_is_intro_or_none = builder.or(st_is_intro, st_is_none); + intro_ok = builder.and(intro_ok, st_is_intro_or_none); + } + builder.connect(is_intro.target, intro_ok.target); + + let is_main = builder.not(is_intro); for self_st in input_pod_self_statements { - let normalized_st = normalize_statement_circuit(params, builder, self_st, &id_value); + let normalized_st = normalize_statement_circuit( + params, + builder, + self_st, + &verified_proof.verifier_data_hash, + ); statements.push(normalized_st); } - let id = calculate_id_circuit(params, builder, input_pod_self_statements); - builder.connect_hashes(expected_id, id); + let sts_hash = + calculate_statements_hash_circuit(params, builder, input_pod_self_statements); + builder.connect_hashes(expected_sts_hash, sts_hash); // - // Verify that all input pod proofs use verifier data from the public input VD - // array. This requires merkle proofs + // Verify that all main input pod proofs use verifier data from the public input VD + // array. This requires merkle proofs. introduction pods are not checked here because + // their verifier_data_hash appears in their introduction statement. // verify_merkle_proof_circuit(builder, vd_mt_proof); - // ensure that mt_proof is enabled - let true_targ = builder._true(); - builder.connect(vd_mt_proof.enabled.target, true_targ.target); + // ensure that mt_proof is enabled if it's a main pod + builder.connect(vd_mt_proof.enabled.target, is_main.target); // connect the vd_mt_proof's root to the actual vds_root, to ensure that the mt proof // verifies against the vds_root builder.connect_hashes(main_pod.vds_root, vd_mt_proof.root); @@ -1646,59 +1697,27 @@ fn verify_main_pod_circuit( builder, &main_pod.merkle_proofs, &main_pod.public_key_of_sks, + &main_pod.signed_bys, &main_pod.merkle_tree_state_transition_proofs, &main_pod.custom_predicate_verifications, &custom_predicate_table, )?; // 2. Calculate the Pod Id from the public statements - let id = calculate_id_circuit(params, builder, pub_statements); + let sts_hash = calculate_statements_hash_circuit(params, builder, pub_statements); - // 4. Verify type - let type_statement = &pub_statements[0]; - // TODO: Store this hash in a global static with lazy init so that we don't have to - // compute it every time. - let expected_type_statement = StatementTarget::from_flattened( - params, - &builder.constants( - &Statement::equal( - ValueRef::Key(AnchoredKey::from((SELF, KEY_TYPE))), - ValueRef::Literal(Value::from(PodType::Main)), - ) - .to_fields(params), - ), - ); - builder.connect_flattenable(type_statement, &expected_type_statement); - - // 3. check that all `input_statements` of type `ValueOf` with origin=SELF have unique keys - // (no duplicates). We do this in the verification of NewEntry operation. // 5. Verify input statements for (i, (st, op)) in izip!(&main_pod.input_statements, &main_pod.operations).enumerate() { let prev_statements = &statements[..input_statements_offset + i]; if i < public_statements_offset { - verify_operation_circuit( - params, - builder, - st, - op, - prev_statements, - input_statements_offset, - &aux_table, - )?; + verify_operation_circuit(params, builder, st, op, prev_statements, &aux_table)?; } else { - verify_operation_public_statement_circuit( - params, - builder, - st, - op, - prev_statements, - input_statements_offset, - )?; + verify_operation_public_statement_circuit(params, builder, st, op, prev_statements)?; } } measure_gates_end!(builder, measure); - Ok(id) + Ok(sts_hash) } #[derive(Clone, Serialize, Deserialize)] @@ -1706,13 +1725,13 @@ pub struct MainPodVerifyTarget { params: Params, vds_root: HashOutTarget, vd_mt_proofs: Vec, - signed_pods: Vec, input_pods_self_statements: Vec>, // The KEY_TYPE statement must be the first public one input_statements: Vec, operations: Vec, merkle_proofs: Vec, public_key_of_sks: Vec, + signed_bys: Vec, merkle_tree_state_transition_proofs: Vec, custom_predicate_batches: Vec, custom_predicate_verifications: Vec, @@ -1723,13 +1742,10 @@ impl MainPodVerifyTarget { MainPodVerifyTarget { params: params.clone(), vds_root: builder.add_virtual_hash(), - vd_mt_proofs: (0..params.max_input_recursive_pods) + vd_mt_proofs: (0..params.max_input_pods) .map(|_| MerkleClaimAndProofTarget::new_virtual(params.max_depth_mt_vds, builder)) .collect(), - signed_pods: (0..params.max_input_signed_pods) - .map(|_| SignedPodVerifyTarget::new_virtual(params, builder)) - .collect(), - input_pods_self_statements: (0..params.max_input_recursive_pods) + input_pods_self_statements: (0..params.max_input_pods) .map(|_| { (0..params.max_input_pods_public_statements) .map(|_| builder.add_virtual_statement(params)) @@ -1750,6 +1766,9 @@ impl MainPodVerifyTarget { public_key_of_sks: (0..params.max_public_key_of) .map(|_| builder.add_virtual_biguint320_target()) .collect(), + signed_bys: (0..params.max_signed_by) + .map(|_| SignedByTarget::new_virtual(builder)) + .collect(), merkle_tree_state_transition_proofs: (0..params .max_merkle_tree_state_transition_proofs_containers) .map(|_| { @@ -1778,16 +1797,17 @@ pub struct CustomPredicateVerification { pub struct MainPodVerifyInput { pub vds_set: VDSet, - // field containing the `vd_mt_proofs` aside from the `vds_set`, because - // inide the MainPodVerifyTarget circuit, since it is the InnerCircuit for - // the RecursiveCircuit, we don't have access to the used verifier_datas. - pub vd_mt_proofs: Vec, - pub signed_pods: Vec, - pub recursive_pods_pub_self_statements: Vec>, + /// field containing the `vd_mt_proofs` aside from the `vds_set`, because + /// inside the MainPodVerifyTarget circuit, since it is the InnerCircuit for + /// the RecursiveCircuit, we don't have access to the used verifier_datas. + /// The bool is used as `enabled` and will be false for intro pods. + pub vd_mt_proofs: Vec<(bool, MerkleClaimAndProof)>, + pub input_pods_pub_self_statements: Vec>, pub statements: Vec, pub operations: Vec, pub merkle_proofs: Vec, pub public_key_of_sks: Vec, + pub signed_bys: Vec, pub merkle_tree_state_transition_proofs: Vec, pub custom_predicate_batches: Vec>, pub custom_predicate_verifications: Vec, @@ -1803,7 +1823,7 @@ fn set_targets_input_pods_self_statements( statements_target.len(), params.max_input_pods_public_statements ); - assert!(statements.len() <= params.num_public_statements_id); + assert!(statements.len() <= params.num_public_statements_hash); for (i, statement) in statements.iter().enumerate() { statements_target[i].set_targets(pw, params, &statement.clone().into())?; @@ -1827,8 +1847,8 @@ impl InnerCircuit for MainPodVerifyTarget { verified_proofs: &[VerifiedProofTarget], ) -> Result { let main_pod = MainPodVerifyTarget::new_virtual(params, builder); - let id = verify_main_pod_circuit(builder, &main_pod, verified_proofs)?; - builder.register_public_inputs(&id.elements); + let sts_hash = verify_main_pod_circuit(builder, &main_pod, verified_proofs)?; + builder.register_public_inputs(&sts_hash.elements); builder.register_public_inputs(&main_pod.vds_root.elements); Ok(main_pod) } @@ -1838,38 +1858,16 @@ impl InnerCircuit for MainPodVerifyTarget { let vds_root = input.vds_set.root(); pw.set_target_arr(&self.vds_root.elements, &vds_root.0)?; - for (i, vd_mt_proof) in input.vd_mt_proofs.iter().enumerate() { - self.vd_mt_proofs[i].set_targets(pw, true, vd_mt_proof)?; - } - // the rest of vd_mt_proofs set them to the empty_pod vd_mt_proof - let vd_emptypod_mt_proof = - input - .vds_set - .get_vds_proofs(&[cache_get_standard_empty_pod_circuit_data() - .1 - .verifier_only - .clone()])?; - let vd_emptypod_mt_proof = vd_emptypod_mt_proof[0].clone(); - for i in input.vd_mt_proofs.len()..self.vd_mt_proofs.len() { - self.vd_mt_proofs[i].set_targets(pw, true, &vd_emptypod_mt_proof)?; - } - - assert!(input.signed_pods.len() <= self.params.max_input_signed_pods); - for (i, signed_pod) in input.signed_pods.iter().enumerate() { - self.signed_pods[i].set_targets(pw, signed_pod)?; - } - // Padding - if input.signed_pods.len() != self.params.max_input_signed_pods { - let dummy = SignedPod::dummy(); - for i in input.signed_pods.len()..self.params.max_input_signed_pods { - self.signed_pods[i].set_targets(pw, &dummy)?; - } - } - - assert!( - input.recursive_pods_pub_self_statements.len() <= self.params.max_input_recursive_pods + assert_eq!( + input.vd_mt_proofs.len(), + input.input_pods_pub_self_statements.len() ); - for (i, pod_pub_statements) in input.recursive_pods_pub_self_statements.iter().enumerate() { + let input_pods_len = input.vd_mt_proofs.len(); + assert!(input_pods_len <= self.params.max_input_pods); + for (i, (enable, vd_mt_proof)) in input.vd_mt_proofs.iter().enumerate() { + self.vd_mt_proofs[i].set_targets(pw, *enable, vd_mt_proof)?; + } + for (i, pod_pub_statements) in input.input_pods_pub_self_statements.iter().enumerate() { set_targets_input_pods_self_statements( pw, &self.params, @@ -1878,12 +1876,17 @@ impl InnerCircuit for MainPodVerifyTarget { )?; } // Padding - if input.recursive_pods_pub_self_statements.len() != self.params.max_input_recursive_pods { + if input_pods_len != self.params.max_input_pods { let empty_pod = EmptyPod::new_boxed(&self.params, input.vds_set.clone()); let empty_pod_statements = empty_pod.pub_statements(); - for i in - input.recursive_pods_pub_self_statements.len()..self.params.max_input_recursive_pods - { + let empty_mt_proof = MerkleClaimAndProof { + root: input.vds_set.root(), + value: RawValue::from(empty_pod.verifier_data_hash()), + ..MerkleClaimAndProof::empty() + }; + + for i in input_pods_len..self.params.max_input_pods { + self.vd_mt_proofs[i].set_targets(pw, false, &empty_mt_proof)?; set_targets_input_pods_self_statements( pw, &self.params, @@ -1919,6 +1922,16 @@ impl InnerCircuit for MainPodVerifyTarget { pw.set_biguint320_target(&self.public_key_of_sks[i], &pad_sk)?; } + assert!(input.signed_bys.len() <= self.params.max_signed_by); + for (i, signed_by) in input.signed_bys.iter().enumerate() { + self.signed_bys[i].set_targets(pw, signed_by)?; + } + // Padding + let pad_signed_by = SignedBy::dummy(); + for i in input.signed_bys.len()..self.params.max_signed_by { + self.signed_bys[i].set_targets(pw, &pad_signed_by)?; + } + assert!( input.merkle_tree_state_transition_proofs.len() <= self @@ -1985,6 +1998,7 @@ impl InnerCircuit for MainPodVerifyTarget { mod tests { use std::{iter, ops::Not}; + use num::FromPrimitive; use plonky2::{ field::{goldilocks_field::GoldilocksField, types::Field}, hash::hash_types::HashOut, @@ -1997,32 +2011,71 @@ mod tests { backends::plonky2::{ basetypes::C, circuits::common::tests::I64_TEST_PAIRS, - mainpod::{calculate_id, OperationArg, OperationAux}, + mainpod::{calculate_statements_hash, OperationArg, OperationAux}, primitives::{ ec::schnorr::SecretKey, merkletree::{MerkleClaimAndProof, MerkleTree, MerkleTreeStateTransitionProof}, }, + signer, }, + dict, frontend::{self, literal, CustomPredicateBatchBuilder, StatementTmplBuilder}, middleware::{ - hash_str, hash_values, Hash, Key, OperationType, PodId, Predicate, RawValue, - StatementTmpl, StatementTmplArg, TypedValue, Wildcard, + hash_values, AnchoredKey, Hash, Key, OperationType, Predicate, RawValue, StatementArg, + StatementTmpl, StatementTmplArg, Wildcard, }, }; + #[derive(Default)] + struct Aux { + merkle_proofs: Vec, + secret_keys: Vec, + signed_bys: Vec, + merkle_tree_state_transition_proofs: Vec, + } + + impl Aux { + fn merkle_proof(v: MerkleClaimAndProof) -> Self { + Self { + merkle_proofs: vec![v], + ..Default::default() + } + } + fn secret_key(v: SecretKey) -> Self { + Self { + secret_keys: vec![v], + ..Default::default() + } + } + fn signed_by(v: SignedBy) -> Self { + Self { + signed_bys: vec![v], + ..Default::default() + } + } + fn merkle_tree_state_transition_proof(v: MerkleTreeStateTransitionProof) -> Self { + Self { + merkle_tree_state_transition_proofs: vec![v], + ..Default::default() + } + } + } + fn operation_verify( st: mainpod::Statement, op: mainpod::Operation, prev_statements: Vec, - merkle_proofs: Vec, - secret_keys: Vec, - merkle_tree_state_transition_proofs: Vec, + aux: Aux, ) -> Result<()> { let params = Params { - max_custom_predicate_batches: 0, + max_merkle_proofs_containers: aux.merkle_proofs.len(), + max_public_key_of: aux.secret_keys.len(), + max_signed_by: aux.signed_bys.len(), + max_merkle_tree_state_transition_proofs_containers: aux + .merkle_tree_state_transition_proofs + .len(), max_custom_predicate_verifications: 0, - max_merkle_proofs_containers: merkle_proofs.len(), - max_public_key_of: secret_keys.len(), + max_custom_predicate_batches: 0, ..Default::default() }; @@ -2035,40 +2088,47 @@ mod tests { .map(|_| builder.add_virtual_statement(¶ms)) .collect(); - let merkle_proofs_target: Vec<_> = merkle_proofs + let merkle_proofs_target: Vec<_> = aux + .merkle_proofs .iter() .map(|_| { MerkleClaimAndProofTarget::new_virtual(params.max_depth_mt_containers, &mut builder) }) .collect(); - let secret_keys_target: Vec<_> = secret_keys + let secret_keys_target: Vec<_> = aux + .secret_keys .iter() .map(|sk| builder.constant_biguint320(&sk.0)) .collect(); - let merkle_tree_state_transition_proofs_target: Vec<_> = - merkle_tree_state_transition_proofs - .iter() - .map(|_| { - MerkleTreeStateTransitionProofTarget::new_virtual( - params.max_depth_mt_containers, - &mut builder, - ) - }) - .collect(); + let signed_by_targets: Vec<_> = aux + .signed_bys + .iter() + .map(|_| SignedByTarget::new_virtual(&mut builder)) + .collect(); + + let merkle_tree_state_transition_proofs_target: Vec<_> = aux + .merkle_tree_state_transition_proofs + .iter() + .map(|_| { + MerkleTreeStateTransitionProofTarget::new_virtual( + params.max_depth_mt_containers, + &mut builder, + ) + }) + .collect(); let aux_table = build_operation_aux_table_circuit( ¶ms, &mut builder, &merkle_proofs_target, &secret_keys_target, + &signed_by_targets, &merkle_tree_state_transition_proofs_target, &[], &[], )?; - // let max_aux_entry_len = max_operation_aux_entry_len(¶ms); - // let aux = builder.add_virtual_targets(1 + max_aux_entry_len); verify_operation_circuit( ¶ms, @@ -2076,7 +2136,6 @@ mod tests { &st_target, &op_target, &prev_statements_target, - 0, &aux_table, )?; @@ -2086,15 +2145,18 @@ mod tests { for (prev_st_target, prev_st) in prev_statements_target.iter().zip(prev_statements.iter()) { prev_st_target.set_targets(&mut pw, ¶ms, prev_st)?; } + for (signed_by_target, signed_by) in signed_by_targets.iter().zip(aux.signed_bys.iter()) { + signed_by_target.set_targets(&mut pw, signed_by)? + } for (merkle_proof_target, merkle_proof) in - merkle_proofs_target.iter().zip(merkle_proofs.iter()) + merkle_proofs_target.iter().zip(aux.merkle_proofs.iter()) { merkle_proof_target.set_targets(&mut pw, true, merkle_proof)? } for (merkle_tree_state_transition_proof_target, merkle_tree_state_transition_proof) in merkle_tree_state_transition_proofs_target .iter() - .zip(merkle_tree_state_transition_proofs.iter()) + .zip(aux.merkle_tree_state_transition_proofs.iter()) { merkle_tree_state_transition_proof_target.set_targets( &mut pw, @@ -2113,49 +2175,24 @@ mod tests { #[test] fn test_lt_lteq_verify_failures() { - let st1: mainpod::Statement = - Statement::equal(AnchoredKey::from((SELF, "hello")), Value::from(55)).into(); - let st2: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(75).into()), "world")), - Value::from(56), - ) - .into(); - let st3: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(88).into()), "hola")), - Value::from(RawValue([ - GoldilocksField::NEG_ONE, - GoldilocksField::ZERO, - GoldilocksField::ZERO, - GoldilocksField::ZERO, - ])), - ) - .into(); - let st4: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(74).into()), "mundo")), - Value::from(-55), - ) - .into(); - let st5: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(70).into()), "que")), - Value::from(-56), - ) - .into(); + let invalid_int = RawValue([ + GoldilocksField::NEG_ONE, + GoldilocksField::ZERO, + GoldilocksField::ZERO, + GoldilocksField::ZERO, + ]); - let prev_statements = [st1, st2, st3, st4, st5]; + let prev_statements = [Statement::None.into()]; [ // 56 < 55, 55 < 55, 56 <= 55, -55 < -55, -55 < -56, -55 <= -56 should fail to verify ( mainpod::Operation( OperationType::Native(NativeOperation::LtFromEntries), - vec![OperationArg::Index(1), OperationArg::Index(0)], + vec![OperationArg::Index(0), OperationArg::Index(0)], OperationAux::None, ), - Statement::lt( - AnchoredKey::from((PodId(RawValue::from(75).into()), "world")), - AnchoredKey::from((SELF, "hello")), - ) - .into(), + Statement::lt(56, 55).into(), ), ( mainpod::Operation( @@ -2163,59 +2200,39 @@ mod tests { vec![OperationArg::Index(0), OperationArg::Index(0)], OperationAux::None, ), - Statement::lt( - AnchoredKey::from((SELF, "hello")), - AnchoredKey::from((SELF, "hello")), - ) - .into(), + Statement::lt(55, 55).into(), ), ( mainpod::Operation( OperationType::Native(NativeOperation::LtEqFromEntries), - vec![OperationArg::Index(1), OperationArg::Index(0)], + vec![OperationArg::Index(0), OperationArg::Index(0)], OperationAux::None, ), - Statement::lt_eq( - AnchoredKey::from((PodId(RawValue::from(75).into()), "world")), - AnchoredKey::from((SELF, "hello")), - ) - .into(), + Statement::lt_eq(56, 55).into(), ), ( mainpod::Operation( OperationType::Native(NativeOperation::LtFromEntries), - vec![OperationArg::Index(3), OperationArg::Index(3)], + vec![OperationArg::Index(0), OperationArg::Index(0)], OperationAux::None, ), - Statement::lt( - AnchoredKey::from((PodId(RawValue::from(74).into()), "mundo")), - AnchoredKey::from((PodId(RawValue::from(74).into()), "mundo")), - ) - .into(), + Statement::lt(-55, -55).into(), ), ( mainpod::Operation( OperationType::Native(NativeOperation::LtFromEntries), - vec![OperationArg::Index(3), OperationArg::Index(4)], + vec![OperationArg::Index(0), OperationArg::Index(0)], OperationAux::None, ), - Statement::lt( - AnchoredKey::from((PodId(RawValue::from(74).into()), "mundo")), - AnchoredKey::from((PodId(RawValue::from(70).into()), "que")), - ) - .into(), + Statement::lt(-55, -56).into(), ), ( mainpod::Operation( OperationType::Native(NativeOperation::LtEqFromEntries), - vec![OperationArg::Index(3), OperationArg::Index(4)], + vec![OperationArg::Index(0), OperationArg::Index(0)], OperationAux::None, ), - Statement::lt_eq( - AnchoredKey::from((PodId(RawValue::from(74).into()), "mundo")), - AnchoredKey::from((PodId(RawValue::from(70).into()), "que")), - ) - .into(), + Statement::lt_eq(-55, -56).into(), ), // 56 < p-1 and p-1 <= p-1 should fail to verify, where p // is the Goldilocks prime and 'p-1' occupies a single @@ -2223,32 +2240,24 @@ mod tests { ( mainpod::Operation( OperationType::Native(NativeOperation::LtFromEntries), - vec![OperationArg::Index(1), OperationArg::Index(2)], + vec![OperationArg::Index(0), OperationArg::Index(0)], OperationAux::None, ), - Statement::lt( - AnchoredKey::from((PodId(RawValue::from(75).into()), "world")), - AnchoredKey::from((PodId(RawValue::from(88).into()), "hola")), - ) - .into(), + Statement::lt(56, invalid_int).into(), ), ( mainpod::Operation( OperationType::Native(NativeOperation::LtEqFromEntries), - vec![OperationArg::Index(2), OperationArg::Index(2)], + vec![OperationArg::Index(0), OperationArg::Index(0)], OperationAux::None, ), - Statement::lt_eq( - AnchoredKey::from((PodId(RawValue::from(88).into()), "hola")), - AnchoredKey::from((PodId(RawValue::from(88).into()), "hola")), - ) - .into(), + Statement::lt_eq(invalid_int, invalid_int).into(), ), ] .into_iter() .for_each(|(op, st)| { let check = std::panic::catch_unwind(|| { - operation_verify(st, op, prev_statements.to_vec(), vec![], vec![], vec![]) + operation_verify(st, op, prev_statements.to_vec(), Aux::default()) }); match check { Err(e) => { @@ -2265,38 +2274,17 @@ mod tests { #[test] fn test_eq_neq_verify_failures() { - let st1: mainpod::Statement = - Statement::equal(AnchoredKey::from((SELF, "hello")), Value::from(55)).into(); - let st2: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(75).into()), "world")), - Value::from(56), - ) - .into(); - let st3: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(88).into()), "hola")), - Value::from(RawValue([ - GoldilocksField::NEG_ONE, - GoldilocksField::ZERO, - GoldilocksField::ZERO, - GoldilocksField::ZERO, - ])), - ) - .into(); - let prev_statements = [st1, st2, st3]; + let prev_statements = [Statement::None.into()]; [ // 56 == 55, 55 != 55 should fail to verify ( mainpod::Operation( OperationType::Native(NativeOperation::EqualFromEntries), - vec![OperationArg::Index(1), OperationArg::Index(0)], + vec![OperationArg::Index(0), OperationArg::Index(0)], OperationAux::None, ), - Statement::equal( - AnchoredKey::from((PodId(RawValue::from(75).into()), "world")), - AnchoredKey::from((SELF, "hello")), - ) - .into(), + Statement::equal(56, 55).into(), ), ( mainpod::Operation( @@ -2304,18 +2292,12 @@ mod tests { vec![OperationArg::Index(0), OperationArg::Index(0)], OperationAux::None, ), - Statement::not_equal( - AnchoredKey::from((SELF, "hello")), - AnchoredKey::from((SELF, "hello")), - ) - .into(), + Statement::not_equal(55, 55).into(), ), ] .into_iter() .for_each(|(op, st)| { - assert!( - operation_verify(st, op, prev_statements.to_vec(), vec![], vec![], vec![]).is_err() - ) + assert!(operation_verify(st, op, prev_statements.to_vec(), Aux::default()).is_err()) }); } @@ -2328,25 +2310,7 @@ mod tests { OperationAux::None, ); let prev_statements = vec![Statement::None.into()]; - operation_verify(st, op, prev_statements, vec![], vec![], vec![]) - } - - #[test] - fn test_operation_verify_newentry() -> Result<()> { - let st1: mainpod::Statement = - Statement::equal(AnchoredKey::from((SELF, "hello")), Value::from(55)).into(); - let st2: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(75).into()), "hello")), - Value::from(55), - ) - .into(); - let prev_statements = vec![st2]; - let op = mainpod::Operation( - OperationType::Native(NativeOperation::NewEntry), - vec![], - OperationAux::None, - ); - operation_verify(st1, op, prev_statements, vec![], vec![], vec![]) + operation_verify(st, op, prev_statements, Aux::default()) } #[test] @@ -2358,21 +2322,18 @@ mod tests { OperationAux::None, ); let prev_statements = vec![Statement::None.into()]; - operation_verify(st, op, prev_statements, vec![], vec![], vec![]) + operation_verify(st, op, prev_statements, Aux::default()) } #[test] fn test_operation_verify_eq() -> Result<()> { - let st1: mainpod::Statement = - Statement::equal(AnchoredKey::from((SELF, "hello")), Value::from(55)).into(); - let st2: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(75).into()), "world")), - Value::from(55), - ) - .into(); + let dict1 = dict!(32, {"hello" => 55})?; + let dict2 = dict!(32, {"world" => 55})?; + let st1: mainpod::Statement = Statement::contains(dict1.clone(), "hello", 55).into(); + let st2: mainpod::Statement = Statement::contains(dict2.clone(), "world", 55).into(); let st: mainpod::Statement = Statement::equal( - AnchoredKey::from((SELF, "hello")), - AnchoredKey::from((PodId(RawValue::from(75).into()), "world")), + AnchoredKey::from((&dict1, "hello")), + AnchoredKey::from((&dict2, "world")), ) .into(); let op = mainpod::Operation( @@ -2381,21 +2342,18 @@ mod tests { OperationAux::None, ); let prev_statements = vec![st1, st2]; - operation_verify(st, op, prev_statements, vec![], vec![], vec![]) + operation_verify(st, op, prev_statements, Aux::default()) } #[test] fn test_operation_verify_neq() -> Result<()> { - let st1: mainpod::Statement = - Statement::equal(AnchoredKey::from((SELF, "hello")), Value::from(55)).into(); - let st2: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(75).into()), "world")), - Value::from(58), - ) - .into(); + let dict1 = dict!(32, {"hello" => 55})?; + let dict2 = dict!(32, {"world" => 75})?; + let st1: mainpod::Statement = Statement::contains(dict1.clone(), "hello", 55).into(); + let st2: mainpod::Statement = Statement::contains(dict2.clone(), "world", 75).into(); let st: mainpod::Statement = Statement::not_equal( - AnchoredKey::from((SELF, "hello")), - AnchoredKey::from((PodId(RawValue::from(75).into()), "world")), + AnchoredKey::from((&dict1, "hello")), + AnchoredKey::from((&dict2, "world")), ) .into(); let op = mainpod::Operation( @@ -2404,21 +2362,18 @@ mod tests { OperationAux::None, ); let prev_statements = vec![st1, st2]; - operation_verify(st, op, prev_statements, vec![], vec![], vec![]) + operation_verify(st, op, prev_statements, Aux::default()) } #[test] fn test_operation_verify_lt() -> Result<()> { - let st1: mainpod::Statement = - Statement::equal(AnchoredKey::from((SELF, "hello")), Value::from(55)).into(); - let st2: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(88).into()), "hello")), - Value::from(56), - ) - .into(); + let dict1 = dict!(32, {"hello" => 55})?; + let dict2 = dict!(32, {"hello" => 56})?; + let st1: mainpod::Statement = Statement::contains(dict1.clone(), "hello", 55).into(); + let st2: mainpod::Statement = Statement::contains(dict2.clone(), "hello", 56).into(); let st: mainpod::Statement = Statement::lt( - AnchoredKey::from((SELF, "hello")), - AnchoredKey::from((PodId(RawValue::from(88).into()), "hello")), + AnchoredKey::from((&dict1, "hello")), + AnchoredKey::from((&dict2, "hello")), ) .into(); let op = mainpod::Operation( @@ -2427,22 +2382,16 @@ mod tests { OperationAux::None, ); let prev_statements = vec![st1, st2.clone()]; - operation_verify(st, op, prev_statements, vec![], vec![], vec![])?; + operation_verify(st, op, prev_statements, Aux::default())?; // Also check negative < negative - let st3: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(89).into()), "hola")), - Value::from(-56), - ) - .into(); - let st4: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(84).into()), "mundo")), - Value::from(-55), - ) - .into(); + let dict3 = dict!(32, {"hola" => -56})?; + let dict4 = dict!(32, {"mundo" => -55})?; + let st3: mainpod::Statement = Statement::contains(dict3.clone(), "hola", -56).into(); + let st4: mainpod::Statement = Statement::contains(dict4.clone(), "mundo", -55).into(); let st: mainpod::Statement = Statement::lt( - AnchoredKey::from((PodId(RawValue::from(89).into()), "hola")), - AnchoredKey::from((PodId(RawValue::from(84).into()), "mundo")), + AnchoredKey::from((&dict3, "hola")), + AnchoredKey::from((&dict4, "mundo")), ) .into(); let op = mainpod::Operation( @@ -2451,12 +2400,12 @@ mod tests { OperationAux::None, ); let prev_statements = vec![st3.clone(), st4]; - operation_verify(st, op, prev_statements, vec![], vec![], vec![])?; + operation_verify(st, op, prev_statements, Aux::default())?; // Also check negative < positive let st: mainpod::Statement = Statement::lt( - AnchoredKey::from((PodId(RawValue::from(89).into()), "hola")), - AnchoredKey::from((PodId(RawValue::from(88).into()), "hello")), + AnchoredKey::from((&dict3, "hola")), + AnchoredKey::from((&dict2, "hello")), ) .into(); let op = mainpod::Operation( @@ -2465,21 +2414,22 @@ mod tests { OperationAux::None, ); let prev_statements = vec![st3, st2]; - operation_verify(st, op, prev_statements, vec![], vec![], vec![]) + operation_verify(st, op, prev_statements, Aux::default()) } #[test] fn test_operation_verify_lteq() -> Result<()> { - let st1: mainpod::Statement = - Statement::equal(AnchoredKey::from((SELF, "hello")), Value::from(55)).into(); - let st2: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(88).into()), "hello")), - Value::from(56), - ) - .into(); + let local = dict!(32, { + "n55" => 55, + "n56" => 56, + "n_56" => -56, + "n_55" => -55, + })?; + let st1: mainpod::Statement = Statement::contains(local.clone(), "n55", 55).into(); + let st2: mainpod::Statement = Statement::contains(local.clone(), "n56", 56).into(); let st: mainpod::Statement = Statement::lt_eq( - AnchoredKey::from((SELF, "hello")), - AnchoredKey::from((PodId(RawValue::from(88).into()), "hello")), + AnchoredKey::from((&local, "n55")), + AnchoredKey::from((&local, "n56")), ) .into(); let op = mainpod::Operation( @@ -2488,22 +2438,14 @@ mod tests { OperationAux::None, ); let prev_statements = vec![st1, st2.clone()]; - operation_verify(st, op, prev_statements, vec![], vec![], vec![])?; + operation_verify(st, op, prev_statements, Aux::default())?; // Also check negative <= negative - let st3: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(89).into()), "hola")), - Value::from(-56), - ) - .into(); - let st4: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(84).into()), "mundo")), - Value::from(-55), - ) - .into(); + let st3: mainpod::Statement = Statement::contains(local.clone(), "n_56", -56).into(); + let st4: mainpod::Statement = Statement::contains(local.clone(), "n_55", -55).into(); let st: mainpod::Statement = Statement::lt_eq( - AnchoredKey::from((PodId(RawValue::from(89).into()), "hola")), - AnchoredKey::from((PodId(RawValue::from(84).into()), "mundo")), + AnchoredKey::from((&local, "n_56")), + AnchoredKey::from((&local, "n_55")), ) .into(); let op = mainpod::Operation( @@ -2512,12 +2454,12 @@ mod tests { OperationAux::None, ); let prev_statements = vec![st3.clone(), st4]; - operation_verify(st, op, prev_statements, vec![], vec![], vec![])?; + operation_verify(st, op, prev_statements, Aux::default())?; // Also check negative <= positive let st: mainpod::Statement = Statement::lt_eq( - AnchoredKey::from((PodId(RawValue::from(89).into()), "hola")), - AnchoredKey::from((PodId(RawValue::from(88).into()), "hello")), + AnchoredKey::from((&local, "n_56")), + AnchoredKey::from((&local, "n56")), ) .into(); let op = mainpod::Operation( @@ -2526,12 +2468,12 @@ mod tests { OperationAux::None, ); let prev_statements = vec![st3, st2]; - operation_verify(st, op, prev_statements.clone(), vec![], vec![], vec![])?; + operation_verify(st, op, prev_statements.clone(), Aux::default())?; // Also check equality, both positive and negative. let st: mainpod::Statement = Statement::lt_eq( - AnchoredKey::from((PodId(RawValue::from(89).into()), "hola")), - AnchoredKey::from((PodId(RawValue::from(89).into()), "hola")), + AnchoredKey::from((&local, "n_56")), + AnchoredKey::from((&local, "n_56")), ) .into(); let op = mainpod::Operation( @@ -2539,10 +2481,10 @@ mod tests { vec![OperationArg::Index(0), OperationArg::Index(0)], OperationAux::None, ); - operation_verify(st, op, prev_statements.clone(), vec![], vec![], vec![])?; + operation_verify(st, op, prev_statements.clone(), Aux::default())?; let st: mainpod::Statement = Statement::lt_eq( - AnchoredKey::from((PodId(RawValue::from(88).into()), "hello")), - AnchoredKey::from((PodId(RawValue::from(88).into()), "hello")), + AnchoredKey::from((&local, "n56")), + AnchoredKey::from((&local, "n56")), ) .into(); let op = mainpod::Operation( @@ -2550,7 +2492,7 @@ mod tests { vec![OperationArg::Index(1), OperationArg::Index(1)], OperationAux::None, ); - operation_verify(st, op, prev_statements, vec![], vec![], vec![]) + operation_verify(st, op, prev_statements, Aux::default()) } #[test] @@ -2567,26 +2509,20 @@ mod tests { let v1 = hash_values(&input_values); let [v2, v3] = input_values; - let st1: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(88).into()), "hola")), - Value::from(v1), - ) - .into(); - let st2: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(128).into()), "mundo")), - v2, - ) - .into(); - let st3: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(256).into()), "!")), - v3, - ) - .into(); + let local = dict!(32, { + "hola" => v1, + "mundo" => v2.clone(), + "!" => v3.clone(), + })?; + + let st1: mainpod::Statement = Statement::contains(local.clone(), "hola", v1).into(); + let st2: mainpod::Statement = Statement::contains(local.clone(), "mundo", v2).into(); + let st3: mainpod::Statement = Statement::contains(local.clone(), "!", v3).into(); let st: mainpod::Statement = Statement::hash_of( - AnchoredKey::from((PodId(RawValue::from(88).into()), "hola")), - AnchoredKey::from((PodId(RawValue::from(128).into()), "mundo")), - AnchoredKey::from((PodId(RawValue::from(256).into()), "!")), + AnchoredKey::from((&local, "hola")), + AnchoredKey::from((&local, "mundo")), + AnchoredKey::from((&local, "!")), ) .into(); let op = mainpod::Operation( @@ -2599,7 +2535,7 @@ mod tests { OperationAux::None, ); let prev_statements = vec![st1, st2, st3]; - operation_verify(st, op, prev_statements, vec![], vec![], vec![]) + operation_verify(st, op, prev_statements, Aux::default()) } #[test] @@ -2611,28 +2547,20 @@ mod tests { overflow.not().then_some((a, b, sum)) }) .try_for_each(|(a, b, sum)| { - let st1: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(88).into()), "hola")), - sum, - ) - .into(); + let local = dict!(32, { + "sum" => sum, + "a" => a, + "b" => b, + })?; - let st2: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(128).into()), "mundo")), - a, - ) - .into(); - - let st3: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(256).into()), "!")), - b, - ) - .into(); + let st1: mainpod::Statement = Statement::contains(local.clone(), "sum", sum).into(); + let st2: mainpod::Statement = Statement::contains(local.clone(), "a", a).into(); + let st3: mainpod::Statement = Statement::contains(local.clone(), "b", b).into(); let st: mainpod::Statement = Statement::sum_of( - AnchoredKey::from((PodId(RawValue::from(88).into()), "hola")), - AnchoredKey::from((PodId(RawValue::from(128).into()), "mundo")), - AnchoredKey::from((PodId(RawValue::from(256).into()), "!")), + AnchoredKey::from((&local, "sum")), + AnchoredKey::from((&local, "a")), + AnchoredKey::from((&local, "b")), ) .into(); let op = mainpod::Operation( @@ -2645,7 +2573,7 @@ mod tests { OperationAux::None, ); let prev_statements = vec![st1, st2, st3]; - operation_verify(st, op, prev_statements, vec![], vec![], vec![]) + operation_verify(st, op, prev_statements, Aux::default()) }) } @@ -2658,28 +2586,21 @@ mod tests { overflow.not().then_some((a, b, prod)) }) .try_for_each(|(a, b, prod)| { - let st1: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(88).into()), "hola")), - prod, - ) - .into(); + let local = dict!(32, { + "prod" => prod, + "a" => a, + "b" => b, + })?; - let st2: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(128).into()), "mundo")), - a, - ) - .into(); - - let st3: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(256).into()), "!")), - b, - ) - .into(); + let st1: mainpod::Statement = + Statement::contains(local.clone(), "prod", prod).into(); + let st2: mainpod::Statement = Statement::contains(local.clone(), "a", a).into(); + let st3: mainpod::Statement = Statement::contains(local.clone(), "b", b).into(); let st: mainpod::Statement = Statement::product_of( - AnchoredKey::from((PodId(RawValue::from(88).into()), "hola")), - AnchoredKey::from((PodId(RawValue::from(128).into()), "mundo")), - AnchoredKey::from((PodId(RawValue::from(256).into()), "!")), + AnchoredKey::from((&local, "prod")), + AnchoredKey::from((&local, "a")), + AnchoredKey::from((&local, "b")), ) .into(); let op = mainpod::Operation( @@ -2692,7 +2613,7 @@ mod tests { OperationAux::None, ); let prev_statements = vec![st1, st2, st3]; - operation_verify(st, op, prev_statements, vec![], vec![], vec![]) + operation_verify(st, op, prev_statements, Aux::default()) }) } @@ -2700,30 +2621,23 @@ mod tests { fn test_operation_verify_maxof() -> Result<()> { I64_TEST_PAIRS.into_iter().try_for_each(|(a, b)| { let max = i64::max(a, b); - let st1: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(88).into()), "hola")), - max, - ) - .into(); + let local = dict!(32, { + "max" => max, + "a" => a, + "b" => b, + })?; - let st2: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(128).into()), "mundo")), - a, - ) - .into(); - - let st3: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(256).into()), "!")), - b, - ) - .into(); + let st1: mainpod::Statement = Statement::contains(local.clone(), "max", max).into(); + let st2: mainpod::Statement = Statement::contains(local.clone(), "a", a).into(); + let st3: mainpod::Statement = Statement::contains(local.clone(), "b", b).into(); let st: mainpod::Statement = Statement::max_of( - AnchoredKey::from((PodId(RawValue::from(88).into()), "hola")), - AnchoredKey::from((PodId(RawValue::from(128).into()), "mundo")), - AnchoredKey::from((PodId(RawValue::from(256).into()), "!")), + AnchoredKey::from((&local, "max")), + AnchoredKey::from((&local, "a")), + AnchoredKey::from((&local, "b")), ) .into(); + let op = mainpod::Operation( OperationType::Native(NativeOperation::MaxOf), vec![ @@ -2734,7 +2648,7 @@ mod tests { OperationAux::None, ); let prev_statements = vec![st1, st2, st3]; - operation_verify(st, op, prev_statements, vec![], vec![], vec![]) + operation_verify(st, op, prev_statements, Aux::default()) }) } @@ -2743,43 +2657,20 @@ mod tests { [(5, 3, 4), (5, 5, 8), (3, 4, 5)] .into_iter() .for_each(|(max, a, b)| { - let st1: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(88).into()), "hola")), - max, - ) - .into(); - - let st2: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(128).into()), "mundo")), - a, - ) - .into(); - - let st3: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(256).into()), "!")), - b, - ) - .into(); - - let st: mainpod::Statement = Statement::max_of( - AnchoredKey::from((PodId(RawValue::from(88).into()), "hola")), - AnchoredKey::from((PodId(RawValue::from(128).into()), "mundo")), - AnchoredKey::from((PodId(RawValue::from(256).into()), "!")), - ) - .into(); + let st: mainpod::Statement = Statement::max_of(max, a, b).into(); let op = mainpod::Operation( OperationType::Native(NativeOperation::MaxOf), vec![ OperationArg::Index(0), - OperationArg::Index(1), - OperationArg::Index(2), + OperationArg::Index(0), + OperationArg::Index(0), ], OperationAux::None, ); - let prev_statements = [st1, st2, st3]; + let prev_statements = [Statement::None.into()]; let check = std::panic::catch_unwind(|| { - operation_verify(st, op, prev_statements.to_vec(), vec![], vec![], vec![]) + operation_verify(st, op, prev_statements.to_vec(), Aux::default()) }); match check { Err(e) => { @@ -2796,14 +2687,18 @@ mod tests { #[test] fn test_operation_verify_lt_to_neq() -> Result<()> { + let local = dict!(32,{ + "a" => 10, + "b" => 20, + })?; let st: mainpod::Statement = Statement::not_equal( - AnchoredKey::from((SELF, "hello")), - AnchoredKey::from((PodId(RawValue::from(88).into()), "hello")), + AnchoredKey::from((&local, "a")), + AnchoredKey::from((&local, "b")), ) .into(); let st1: mainpod::Statement = Statement::lt( - AnchoredKey::from((SELF, "hello")), - AnchoredKey::from((PodId(RawValue::from(88).into()), "hello")), + AnchoredKey::from((&local, "a")), + AnchoredKey::from((&local, "b")), ) .into(); let op = mainpod::Operation( @@ -2812,24 +2707,29 @@ mod tests { OperationAux::None, ); let prev_statements = vec![st1]; - operation_verify(st, op, prev_statements, vec![], vec![], vec![]) + operation_verify(st, op, prev_statements, Aux::default()) } #[test] fn test_operation_verify_transitive_eq() -> Result<()> { + let local = dict!(32,{ + "a" => 10, + "b" => 10, + "c" => 10, + })?; let st: mainpod::Statement = Statement::equal( - AnchoredKey::from((SELF, "hello")), - AnchoredKey::from((PodId(RawValue::from(88).into()), "hola")), + AnchoredKey::from((&local, "a")), + AnchoredKey::from((&local, "c")), ) .into(); let st1: mainpod::Statement = Statement::equal( - AnchoredKey::from((SELF, "hello")), - AnchoredKey::from((PodId(RawValue::from(89).into()), "world")), + AnchoredKey::from((&local, "a")), + AnchoredKey::from((&local, "b")), ) .into(); let st2: mainpod::Statement = Statement::equal( - AnchoredKey::from((PodId(RawValue::from(89).into()), "world")), - AnchoredKey::from((PodId(RawValue::from(88).into()), "hola")), + AnchoredKey::from((&local, "b")), + AnchoredKey::from((&local, "c")), ) .into(); let op = mainpod::Operation( @@ -2838,7 +2738,7 @@ mod tests { OperationAux::None, ); let prev_statements = vec![st1, st2]; - operation_verify(st, op, prev_statements, vec![], vec![], vec![]) + operation_verify(st, op, prev_statements, Aux::default()) } #[test] @@ -2854,16 +2754,21 @@ mod tests { .collect(); let mt = MerkleTree::new(params.max_depth_mt_containers, &kvs)?; - let root = Value::from(mt.root()); - let root_ak = AnchoredKey::from((PodId(RawValue::from(88).into()), "merkle root")); + let root = mt.root(); + let key = Value::from(5); + let local = dict!(32,{ + "merkle_root" => root, + "key" => key.clone(), + })?; + let root_ak = AnchoredKey::from((&local, "merkle_root")); + let key_ak = AnchoredKey::from((&local, "key")); - let key = 5.into(); - let key_ak = AnchoredKey::from((PodId(RawValue::from(88).into()), "key")); + let no_key_pf = mt.prove_nonexistence(&key.raw())?; - let no_key_pf = mt.prove_nonexistence(&key)?; - - let root_st: mainpod::Statement = Statement::equal(root_ak.clone(), root.clone()).into(); - let key_st: mainpod::Statement = Statement::equal(key_ak.clone(), key).into(); + let root_st: mainpod::Statement = + Statement::contains(local.clone(), "merkle_root", root).into(); + let key_st: mainpod::Statement = + Statement::contains(local.clone(), "key", key.clone()).into(); let st: mainpod::Statement = Statement::not_contains(root_ak, key_ak).into(); let op = mainpod::Operation( OperationType::Native(NativeOperation::NotContainsFromEntries), @@ -2871,14 +2776,9 @@ mod tests { OperationAux::MerkleProofIndex(0), ); - let merkle_proofs = vec![MerkleClaimAndProof::new( - Hash::from(root.raw()), - key, - None, - no_key_pf, - )]; + let merkle_proof = MerkleClaimAndProof::new(root, key.raw(), None, no_key_pf); let prev_statements = vec![root_st, key_st]; - operation_verify(st, op, prev_statements, merkle_proofs, vec![], vec![]) + operation_verify(st, op, prev_statements, Aux::merkle_proof(merkle_proof)) } #[test] @@ -2894,18 +2794,24 @@ mod tests { .collect(); let mt = MerkleTree::new(params.max_depth_mt_containers, &kvs)?; - let root = Value::from(mt.root()); - let root_ak = AnchoredKey::from((PodId(RawValue::from(88).into()), "merkle root")); + let root = mt.root(); + let key = Value::from(175); + let (value, key_pf) = mt.prove(&key.raw())?; + let local = dict!(32,{ + "merkle_root" => root, + "key" => key.clone(), + "value" => value, + })?; + let root_ak = AnchoredKey::from((&local, "merkle_root")); + let key_ak = AnchoredKey::from((&local, "key")); + let value_ak = AnchoredKey::from((&local, "value")); - let key = 175.into(); - let key_ak = AnchoredKey::from((PodId(RawValue::from(70).into()), "key")); - - let (value, key_pf) = mt.prove(&key)?; - let value_ak = AnchoredKey::from((PodId(RawValue::from(72).into()), "value")); - - let root_st: mainpod::Statement = Statement::equal(root_ak.clone(), root.clone()).into(); - let key_st: mainpod::Statement = Statement::equal(key_ak.clone(), key).into(); - let value_st: mainpod::Statement = Statement::equal(value_ak.clone(), value).into(); + let root_st: mainpod::Statement = + Statement::contains(local.clone(), "merkle_root", root).into(); + let key_st: mainpod::Statement = + Statement::contains(local.clone(), "key", key.clone()).into(); + let value_st: mainpod::Statement = + Statement::contains(local.clone(), "value", value).into(); let st: mainpod::Statement = Statement::contains(root_ak, key_ak, value_ak).into(); let op = mainpod::Operation( @@ -2918,14 +2824,9 @@ mod tests { OperationAux::MerkleProofIndex(0), ); - let merkle_proofs = vec![MerkleClaimAndProof::new( - Hash::from(root.raw()), - key, - Some(value), - key_pf, - )]; + let merkle_proof = MerkleClaimAndProof::new(root, key.raw(), Some(value), key_pf); let prev_statements = vec![root_st, key_st, value_st]; - operation_verify(st, op, prev_statements, merkle_proofs, vec![], vec![]) + operation_verify(st, op, prev_statements, Aux::merkle_proof(merkle_proof)) } #[test] @@ -2934,50 +2835,27 @@ mod tests { let mut tree = MerkleTree::new(params.max_depth_mt_containers, &[].into())?; - let key = 175.into(); - let key_ak = AnchoredKey::from((PodId(RawValue::from(70).into()), "key")); + let key = Value::from(175); + let value = Value::from(0); + let state_transition_proof = tree.insert(&key.raw(), &value.raw())?; + let old_root = state_transition_proof.old_root; + let new_root = state_transition_proof.new_root; - let value = 0.into(); - let value_ak = AnchoredKey::from((PodId(RawValue::from(72).into()), "value")); - - let state_transition_proof = tree.insert(&key, &value)?; - - let old_root = Value::from(state_transition_proof.old_root); - let old_root_ak = AnchoredKey::from((PodId(RawValue::from(73).into()), "old_root")); - - let new_root = Value::from(state_transition_proof.new_root); - let new_root_ak = AnchoredKey::from((PodId(RawValue::from(74).into()), "new_root")); - - let new_root_st: mainpod::Statement = - Statement::equal(new_root_ak.clone(), new_root.clone()).into(); - let old_root_st: mainpod::Statement = - Statement::equal(old_root_ak.clone(), old_root.clone()).into(); - let key_st: mainpod::Statement = Statement::equal(key_ak.clone(), key).into(); - let value_st: mainpod::Statement = Statement::equal(value_ak.clone(), value).into(); - - let st: mainpod::Statement = - Statement::insert(new_root_ak, old_root_ak, key_ak, value_ak).into(); + let st: mainpod::Statement = Statement::insert(new_root, old_root, key, value).into(); let op = mainpod::Operation( OperationType::Native(NativeOperation::ContainerInsertFromEntries), vec![ OperationArg::Index(0), - OperationArg::Index(1), - OperationArg::Index(2), - OperationArg::Index(3), + OperationArg::Index(0), + OperationArg::Index(0), + OperationArg::Index(0), ], OperationAux::MerkleTreeStateTransitionProofIndex(0), ); - let merkle_tree_state_transition_proofs = vec![state_transition_proof]; - let prev_statements = vec![new_root_st, old_root_st, key_st, value_st]; - operation_verify( - st, - op, - prev_statements, - vec![], - vec![], - merkle_tree_state_transition_proofs, - ) + let aux = Aux::merkle_tree_state_transition_proof(state_transition_proof); + let prev_statements = vec![Statement::None.into()]; + operation_verify(st, op, prev_statements, aux) } #[test] @@ -2989,50 +2867,27 @@ mod tests { &[(175.into(), 55.into())].into(), )?; - let key = 175.into(); - let key_ak = AnchoredKey::from((PodId(RawValue::from(70).into()), "key")); + let key = Value::from(175); + let value = Value::from(0); + let state_transition_proof = tree.update(&key.raw(), &value.raw())?; + let old_root = state_transition_proof.old_root; + let new_root = state_transition_proof.new_root; - let value = 0.into(); - let value_ak = AnchoredKey::from((PodId(RawValue::from(72).into()), "value")); - - let state_transition_proof = tree.update(&key, &value)?; - - let old_root = Value::from(state_transition_proof.old_root); - let old_root_ak = AnchoredKey::from((PodId(RawValue::from(73).into()), "old_root")); - - let new_root = Value::from(state_transition_proof.new_root); - let new_root_ak = AnchoredKey::from((PodId(RawValue::from(74).into()), "new_root")); - - let new_root_st: mainpod::Statement = - Statement::equal(new_root_ak.clone(), new_root.clone()).into(); - let old_root_st: mainpod::Statement = - Statement::equal(old_root_ak.clone(), old_root.clone()).into(); - let key_st: mainpod::Statement = Statement::equal(key_ak.clone(), key).into(); - let value_st: mainpod::Statement = Statement::equal(value_ak.clone(), value).into(); - - let st: mainpod::Statement = - Statement::update(new_root_ak, old_root_ak, key_ak, value_ak).into(); + let st: mainpod::Statement = Statement::update(new_root, old_root, key, value).into(); let op = mainpod::Operation( OperationType::Native(NativeOperation::ContainerUpdateFromEntries), vec![ OperationArg::Index(0), - OperationArg::Index(1), - OperationArg::Index(2), - OperationArg::Index(3), + OperationArg::Index(0), + OperationArg::Index(0), + OperationArg::Index(0), ], OperationAux::MerkleTreeStateTransitionProofIndex(0), ); - let merkle_tree_state_transition_proofs = vec![state_transition_proof]; - let prev_statements = vec![new_root_st, old_root_st, key_st, value_st]; - operation_verify( - st, - op, - prev_statements, - vec![], - vec![], - merkle_tree_state_transition_proofs, - ) + let aux = Aux::merkle_tree_state_transition_proof(state_transition_proof); + let prev_statements = vec![Statement::None.into()]; + operation_verify(st, op, prev_statements, aux) } #[test] @@ -3044,44 +2899,25 @@ mod tests { &[(175.into(), 55.into())].into(), )?; - let key = 175.into(); - let key_ak = AnchoredKey::from((PodId(RawValue::from(70).into()), "key")); + let key = Value::from(175); + let state_transition_proof = tree.delete(&key.raw())?; + let old_root = state_transition_proof.old_root; + let new_root = state_transition_proof.new_root; - let state_transition_proof = tree.delete(&key)?; - - let old_root = Value::from(state_transition_proof.old_root); - let old_root_ak = AnchoredKey::from((PodId(RawValue::from(73).into()), "old_root")); - - let new_root = Value::from(state_transition_proof.new_root); - let new_root_ak = AnchoredKey::from((PodId(RawValue::from(74).into()), "new_root")); - - let new_root_st: mainpod::Statement = - Statement::equal(new_root_ak.clone(), new_root.clone()).into(); - let old_root_st: mainpod::Statement = - Statement::equal(old_root_ak.clone(), old_root.clone()).into(); - let key_st: mainpod::Statement = Statement::equal(key_ak.clone(), key).into(); - - let st: mainpod::Statement = Statement::delete(new_root_ak, old_root_ak, key_ak).into(); + let st: mainpod::Statement = Statement::delete(new_root, old_root, key).into(); let op = mainpod::Operation( OperationType::Native(NativeOperation::ContainerDeleteFromEntries), vec![ OperationArg::Index(0), - OperationArg::Index(1), - OperationArg::Index(2), + OperationArg::Index(0), + OperationArg::Index(0), ], OperationAux::MerkleTreeStateTransitionProofIndex(0), ); - let merkle_tree_state_transition_proofs = vec![state_transition_proof]; - let prev_statements = vec![new_root_st, old_root_st, key_st]; - operation_verify( - st, - op, - prev_statements, - vec![], - vec![], - merkle_tree_state_transition_proofs, - ) + let aux = Aux::merkle_tree_state_transition_proof(state_transition_proof); + let prev_statements = vec![Statement::None.into()]; + operation_verify(st, op, prev_statements, aux) } #[test] @@ -3094,23 +2930,16 @@ mod tests { .into_iter() .try_for_each(|secret_key| { let public_key = secret_key.public_key(); - let public_key_value = Value::from(TypedValue::from(public_key)); - let secret_key_value = Value::from(TypedValue::from(secret_key.clone())); - let public_key_ak = AnchoredKey::from((PodId(RawValue::from(88).into()), "public key")); - let secret_key_ak = AnchoredKey::from((PodId(RawValue::from(70).into()), "secret key")); - let public_key_st: mainpod::Statement = - Statement::equal(public_key_ak.clone(), public_key_value.clone()).into(); - let secret_key_st: mainpod::Statement = - Statement::equal(secret_key_ak.clone(), secret_key_value.clone()).into(); + let st: mainpod::Statement = - Statement::public_key_of(public_key_ak, secret_key_ak).into(); + Statement::public_key_of(public_key, secret_key.clone()).into(); let op = mainpod::Operation( OperationType::Native(NativeOperation::PublicKeyOf), - vec![OperationArg::Index(0), OperationArg::Index(1)], + vec![OperationArg::Index(0), OperationArg::Index(0)], OperationAux::PublicKeyOfIndex(0), ); - let prev_statements = vec![public_key_st, secret_key_st]; - operation_verify(st, op, prev_statements, vec![], vec![secret_key], vec![]) + let prev_statements = vec![Statement::None.into()]; + operation_verify(st, op, prev_statements, Aux::secret_key(secret_key)) }) } @@ -3118,102 +2947,87 @@ mod tests { fn test_operation_verify_publickeyof_failure_wrong_key() { let secret_key = SecretKey(BigUint::one()); let public_key = SecretKey(BigUint::ZERO).public_key(); - let public_key_value = Value::from(TypedValue::from(public_key)); - let secret_key_value = Value::from(TypedValue::from(secret_key.clone())); - let public_key_ak = AnchoredKey::from((PodId(RawValue::from(88).into()), "public key")); - let secret_key_ak = AnchoredKey::from((PodId(RawValue::from(70).into()), "secret key")); - let public_key_st: mainpod::Statement = - Statement::equal(public_key_ak.clone(), public_key_value.clone()).into(); - let secret_key_st: mainpod::Statement = - Statement::equal(secret_key_ak.clone(), secret_key_value.clone()).into(); - let st: mainpod::Statement = Statement::public_key_of(public_key_ak, secret_key_ak).into(); + + let st: mainpod::Statement = + Statement::public_key_of(public_key, secret_key.clone()).into(); let op = mainpod::Operation( OperationType::Native(NativeOperation::PublicKeyOf), - vec![OperationArg::Index(0), OperationArg::Index(1)], + vec![OperationArg::Index(0), OperationArg::Index(0)], OperationAux::PublicKeyOfIndex(0), ); - let prev_statements = vec![public_key_st, secret_key_st]; - assert!( - operation_verify(st, op, prev_statements, vec![], vec![secret_key], vec![]).is_err() - ) + let prev_statements = vec![Statement::None.into()]; + assert!(operation_verify(st, op, prev_statements, Aux::secret_key(secret_key)).is_err()) } #[test] fn test_operation_verify_publickeyof_failure_pk_type() { let secret_key = SecretKey(BigUint::one()); let public_key = 123i64; - let public_key_value = Value::from(TypedValue::from(public_key)); - let secret_key_value = Value::from(TypedValue::from(secret_key.clone())); - let public_key_ak = AnchoredKey::from((PodId(RawValue::from(88).into()), "public key")); - let secret_key_ak = AnchoredKey::from((PodId(RawValue::from(70).into()), "secret key")); - let public_key_st: mainpod::Statement = - Statement::equal(public_key_ak.clone(), public_key_value.clone()).into(); - let secret_key_st: mainpod::Statement = - Statement::equal(secret_key_ak.clone(), secret_key_value.clone()).into(); - let st: mainpod::Statement = Statement::public_key_of(public_key_ak, secret_key_ak).into(); + + let st: mainpod::Statement = + Statement::public_key_of(public_key, secret_key.clone()).into(); let op = mainpod::Operation( OperationType::Native(NativeOperation::PublicKeyOf), - vec![OperationArg::Index(0), OperationArg::Index(1)], + vec![OperationArg::Index(0), OperationArg::Index(0)], OperationAux::None, ); - let prev_statements = vec![public_key_st, secret_key_st]; - assert!( - operation_verify(st, op, prev_statements, vec![], vec![secret_key], vec![]).is_err() - ) + let prev_statements = vec![Statement::None.into()]; + assert!(operation_verify(st, op, prev_statements, Aux::secret_key(secret_key)).is_err()) } #[test] fn test_operation_verify_publickeyof_failure_sk_type() { let secret_key = 123i64; let public_key = SecretKey(BigUint::from(123u32)).public_key(); - let public_key_value = Value::from(TypedValue::from(public_key)); - let secret_key_value = Value::from(TypedValue::from(secret_key)); - let public_key_ak = AnchoredKey::from((PodId(RawValue::from(88).into()), "public key")); - let secret_key_ak = AnchoredKey::from((PodId(RawValue::from(70).into()), "secret key")); - let public_key_st: mainpod::Statement = - Statement::equal(public_key_ak.clone(), public_key_value.clone()).into(); - let secret_key_st: mainpod::Statement = - Statement::equal(secret_key_ak.clone(), secret_key_value.clone()).into(); - let st: mainpod::Statement = Statement::public_key_of(public_key_ak, secret_key_ak).into(); + + let st: mainpod::Statement = Statement::public_key_of(public_key, secret_key).into(); let op = mainpod::Operation( OperationType::Native(NativeOperation::PublicKeyOf), - vec![OperationArg::Index(0), OperationArg::Index(1)], + vec![OperationArg::Index(0), OperationArg::Index(0)], OperationAux::PublicKeyOfIndex(0), ); - let prev_statements = vec![public_key_st, secret_key_st]; - assert!(operation_verify( - st, - op, - prev_statements, - vec![], - vec![SecretKey(BigUint::from(123u32))], - vec![] - ) - .is_err()) + let prev_statements = vec![Statement::None.into()]; + let aux = Aux::secret_key(SecretKey(BigUint::from(123u32))); + assert!(operation_verify(st, op, prev_statements, aux,).is_err()) } #[test] fn test_operation_verify_publickeyof_failure_sk_size() { let secret_key = SecretKey(&*GROUP_ORDER - BigUint::ZERO); let public_key = secret_key.public_key(); - let public_key_value = Value::from(TypedValue::from(public_key)); - let secret_key_value = Value::from(TypedValue::from(secret_key.clone())); - let public_key_ak = AnchoredKey::from((PodId(RawValue::from(88).into()), "public key")); - let secret_key_ak = AnchoredKey::from((PodId(RawValue::from(70).into()), "secret key")); - let public_key_st: mainpod::Statement = - Statement::equal(public_key_ak.clone(), public_key_value.clone()).into(); - let secret_key_st: mainpod::Statement = - Statement::equal(secret_key_ak.clone(), secret_key_value.clone()).into(); - let st: mainpod::Statement = Statement::public_key_of(public_key_ak, secret_key_ak).into(); + + let st: mainpod::Statement = + Statement::public_key_of(public_key, secret_key.clone()).into(); let op = mainpod::Operation( OperationType::Native(NativeOperation::PublicKeyOf), - vec![OperationArg::Index(0), OperationArg::Index(1)], + vec![OperationArg::Index(0), OperationArg::Index(0)], OperationAux::PublicKeyOfIndex(0), ); - let prev_statements = vec![public_key_st, secret_key_st]; - assert!( - operation_verify(st, op, prev_statements, vec![], vec![secret_key], vec![]).is_err() - ) + let prev_statements = vec![Statement::None.into()]; + assert!(operation_verify(st, op, prev_statements, Aux::secret_key(secret_key)).is_err()) + } + + #[test] + fn test_operation_verify_signedby_ok() -> Result<()> { + let sk = SecretKey(BigUint::from_u32(0xbadcafe).unwrap()); + let pk = sk.public_key(); + let msg = RawValue([F(1), F(2), F(3), F(4)]); + let nonce = BigUint::from_u32(123).unwrap(); + let sig = signer::Signer(sk).sign_with_nonce(nonce, msg); + let signed_by = SignedBy { + msg, + pk, + sig: sig.clone(), + }; + + let st: mainpod::Statement = Statement::signed_by(msg, pk).into(); + let op = mainpod::Operation( + OperationType::Native(NativeOperation::SignedBy), + vec![OperationArg::Index(0), OperationArg::Index(0)], + OperationAux::SignedByIndex(0), + ); + let prev_statements = vec![Statement::None.into()]; + operation_verify(st, op, prev_statements, Aux::signed_by(signed_by)) } fn helper_statement_arg_from_template( @@ -3259,7 +3073,7 @@ mod tests { fn test_statement_arg_from_template() -> Result<()> { let params = Params::default(); - let pod_id = PodId(hash_str("pod_id")); + let dict = Hash([F(6), F(7), F(8), F(9)]); // case: None let st_tmpl_arg = StatementTmplArg::None; @@ -3276,8 +3090,8 @@ mod tests { // case: AnchoredKey(id_wildcard, key_literal) let st_tmpl_arg = StatementTmplArg::AnchoredKey(Wildcard::new("a".to_string(), 1), Key::from("foo")); - let args = vec![Value::from(1), Value::from(pod_id), Value::from(3)]; - let expected_st_arg = StatementArg::Key(AnchoredKey::new(pod_id, Key::from("foo"))); + let args = vec![Value::from(1), Value::from(dict), Value::from(3)]; + let expected_st_arg = StatementArg::Key(AnchoredKey::new(dict, Key::from("foo"))); helper_statement_arg_from_template(¶ms, st_tmpl_arg, args, expected_st_arg)?; // case: WildcardLiteral(wildcard) @@ -3332,7 +3146,7 @@ mod tests { fn test_statement_from_template() -> Result<()> { let params = Params::default(); - let pod_id = PodId(hash_str("pod_id")); + let dict = Hash([F(6), F(7), F(8), F(9)]); let st_tmpl = StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), @@ -3341,9 +3155,9 @@ mod tests { StatementTmplArg::Literal(Value::from("value")), ], }; - let args = vec![Value::from(1), Value::from(pod_id.0), Value::from(3)]; + let args = vec![Value::from(1), Value::from(dict), Value::from(3)]; let expected_st = Statement::equal( - AnchoredKey::new(pod_id, Key::from("key")), + AnchoredKey::new(dict, Key::from("key")), Value::from("value"), ); helper_statement_from_template(¶ms, st_tmpl, args, expected_st)?; @@ -3427,21 +3241,15 @@ mod tests { let _ = builder.predicate_or("pred_or", &["id"], &["secret"], &[stb0, stb1])?; let batch = builder.finish(); - let pod_id = PodId(hash_str("pod_id")); + let dict = Hash([F(6), F(7), F(8), F(9)]); // AND let custom_predicate = CustomPredicateRef::new(batch.clone(), 0); let op_args = vec![ - Statement::equal( - AnchoredKey::new(pod_id, Key::from("score")), - Value::from(42), - ), - Statement::equal( - AnchoredKey::new(pod_id, Key::from("key")), - Value::from(1234), - ), + Statement::equal(AnchoredKey::new(dict, Key::from("score")), Value::from(42)), + Statement::equal(AnchoredKey::new(dict, Key::from("key")), Value::from(1234)), ]; - let args = vec![Value::from(pod_id), Value::from(1234)]; + let args = vec![Value::from(dict), Value::from(1234)]; let expected_st = Statement::Custom( custom_predicate.clone(), vec![args[0].clone(), Value::from(0)], @@ -3459,13 +3267,10 @@ mod tests { // OR (1) let custom_predicate = CustomPredicateRef::new(batch.clone(), 1); let op_args = vec![ - Statement::equal( - AnchoredKey::new(pod_id, Key::from("score")), - Value::from(42), - ), + Statement::equal(AnchoredKey::new(dict, Key::from("score")), Value::from(42)), Statement::None, ]; - let args = vec![Value::from(pod_id), Value::from(0)]; + let args = vec![Value::from(dict), Value::from(0)]; let expected_st = Statement::Custom( custom_predicate.clone(), vec![args[0].clone(), Value::from(0)], @@ -3484,12 +3289,9 @@ mod tests { let custom_predicate = CustomPredicateRef::new(batch.clone(), 1); let op_args = vec![ Statement::None, - Statement::equal( - AnchoredKey::new(pod_id, Key::from("key")), - Value::from(1234), - ), + Statement::equal(AnchoredKey::new(dict, Key::from("key")), Value::from(1234)), ]; - let args = vec![Value::from(pod_id), Value::from(1234)]; + let args = vec![Value::from(dict), Value::from(1234)]; let expected_st = Statement::Custom( custom_predicate.clone(), vec![args[0].clone(), Value::from(0)], @@ -3535,22 +3337,19 @@ mod tests { let _ = builder.predicate_or("pred_or", &["id"], &["secret_id"], &[stb0, stb1])?; let batch = builder.finish(); - let pod_id = PodId(hash_str("pod_id")); - let secret_pod_id = PodId(hash_str("secret_pod_id")); + let dict = Hash([F(1), F(2), F(3), F(4)]); + let secret_dict = Hash([F(6), F(7), F(8), F(9)]); // AND (0) Sanity check with correct values let custom_predicate = CustomPredicateRef::new(batch.clone(), 0); let op_args = vec![ + Statement::equal(AnchoredKey::new(dict, Key::from("score")), Value::from(42)), Statement::equal( - AnchoredKey::new(pod_id, Key::from("score")), - Value::from(42), - ), - Statement::equal( - AnchoredKey::new(secret_pod_id, Key::from("key")), - AnchoredKey::new(pod_id, Key::from("score")), + AnchoredKey::new(secret_dict, Key::from("key")), + AnchoredKey::new(dict, Key::from("score")), ), ]; - let args = vec![Value::from(pod_id), Value::from(secret_pod_id)]; + let args = vec![Value::from(dict), Value::from(secret_dict)]; let expected_st = Statement::Custom( custom_predicate.clone(), vec![args[0].clone(), Value::from(0)], @@ -3565,19 +3364,16 @@ mod tests { ) .unwrap(); - // AND (1) Different pod_id for same wildcard + // AND (1) Different dict for same wildcard let custom_predicate = CustomPredicateRef::new(batch.clone(), 0); let op_args = vec![ + Statement::equal(AnchoredKey::new(dict, Key::from("score")), Value::from(42)), Statement::equal( - AnchoredKey::new(pod_id, Key::from("score")), - Value::from(42), - ), - Statement::equal( - AnchoredKey::new(secret_pod_id, Key::from("key")), - AnchoredKey::new(PodId(hash_str("BAD")), Key::from("score")), + AnchoredKey::new(secret_dict, Key::from("key")), + AnchoredKey::new(Hash([F(0), F(5), F(1), F(6)]), Key::from("score")), ), ]; - let args = vec![Value::from(pod_id), Value::from(secret_pod_id)]; + let args = vec![Value::from(dict), Value::from(secret_dict)]; assert!(helper_custom_operation_verify_gadget( ¶ms, @@ -3591,13 +3387,13 @@ mod tests { // AND (2) key doesn't match template let custom_predicate = CustomPredicateRef::new(batch.clone(), 0); let op_args = vec![ - Statement::equal(AnchoredKey::new(pod_id, Key::from("BAD")), Value::from(42)), + Statement::equal(AnchoredKey::new(dict, Key::from("BAD")), Value::from(42)), Statement::equal( - AnchoredKey::new(secret_pod_id, Key::from("key")), - AnchoredKey::new(pod_id, Key::from("score")), + AnchoredKey::new(secret_dict, Key::from("key")), + AnchoredKey::new(dict, Key::from("score")), ), ]; - let args = vec![Value::from(pod_id), Value::from(secret_pod_id)]; + let args = vec![Value::from(dict), Value::from(secret_dict)]; assert!(helper_custom_operation_verify_gadget( ¶ms, @@ -3612,15 +3408,15 @@ mod tests { let custom_predicate = CustomPredicateRef::new(batch.clone(), 0); let op_args = vec![ Statement::equal( - AnchoredKey::new(pod_id, Key::from("score")), + AnchoredKey::new(dict, Key::from("score")), Value::from(0xbad), ), Statement::equal( - AnchoredKey::new(secret_pod_id, Key::from("key")), - AnchoredKey::new(pod_id, Key::from("score")), + AnchoredKey::new(secret_dict, Key::from("key")), + AnchoredKey::new(dict, Key::from("score")), ), ]; - let args = vec![Value::from(pod_id), Value::from(secret_pod_id)]; + let args = vec![Value::from(dict), Value::from(secret_dict)]; assert!(helper_custom_operation_verify_gadget( ¶ms, @@ -3634,16 +3430,13 @@ mod tests { // AND (4) predicate doesn't match template let custom_predicate = CustomPredicateRef::new(batch.clone(), 0); let op_args = vec![ - Statement::equal( - AnchoredKey::new(pod_id, Key::from("score")), - Value::from(42), - ), + Statement::equal(AnchoredKey::new(dict, Key::from("score")), Value::from(42)), Statement::not_equal( - AnchoredKey::new(secret_pod_id, Key::from("key")), - AnchoredKey::new(pod_id, Key::from("score")), + AnchoredKey::new(secret_dict, Key::from("key")), + AnchoredKey::new(dict, Key::from("score")), ), ]; - let args = vec![Value::from(pod_id), Value::from(secret_pod_id)]; + let args = vec![Value::from(dict), Value::from(secret_dict)]; assert!(helper_custom_operation_verify_gadget( ¶ms, @@ -3657,7 +3450,7 @@ mod tests { // OR (1) Two Nones let custom_predicate = CustomPredicateRef::new(batch.clone(), 1); let op_args = vec![Statement::None, Statement::None]; - let args = vec![Value::from(pod_id), Value::from(0)]; + let args = vec![Value::from(dict), Value::from(0)]; assert!(helper_custom_operation_verify_gadget( ¶ms, @@ -3671,14 +3464,15 @@ mod tests { Ok(()) } - fn helper_calculate_id(params: &Params, statements: &[Statement]) -> Result<()> { + fn helper_calculate_statements_hash(params: &Params, statements: &[Statement]) -> Result<()> { let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::new(config); let statements_target = (0..params.max_public_statements) .map(|_| builder.add_virtual_statement(params)) .collect_vec(); - let id_target = calculate_id_circuit(params, &mut builder, &statements_target); + let sts_hash_target = + calculate_statements_hash_circuit(params, &mut builder, &statements_target); let mut pw = PartialWitness::::new(); @@ -3695,11 +3489,11 @@ mod tests { st_target.set_targets(&mut pw, params, st)?; } // Expected Output - let expected_id = calculate_id(&statements, params); + let expected_sts_hash = calculate_statements_hash(&statements, params); pw.set_hash_target( - id_target, + sts_hash_target, HashOut { - elements: expected_id.0, + elements: expected_sts_hash.0, }, )?; @@ -3710,28 +3504,29 @@ mod tests { } #[test] - fn test_calculate_id() -> frontend::Result<()> { + fn test_calculate_sts_hash() -> frontend::Result<()> { // Case with no public public statements let params = Params { max_public_statements: 0, - num_public_statements_id: 8, + num_public_statements_hash: 8, ..Default::default() }; - helper_calculate_id(¶ms, &[]).unwrap(); + helper_calculate_statements_hash(¶ms, &[]).unwrap(); - // Case with number of statements for the id equal to number of public statements + // Case with number of statements for the sts_hash equal to number of public statements let params = Params { max_public_statements: 2, - num_public_statements_id: 2, + num_public_statements_hash: 2, ..Default::default() }; + let dict = Hash([F(1), F(2), F(3), F(4)]); let statements = [ - Statement::equal(AnchoredKey::from((SELF, "foo")), Value::from(42)), + Statement::equal(AnchoredKey::from((dict, "foo")), Value::from(42)), Statement::equal( - AnchoredKey::from((SELF, "bar")), - AnchoredKey::from((SELF, "baz")), + AnchoredKey::from((dict, "bar")), + AnchoredKey::from((dict, "baz")), ), ] .into_iter() @@ -3739,25 +3534,25 @@ mod tests { .take(params.max_public_statements) .collect_vec(); - helper_calculate_id(¶ms, &statements).unwrap(); + helper_calculate_statements_hash(¶ms, &statements).unwrap(); - // Case with more statements for the id than the number of public statements + // Case with more statements for the sts_hash than the number of public statements let params = Params { max_public_statements: 4, - num_public_statements_id: 6, + num_public_statements_hash: 6, ..Default::default() }; - let pod_id = PodId(hash_str("pod_id")); + let dict2 = Hash([F(5), F(6), F(7), F(8)]); let statements = [ - Statement::equal(AnchoredKey::from((SELF, "foo")), Value::from(42)), + Statement::equal(AnchoredKey::from((dict, "foo")), Value::from(42)), Statement::equal( - AnchoredKey::from((SELF, "bar")), - AnchoredKey::from((SELF, "baz")), + AnchoredKey::from((dict, "bar")), + AnchoredKey::from((dict, "baz")), ), Statement::lt( - AnchoredKey::from((pod_id, "one")), - AnchoredKey::from((pod_id, "two")), + AnchoredKey::from((dict2, "one")), + AnchoredKey::from((dict2, "two")), ), ] .into_iter() @@ -3765,7 +3560,7 @@ mod tests { .take(params.max_public_statements) .collect_vec(); - helper_calculate_id(¶ms, &statements).unwrap(); + helper_calculate_statements_hash(¶ms, &statements).unwrap(); Ok(()) } diff --git a/src/backends/plonky2/circuits/mod.rs b/src/backends/plonky2/circuits/mod.rs index d98c848..35e3bea 100644 --- a/src/backends/plonky2/circuits/mod.rs +++ b/src/backends/plonky2/circuits/mod.rs @@ -3,5 +3,4 @@ pub mod hash; pub mod mainpod; pub mod metrics; pub mod mux_table; -pub mod signedpod; pub mod utils; diff --git a/src/backends/plonky2/circuits/signedpod.rs b/src/backends/plonky2/circuits/signedpod.rs deleted file mode 100644 index 7bd9107..0000000 --- a/src/backends/plonky2/circuits/signedpod.rs +++ /dev/null @@ -1,252 +0,0 @@ -use std::iter; - -use itertools::Itertools; -use plonky2::{ - hash::hash_types::{HashOut, HashOutTarget}, - iop::witness::{PartialWitness, WitnessWrite}, - plonk::circuit_builder::CircuitBuilder, -}; -use serde::{Deserialize, Serialize}; - -use crate::{ - backends::plonky2::{ - basetypes::D, - circuits::common::{ - CircuitBuilderPod, PredicateTarget, StatementArgTarget, StatementTarget, ValueTarget, - }, - error::Result, - primitives::{ - merkletree::{ - verify_merkle_proof_existence_circuit, MerkleClaimAndProof, - MerkleProofExistenceTarget, - }, - signature::{verify_signature_circuit, SignatureVerifyTarget}, - }, - signedpod::SignedPod, - }, - measure_gates_begin, measure_gates_end, - middleware::{ - hash_str, Key, NativePredicate, Params, PodType, RawValue, Value, F, KEY_SIGNER, KEY_TYPE, - SELF, - }, -}; - -pub fn verify_signed_pod_circuit( - builder: &mut CircuitBuilder, - signed_pod: &SignedPodVerifyTarget, -) -> Result<()> { - let params = &signed_pod.params; - let measure = measure_gates_begin!(builder, "SignedPodVerify"); - // 1. Verify id - assert_eq!(params.max_signed_pod_values, signed_pod.mt_proofs.len()); - for mt_proof in &signed_pod.mt_proofs { - verify_merkle_proof_existence_circuit(builder, mt_proof); - builder.connect_hashes(signed_pod.id, mt_proof.root); - // mt_proofs.push(mt_proof); - } - - // 2. Verify type - let type_mt_proof = &signed_pod.mt_proofs[0]; - let key_type = builder.constant_value(hash_str(KEY_TYPE).into()); - builder.connect_values(type_mt_proof.key, key_type); - let value_type = builder.constant_value(Value::from(PodType::Signed).raw()); - builder.connect_values(type_mt_proof.value, value_type); - - // 3.a. Verify signature - verify_signature_circuit(builder, &signed_pod.signature); - - // 3.b. Verify signer (ie. hash(signature.pk) == merkletree.signer_leaf) - let signer_mt_proof = &signed_pod.mt_proofs[1]; - let key_signer = builder.constant_value(Key::from(KEY_SIGNER).raw()); - let pk_hash = signed_pod.signature.pk.to_value(builder); - builder.connect_values(signer_mt_proof.key, key_signer); - builder.connect_values(signer_mt_proof.value, pk_hash); - - // 3.c. connect signed message to pod.id - builder.connect_values( - ValueTarget::from_slice(&signed_pod.id.elements), - signed_pod.signature.msg, - ); - - measure_gates_end!(builder, measure); - Ok(()) -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct SignedPodVerifyTarget { - params: Params, - id: HashOutTarget, - // the KEY_TYPE entry must be the first one - // the KEY_SIGNER entry must be the second one - mt_proofs: Vec, - pub(crate) signature: SignatureVerifyTarget, -} - -impl SignedPodVerifyTarget { - pub fn new_virtual(params: &Params, builder: &mut CircuitBuilder) -> Self { - SignedPodVerifyTarget { - params: params.clone(), - id: builder.add_virtual_hash(), - mt_proofs: (0..params.max_signed_pod_values) - .map(|_| { - MerkleProofExistenceTarget::new_virtual(params.max_depth_mt_containers, builder) - }) - .collect(), - signature: SignatureVerifyTarget::new_virtual(builder), - } - } - pub fn pub_statements( - &self, - builder: &mut CircuitBuilder, - self_id: bool, - ) -> Vec { - let mut statements = Vec::new(); - let predicate = PredicateTarget::new_native(builder, &self.params, NativePredicate::Equal); - let pod_id = if self_id { - builder.constant_value(SELF.0.into()) - } else { - ValueTarget { - elements: self.id.elements, - } - }; - for mt_proof in &self.mt_proofs { - let args = [ - StatementArgTarget::anchored_key(builder, &pod_id, &mt_proof.key), - StatementArgTarget::literal(builder, &mt_proof.value), - ] - .into_iter() - .chain(iter::repeat_with(|| StatementArgTarget::none(builder))) - .take(self.params.max_statement_args) - .collect(); - let statement = StatementTarget { - predicate: predicate.clone(), - args, - }; - statements.push(statement); - } - statements - } - - pub fn set_targets(&self, pw: &mut PartialWitness, pod: &SignedPod) -> Result<()> { - // set the self.mt_proofs witness with the following order: - // - KEY_TYPE leaf proof - // - KEY_SIGNER leaf proof - // - rest of leaves - // - empty leaves (if needed) - - // add proof verification of KEY_TYPE & KEY_SIGNER leaves - let key_type_key = Key::from(KEY_TYPE); - let key_signer_key = Key::from(KEY_SIGNER); - [&key_type_key, &key_signer_key] - .iter() - .enumerate() - .try_for_each(|(i, k)| { - let (v, proof) = pod.dict.prove(k)?; - self.mt_proofs[i].set_targets( - pw, - true, - &MerkleClaimAndProof::new(pod.dict.commitment(), k.raw(), Some(v.raw()), proof), - ) - })?; - - // add the verification of the rest of leaves - let mut curr = 2; // since we already added key_type and key_signer - for (k, v) in pod.dict.kvs().iter().sorted_by_key(|kv| kv.0.hash()) { - if *k == key_type_key || *k == key_signer_key { - // skip the key_type & key_signer leaves, since they have - // already been checked - continue; - } - - let (obtained_v, proof) = pod.dict.prove(k)?; - assert_eq!(obtained_v, v); // sanity check - - self.mt_proofs[curr].set_targets( - pw, - true, - &MerkleClaimAndProof::new(pod.dict.commitment(), k.raw(), Some(v.raw()), proof), - )?; - curr += 1; - } - // sanity check - assert!(curr <= self.params.max_signed_pod_values); - - // add the proofs of empty leaves (if needed), till the max_signed_pod_values - let mut mp = MerkleClaimAndProof::empty(); - mp.root = pod.dict.commitment(); - for i in curr..self.params.max_signed_pod_values { - self.mt_proofs[i].set_targets(pw, false, &mp)?; - } - - // get the signer pk - let pk = pod.signer; - // the msg signed is the pod.id - let msg = RawValue::from(pod.id.0); - - // set signature targets values - self.signature - .set_targets(pw, true, pk, msg, pod.signature.clone())?; - - // set the id target value - pw.set_hash_target(self.id, HashOut::from_vec(pod.id.0 .0.to_vec()))?; - - Ok(()) - } -} - -#[cfg(test)] -pub mod tests { - use std::any::Any; - - use plonky2::plonk::{circuit_builder::CircuitBuilder, circuit_data::CircuitConfig}; - - use super::*; - use crate::{ - backends::plonky2::{ - basetypes::C, - primitives::ec::schnorr::SecretKey, - signedpod::{SignedPod, Signer}, - }, - middleware::F, - }; - - #[test] - fn test_signed_pod_verify() -> Result<()> { - let params = Params { - max_signed_pod_values: 6, - ..Default::default() - }; - // set max_signed_pod_values to 6, and we insert 3 leaves, so that the - // circuit has enough space for the 3 leaves plus the KEY_TYPE and - // KEY_SIGNER and one empty leaf. - - // prepare a signedpod - let mut pod = crate::frontend::SignedPodBuilder::new(¶ms); - pod.insert("idNumber", "4242424242"); - pod.insert("dateOfBirth", 1169909384); - pod.insert("socialSecurityNumber", "G2121210"); - let sk = SecretKey::new_rand(); - let signer = Signer(sk); - let pod = pod.sign(&signer).unwrap(); - let signed_pod = (pod.pod as Box).downcast::().unwrap(); - - // use the pod in the circuit - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); - let mut pw = PartialWitness::::new(); - - // build the circuit logic - let signed_pod_verify = SignedPodVerifyTarget::new_virtual(¶ms, &mut builder); - verify_signed_pod_circuit(&mut builder, &signed_pod_verify)?; - - // set the signed_pod as target values for the circuit - signed_pod_verify.set_targets(&mut pw, &signed_pod)?; - - // generate & verify proof - let data = builder.build::(); - let proof = data.prove(pw)?; - data.verify(proof)?; - - Ok(()) - } -} diff --git a/src/backends/plonky2/emptypod.rs b/src/backends/plonky2/emptypod.rs index f9eb24b..2bc8b29 100644 --- a/src/backends/plonky2/emptypod.rs +++ b/src/backends/plonky2/emptypod.rs @@ -3,7 +3,6 @@ use plonky2::{ hash::hash_types::HashOutTarget, iop::witness::{PartialWitness, WitnessWrite}, plonk::{ - circuit_builder::CircuitBuilder, circuit_data::{self, CircuitConfig}, proof::ProofWithPublicInputs, }, @@ -12,16 +11,16 @@ use serde::{Deserialize, Serialize}; use crate::{ backends::plonky2::{ - basetypes::{Proof, C, D}, + basetypes::{CircuitBuilder, Proof, C, D}, cache_get_standard_rec_main_pod_common_circuit_data, circuits::{ common::{Flattenable, StatementTarget}, - mainpod::{calculate_id_circuit, PI_OFFSET_ID}, + mainpod::{calculate_statements_hash_circuit, PI_OFFSET_STATEMENTS_HASH}, }, deserialize_proof, deserialize_verifier_only, error::{Error, Result}, hash_common_data, - mainpod::{self, calculate_id}, + mainpod::{self, calculate_statements_hash}, recursion::pad_circuit, serialization::{ CircuitDataSerializer, VerifierCircuitDataSerializer, VerifierOnlyCircuitDataSerializer, @@ -30,52 +29,57 @@ use crate::{ }, cache::{self, CacheEntry}, middleware::{ - self, AnchoredKey, Hash, Params, Pod, PodId, PodType, RecursivePod, Statement, ToFields, - VDSet, Value, VerifierOnlyCircuitData, F, HASH_SIZE, KEY_TYPE, SELF, + self, Hash, IntroPredicateRef, Params, Pod, PodType, Statement, ToFields, VDSet, + VerifierOnlyCircuitData, EMPTY_HASH, F, HASH_SIZE, }, timed, }; -struct EmptyPodVerifyCircuit { - params: Params, -} - -fn type_statement() -> Statement { - Statement::equal( - AnchoredKey::from((SELF, KEY_TYPE)), - Value::from(PodType::Empty), +fn empty_statement() -> Statement { + Statement::Intro( + IntroPredicateRef { + name: "empty".to_string(), + args_len: 0, + verifier_data_hash: EMPTY_HASH, + }, + vec![], ) } -impl EmptyPodVerifyCircuit { - fn eval(&self, builder: &mut CircuitBuilder) -> Result { - let type_statement = StatementTarget::from_flattened( - &self.params, - &builder.constants(&type_statement().to_fields(&self.params)), - ); - let id = calculate_id_circuit(&self.params, builder, &[type_statement]); - let vds_root = builder.add_virtual_hash(); - builder.register_public_inputs(&id.elements); - builder.register_public_inputs(&vds_root.elements); - Ok(EmptyPodVerifyTarget { vds_root }) - } -} - #[derive(Clone, Serialize, Deserialize)] pub struct EmptyPodVerifyTarget { vds_root: HashOutTarget, } impl EmptyPodVerifyTarget { + pub fn new_virtual(builder: &mut CircuitBuilder) -> Self { + Self { + vds_root: builder.add_virtual_hash(), + } + } pub fn set_targets(&self, pw: &mut PartialWitness, vds_root: Hash) -> Result<()> { Ok(pw.set_target_arr(&self.vds_root.elements, &vds_root.0)?) } } +fn verify_empty_pod_circuit( + params: &Params, + builder: &mut CircuitBuilder, + empty_pod: &EmptyPodVerifyTarget, +) { + let empty_statement = StatementTarget::from_flattened( + params, + &builder.constants(&empty_statement().to_fields(params)), + ); + let sts_hash = calculate_statements_hash_circuit(params, builder, &[empty_statement]); + builder.register_public_inputs(&sts_hash.elements); + builder.register_public_inputs(&empty_pod.vds_root.elements); +} + #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct EmptyPod { params: Params, - id: PodId, + sts_hash: Hash, verifier_only: VerifierOnlyCircuitDataSerializer, common_hash: String, vd_set: VDSet, @@ -110,17 +114,15 @@ fn build() -> Result<(EmptyPodVerifyTarget, CircuitData)> { #[cfg(feature = "zk")] let config = CircuitConfig::standard_recursion_zk_config(); - let mut builder = CircuitBuilder::::new(config); - let empty_pod_verify_target = EmptyPodVerifyCircuit { - params: params.clone(), - } - .eval(&mut builder)?; + let mut builder = CircuitBuilder::new(config); + let empty_pod = EmptyPodVerifyTarget::new_virtual(&mut builder); + verify_empty_pod_circuit(¶ms, &mut builder, &empty_pod); let common_circuit_data = &*cache_get_standard_rec_main_pod_common_circuit_data(); pad_circuit(&mut builder, common_circuit_data); let data = timed!("EmptyPod build", builder.build::()); assert_eq!(common_circuit_data.0, data.common); - Ok((empty_pod_verify_target, data)) + Ok((empty_pod, data)) } impl EmptyPod { @@ -130,19 +132,22 @@ impl EmptyPod { let mut pw = PartialWitness::::new(); empty_pod_verify_target.set_targets(&mut pw, vd_set.root())?; let proof = timed!("EmptyPod prove", data.prove(pw)?); - let id = &proof.public_inputs[PI_OFFSET_ID..PI_OFFSET_ID + HASH_SIZE]; - let id = PodId(Hash([id[0], id[1], id[2], id[3]])); + let sts_hash = { + let v = &proof.public_inputs + [PI_OFFSET_STATEMENTS_HASH..PI_OFFSET_STATEMENTS_HASH + HASH_SIZE]; + Hash([v[0], v[1], v[2], v[3]]) + }; let common_hash = hash_common_data(&data.common).expect("hash ok"); Ok(EmptyPod { params: params.clone(), verifier_only: VerifierOnlyCircuitDataSerializer(data.verifier_only.clone()), common_hash, - id, + sts_hash, vd_set, proof: proof.proof, }) } - pub fn new_boxed(params: &Params, vd_set: VDSet) -> Box { + pub fn new_boxed(params: &Params, vd_set: VDSet) -> Box { let default_params = Params::default(); assert_eq!(default_params.id_params(), params.id_params()); @@ -173,12 +178,12 @@ impl Pod for EmptyPod { .into_iter() .map(mainpod::Statement::from) .collect_vec(); - let id = PodId(calculate_id(&statements, &self.params)); - if id != self.id { - return Err(Error::id_not_equal(self.id, id)); + let sts_hash = calculate_statements_hash(&statements, &self.params); + if sts_hash != self.sts_hash { + return Err(Error::statements_hash_not_equal(self.sts_hash, sts_hash)); } - let public_inputs = id + let public_inputs = sts_hash .to_fields(&self.params) .iter() .chain(self.vd_set.root().0.iter()) @@ -194,28 +199,17 @@ impl Pod for EmptyPod { .map_err(|e| Error::plonky2_proof_fail("EmptyPod", e)) } - fn id(&self) -> PodId { - self.id + fn statements_hash(&self) -> Hash { + self.sts_hash } fn pod_type(&self) -> (usize, &'static str) { (PodType::Empty as usize, "Empty") } fn pub_self_statements(&self) -> Vec { - vec![type_statement()] + vec![empty_statement()] } - fn serialize_data(&self) -> serde_json::Value { - serde_json::to_value(Data { - proof: serialize_proof(&self.proof), - verifier_only: serialize_verifier_only(&self.verifier_only), - common_hash: self.common_hash.clone(), - }) - .expect("serialization to json") - } -} - -impl RecursivePod for EmptyPod { fn verifier_data(&self) -> VerifierOnlyCircuitData { self.verifier_only.0.clone() } @@ -228,24 +222,33 @@ impl RecursivePod for EmptyPod { fn vd_set(&self) -> &VDSet { &self.vd_set } + + fn serialize_data(&self) -> serde_json::Value { + serde_json::to_value(Data { + proof: serialize_proof(&self.proof), + verifier_only: serialize_verifier_only(&self.verifier_only), + common_hash: self.common_hash.clone(), + }) + .expect("serialization to json") + } fn deserialize_data( params: Params, data: serde_json::Value, vd_set: VDSet, - id: PodId, - ) -> Result> { + sts_hash: Hash, + ) -> Result { let data: Data = serde_json::from_value(data)?; let common_circuit_data = cache_get_standard_rec_main_pod_common_circuit_data(); let proof = deserialize_proof(&common_circuit_data, &data.proof)?; let verifier_only = deserialize_verifier_only(&data.verifier_only)?; - Ok(Box::new(Self { + Ok(Self { params, - id, + sts_hash, verifier_only: VerifierOnlyCircuitDataSerializer(verifier_only), common_hash: data.common_hash, vd_set, proof, - })) + }) } } diff --git a/src/backends/plonky2/error.rs b/src/backends/plonky2/error.rs index bd5d29d..355eaf1 100644 --- a/src/backends/plonky2/error.rs +++ b/src/backends/plonky2/error.rs @@ -1,21 +1,15 @@ use std::{backtrace::Backtrace, fmt::Debug}; -use crate::middleware::{PodId, PodType, Value}; +use crate::middleware::Hash; pub type Result = core::result::Result; #[derive(thiserror::Error, Debug)] pub enum InnerError { - #[error("id does not match, expected {0}, found {1}")] - IdNotEqual(PodId, PodId), - #[error("type does not match, expected {0}, found {1}")] - TypeNotEqual(PodType, Value), - #[error("signer public key does not match, expected {0}, found {1}")] - SignerNotEqual(Value, Value), + #[error("Statements hash does not match, expected {0}, found {1}")] + StsHashNotEqual(Hash, Hash), // POD related - #[error("invalid POD ID")] - PodIdInvalid, #[error("verification failed: POD does not have type statement")] NotTypeStatement, #[error("repeated ValueOf")] @@ -85,16 +79,7 @@ impl Error { pub fn not_type_statement() -> Self { new!(NotTypeStatement) } - pub fn pod_id_invalid() -> Self { - new!(PodIdInvalid) - } - pub fn id_not_equal(expected: PodId, found: PodId) -> Self { - new!(IdNotEqual(expected, found)) - } - pub fn type_not_equal(expected: PodType, found: Value) -> Self { - new!(TypeNotEqual(expected, found)) - } - pub(crate) fn signer_not_equal(expected: Value, found: Value) -> Self { - new!(SignerNotEqual(expected, found)) + pub fn statements_hash_not_equal(expected: Hash, found: Hash) -> Self { + new!(StsHashNotEqual(expected, found)) } } diff --git a/src/backends/plonky2/mainpod/mod.rs b/src/backends/plonky2/mainpod/mod.rs index 638ecb2..828b78b 100644 --- a/src/backends/plonky2/mainpod/mod.rs +++ b/src/backends/plonky2/mainpod/mod.rs @@ -1,8 +1,10 @@ pub mod operation; +use crate::middleware::PodType; pub mod statement; -use std::{any::Any, iter, sync::Arc}; +use std::{iter, sync::Arc}; use itertools::Itertools; +use num_bigint::BigUint; pub use operation::*; use plonky2::{hash::poseidon::PoseidonHash, plonk::config::Hasher}; use serde::{Deserialize, Serialize}; @@ -10,7 +12,7 @@ pub use statement::*; use crate::{ backends::plonky2::{ - basetypes::{CircuitData, Proof, ProofWithPublicInputs, VerifierOnlyCircuitData}, + basetypes::{CircuitData, Proof, ProofWithPublicInputs, VerifierOnlyCircuitData, F}, cache::{self, CacheEntry}, cache_get_standard_rec_main_pod_common_circuit_data, circuits::mainpod::{CustomPredicateVerification, MainPodVerifyInput, MainPodVerifyTarget}, @@ -20,7 +22,10 @@ use crate::{ hash_common_data, mock::emptypod::MockEmptyPod, primitives::{ - ec::schnorr::SecretKey, + ec::{ + curve::Point as PublicKey, + schnorr::{SecretKey, Signature}, + }, merkletree::{MerkleClaimAndProof, MerkleTreeStateTransitionProof}, }, recursion::{ @@ -30,32 +35,31 @@ use crate::{ CircuitDataSerializer, CommonCircuitDataSerializer, VerifierCircuitDataSerializer, }, serialize_proof, serialize_verifier_only, - signedpod::SignedPod, }, middleware::{ - self, resolve_wildcard_values, value_from_op, AnchoredKey, CustomPredicateBatch, - Error as MiddlewareError, Hash, MainPodInputs, NativeOperation, OperationType, Params, Pod, - PodId, PodProver, PodType, RecursivePod, StatementArg, ToFields, VDSet, KEY_TYPE, SELF, + self, resolve_wildcard_values, value_from_op, CustomPredicateBatch, + Error as MiddlewareError, Hash, MainPodInputs, MainPodProver, NativeOperation, + OperationType, Params, Pod, RawValue, StatementArg, ToFields, VDSet, }, timed, }; -/// Hash a list of public statements to derive the PodId. To make circuits with different number -/// of `max_public_statements compatible we pad the statements up to `num_public_statements_id`. -/// As an optimization we front pad with none-statements so that circuits with a small -/// `max_public_statements` only pay for `max_public_statements` by starting the poseidon state -/// with a precomputed constant corresponding to the front-padding part: -/// `id = hash(serialize(reverse(statements || none-statements)))` -pub fn calculate_id(statements: &[Statement], params: &Params) -> middleware::Hash { - assert!(statements.len() <= params.num_public_statements_id); - assert!(params.max_public_statements <= params.num_public_statements_id); +/// Hash a list of public statements to derive the Statements hash. To make circuits with +/// different number of `max_public_statements compatible we pad the statements up to +/// `num_public_statements_id`. As an optimization we front pad with none-statements so that +/// circuits with a small `max_public_statements` only pay for `max_public_statements` by starting +/// the poseidon state with a precomputed constant corresponding to the front-padding part: `id = +/// hash(serialize(reverse(statements || none-statements)))` +pub fn calculate_statements_hash(statements: &[Statement], params: &Params) -> middleware::Hash { + assert!(statements.len() <= params.num_public_statements_hash); + assert!(params.max_public_statements <= params.num_public_statements_hash); let mut none_st: Statement = middleware::Statement::None.into(); pad_statement(params, &mut none_st); let statements_back_padded = statements .iter() .chain(iter::repeat(&none_st)) - .take(params.num_public_statements_id) + .take(params.num_public_statements_hash) .collect_vec(); let field_elems = statements_back_padded .iter() @@ -236,6 +240,60 @@ pub(crate) fn extract_public_key_of( Ok(table) } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct SignedBy { + pub msg: RawValue, + pub pk: PublicKey, + pub sig: Signature, +} + +impl SignedBy { + /// A valid deterministic signature from a known private key and nonce, used for padding + pub fn dummy() -> Self { + let sk = SecretKey(BigUint::from(1u32)); + let pk = sk.public_key(); + let msg = RawValue([F(0), F(0), F(0), F(0)]); + let nonce = BigUint::from(2u32); + let sig = sk.sign(msg, &nonce); + Self { msg, pk, sig } + } +} + +/// Extracts Signatures verification data from SignedBy ops. +pub(crate) fn extract_signatures( + params: &Params, + aux_list: &mut [OperationAux], + operations: &[middleware::Operation], + statements: &[middleware::Statement], +) -> Result> { + let mut table = Vec::new(); + for (i, (op, st)) in operations.iter().zip(statements.iter()).enumerate() { + let deduction_err = || MiddlewareError::invalid_deduction(op.clone(), st.clone()); + if let ( + middleware::Operation::SignedBy(msg_s, pk_s, sig), + middleware::Statement::SignedBy(msg_ref, pk_ref), + ) = (op, st) + { + let msg = value_from_op(msg_s, msg_ref).ok_or_else(deduction_err)?; + let pk = value_from_op(pk_s, pk_ref).ok_or_else(deduction_err)?; + aux_list[i] = OperationAux::SignedByIndex(table.len()); + table.push(SignedBy { + msg: msg.raw(), + pk: PublicKey::try_from(pk.typed())?, + sig: sig.clone(), + }); + } + } + if table.len() > params.max_signed_by { + return Err(Error::custom(format!( + "The number of required signatures ({}) exceeds the maximum number ({}).", + table.len(), + params.max_signed_by + ))); + } + Ok(table) +} + /// Find the operation argument statement in the list of previous statements and return the index. fn find_op_arg(statements: &[Statement], op_arg: &middleware::Statement) -> Result { // NOTE: The `None` `Statement` always exists as a constant at index 0 @@ -283,37 +341,17 @@ pub(crate) fn layout_statements( // predicate statements statements.push(middleware::Statement::None.into()); - // Input signed pods region - let dummy_signed_pod_box: Box = Box::new(SignedPod::dummy()); - let dummy_signed_pod = dummy_signed_pod_box.as_ref(); - assert!(inputs.signed_pods.len() <= params.max_input_signed_pods); - for i in 0..params.max_input_signed_pods { - let pod = inputs.signed_pods.get(i).unwrap_or(&dummy_signed_pod); - let sts = pod.pub_statements(); - assert!(sts.len() <= params.max_signed_pod_values); - for j in 0..params.max_signed_pod_values { - let mut st = sts - .get(j) - .unwrap_or(&middleware::Statement::None) - .clone() - .into(); - pad_statement(params, &mut st); - statements.push(st); - } - } - - // Input main pods region - let empty_pod_box: Box = - if mock || inputs.recursive_pods.len() == params.max_input_recursive_pods { - // We mocking or we don't need padding so we skip creating an EmptyPod - MockEmptyPod::new_boxed(params, inputs.vd_set.clone()) - } else { - EmptyPod::new_boxed(params, inputs.vd_set.clone()) - }; + // Input pods region + let empty_pod_box: Box = if mock || inputs.pods.len() == params.max_input_pods { + // We mocking or we don't need padding so we skip creating an EmptyPod + MockEmptyPod::new_boxed(params, inputs.vd_set.clone()) + } else { + EmptyPod::new_boxed(params, inputs.vd_set.clone()) + }; let empty_pod = empty_pod_box.as_ref(); - assert!(inputs.recursive_pods.len() <= params.max_input_recursive_pods); - for i in 0..params.max_input_recursive_pods { - let pod = inputs.recursive_pods.get(i).copied().unwrap_or(empty_pod); + assert!(inputs.pods.len() <= params.max_input_pods); + for i in 0..params.max_input_pods { + let pod = inputs.pods.get(i).copied().unwrap_or(empty_pod); let sts = pod.pub_statements(); assert!(sts.len() <= params.max_public_statements); for j in 0..params.max_input_pods_public_statements { @@ -347,20 +385,7 @@ pub(crate) fn layout_statements( // Public statements assert!(inputs.public_statements.len() < params.max_public_statements); - let pod_type = if mock { - PodType::MockMain - } else { - PodType::Main - }; - let mut type_st = middleware::Statement::Equal( - AnchoredKey::from((SELF, KEY_TYPE)).into(), - middleware::Value::from(pod_type).into(), - ) - .into(); - pad_statement(params, &mut type_st); - statements.push(type_st); - - for i in 0..(params.max_public_statements - 1) { + for i in 0..params.max_public_statements { let mut st = inputs .public_statements .get(i) @@ -373,7 +398,7 @@ pub(crate) fn layout_statements( let offset_public_statements = statements.len() - params.max_public_statements; let public_statements = statements - [offset_public_statements..offset_public_statements + 1 + inputs.public_statements.len()] + [offset_public_statements..offset_public_statements + inputs.public_statements.len()] .to_vec(); Ok((statements, public_statements)) } @@ -413,13 +438,7 @@ pub(crate) fn process_public_statements_operations( mut operations: Vec, ) -> Result> { let offset_public_statements = statements.len() - params.max_public_statements; - operations.push(Operation( - OperationType::Native(NativeOperation::NewEntry), - vec![], - OperationAux::None, - )); - for i in 0..(params.max_public_statements - 1) { - let st = &statements[offset_public_statements + i + 1]; + for st in statements.iter().skip(offset_public_statements) { let mut op = if st.is_none() { Operation( OperationType::Native(NativeOperation::None), @@ -442,44 +461,28 @@ pub(crate) fn process_public_statements_operations( pub struct Prover {} -impl PodProver for Prover { - fn prove( - &self, - params: &Params, - vd_set: &VDSet, - inputs: MainPodInputs, - ) -> Result> { - let signed_pods_input: Vec = inputs - .signed_pods - .iter() - .map(|p| { - let p = (*p as &dyn Any) - .downcast_ref::() - .expect("type SignedPod"); - p.clone() - }) - .collect_vec(); - +impl MainPodProver for Prover { + fn prove(&self, params: &Params, inputs: MainPodInputs) -> Result> { // Pad input recursive pods with empty pods if necessary - let empty_pod = if inputs.recursive_pods.len() == params.max_input_recursive_pods { + let empty_pod = if inputs.pods.len() == params.max_input_pods { // We don't need padding so we skip creating an EmptyPod MockEmptyPod::new_boxed(params, inputs.vd_set.clone()) } else { EmptyPod::new_boxed(params, inputs.vd_set.clone()) }; let inputs = MainPodInputs { - recursive_pods: &inputs - .recursive_pods + pods: &inputs + .pods .iter() .copied() .chain(iter::repeat(&*empty_pod)) - .take(params.max_input_recursive_pods) + .take(params.max_input_pods) .collect_vec(), ..inputs }; - let recursive_pods_pub_self_statements = inputs - .recursive_pods + let input_pods_pub_self_statements = inputs + .pods .iter() .map(|pod| { assert_eq!(params.id_params(), pod.params().id_params()); @@ -500,6 +503,8 @@ impl PodProver for Prover { )?; let public_key_of_sks = extract_public_key_of(params, &mut aux_list, inputs.operations, inputs.statements)?; + let signed_bys = + extract_signatures(params, &mut aux_list, inputs.operations, inputs.statements)?; let merkle_tree_state_transition_proofs = extract_merkle_tree_state_transition_proofs(params, &mut aux_list, inputs.operations)?; @@ -514,38 +519,54 @@ impl PodProver for Prover { let operations = process_public_statements_operations(params, &statements, operations)?; // get the id out of the public statements - let id: PodId = PodId(calculate_id(&public_statements, params)); + let sts_hash = calculate_statements_hash(&public_statements, params); let common_hash: String = cache_get_rec_main_pod_common_hash(params).clone(); let proofs = inputs - .recursive_pods + .pods .iter() .map(|pod| { assert_eq!(pod.common_hash(), common_hash); assert_eq!(inputs.vd_set.root(), pod.vd_set().root()); ProofWithPublicInputs { proof: pod.proof(), - public_inputs: [pod.id().0 .0, inputs.vd_set.root().0].concat(), + public_inputs: [pod.statements_hash().0, inputs.vd_set.root().0].concat(), } }) .collect_vec(); let verifier_datas = inputs - .recursive_pods + .pods .iter() .map(|pod| pod.verifier_data()) .collect_vec(); - let vd_mt_proofs = vd_set.get_vds_proofs(&verifier_datas)?; + let mut vd_mt_proofs = Vec::with_capacity(inputs.pods.len()); + for (pod, vd) in inputs.pods.iter().zip(&verifier_datas) { + vd_mt_proofs.push(if pod.is_main() { + (true, inputs.vd_set.get_vds_proof(vd)?) + } else { + // For intro pods we don't verify inclusion of their vk into the vd set, so we + // generate a dummy mt proof with expected root and value to pass some constraints + ( + false, + MerkleClaimAndProof { + root: inputs.vd_set.root(), + value: RawValue::from(pod.verifier_data_hash()), + ..MerkleClaimAndProof::empty() + }, + ) + }); + } let input = MainPodVerifyInput { vds_set: inputs.vd_set.clone(), vd_mt_proofs, - signed_pods: signed_pods_input, - recursive_pods_pub_self_statements, + input_pods_pub_self_statements, statements: statements[statements.len() - params.max_statements..].to_vec(), operations, merkle_proofs, public_key_of_sks, + signed_bys, merkle_tree_state_transition_proofs, custom_predicate_batches, custom_predicate_verifications, @@ -567,7 +588,7 @@ impl PodProver for Prover { params: params.clone(), verifier_only: circuit_data.verifier_only.clone(), common_hash, - id, + sts_hash, vd_set: inputs.vd_set, public_statements, proof: proof_with_pis.proof, @@ -578,7 +599,7 @@ impl PodProver for Prover { #[derive(Clone, Debug, PartialEq, Eq)] pub struct MainPod { params: Params, - id: PodId, + sts_hash: Hash, verifier_only: VerifierOnlyCircuitData, common_hash: String, /// vds_root is the merkle-root of the `VDSet`, which contains the @@ -598,7 +619,7 @@ pub(crate) fn rec_main_pod_circuit_data( timed!( "recursive MainPod circuit_data padded", RecursiveCircuit::::target_and_circuit_data_padded( - params.max_input_recursive_pods, + params.max_input_pods, &rec_common_circuit_data, params, ) @@ -676,6 +697,9 @@ impl Pod for MainPod { fn params(&self) -> &Params { &self.params } + fn is_main(&self) -> bool { + true + } fn verify(&self) -> Result<()> { // 0. Assert that the CommonCircuitData of the pod is the current one let expect_common_hash = &*cache_get_rec_main_pod_common_hash(&self.params); @@ -686,9 +710,9 @@ impl Pod for MainPod { ))); } // 2. get the id out of the public statements - let id = PodId(calculate_id(&self.public_statements, &self.params)); - if id != self.id { - return Err(Error::id_not_equal(self.id, id)); + let sts_hash = calculate_statements_hash(&self.public_statements, &self.params); + if sts_hash != self.sts_hash { + return Err(Error::statements_hash_not_equal(self.sts_hash, sts_hash)); } // 7. verifier_data_hash is in the VDSet @@ -705,7 +729,7 @@ impl Pod for MainPod { // 1, 3, 4, 5 verification via the zkSNARK proof let rec_main_pod_verifier_circuit_data = &*cache_get_rec_main_pod_verifier_circuit_data(&self.params); - let public_inputs = id + let public_inputs = sts_hash .to_fields(&self.params) .iter() .chain(self.vd_set.root().0.iter()) @@ -719,8 +743,8 @@ impl Pod for MainPod { .map_err(|e| Error::plonky2_proof_fail("MainPod", e)) } - fn id(&self) -> PodId { - self.id + fn statements_hash(&self) -> Hash { + self.sts_hash } fn pod_type(&self) -> (usize, &'static str) { (PodType::Main as usize, "Main") @@ -734,18 +758,6 @@ impl Pod for MainPod { .collect() } - fn serialize_data(&self) -> serde_json::Value { - serde_json::to_value(Data { - proof: serialize_proof(&self.proof), - public_statements: self.public_statements.clone(), - verifier_only: serialize_verifier_only(&self.verifier_only), - common_hash: self.common_hash.clone(), - }) - .expect("serialization to json") - } -} - -impl RecursivePod for MainPod { fn verifier_data(&self) -> VerifierOnlyCircuitData { self.verifier_only.clone() } @@ -758,30 +770,42 @@ impl RecursivePod for MainPod { fn vd_set(&self) -> &VDSet { &self.vd_set } + + fn serialize_data(&self) -> serde_json::Value { + serde_json::to_value(Data { + proof: serialize_proof(&self.proof), + public_statements: self.public_statements.clone(), + verifier_only: serialize_verifier_only(&self.verifier_only), + common_hash: self.common_hash.clone(), + }) + .expect("serialization to json") + } fn deserialize_data( params: Params, data: serde_json::Value, vd_set: VDSet, - id: PodId, - ) -> Result> { + id: Hash, + ) -> Result { let data: Data = serde_json::from_value(data)?; let common = cache_get_rec_main_pod_common_circuit_data(¶ms); let proof = deserialize_proof(&common, &data.proof)?; let verifier_only = deserialize_verifier_only(&data.verifier_only)?; - Ok(Box::new(Self { + Ok(Self { params, - id, + sts_hash: id, verifier_only, common_hash: data.common_hash, vd_set, proof, public_statements: data.public_statements, - })) + }) } } #[cfg(test)] pub mod tests { + use std::{any::Any, collections::HashSet}; + use num::{BigUint, One}; use super::*; @@ -789,18 +813,19 @@ pub mod tests { backends::plonky2::{ mock::mainpod::{MockMainPod, MockProver}, primitives::ec::schnorr::SecretKey, - signedpod::Signer, + signer::Signer, }, + dict, examples::{ - attest_eth_friend, tickets_pod_full_flow, zu_kyc_pod_builder, zu_kyc_sign_pod_builders, - EthDosHelper, + attest_eth_friend, tickets_pod_full_flow, zu_kyc_pod_builder, + zu_kyc_sign_dict_builders, EthDosHelper, }, frontend::{ self, literal, CustomPredicateBatchBuilder, MainPodBuilder, StatementTmplBuilder as STB, }, middleware::{ - self, containers::Set, CustomPredicateRef, NativePredicate as NP, DEFAULT_VD_LIST, - DEFAULT_VD_SET, + self, containers::Set, CustomPredicateRef, NativePredicate as NP, Signer as _, + DEFAULT_VD_LIST, DEFAULT_VD_SET, }, }; @@ -809,7 +834,7 @@ pub mod tests { let params = middleware::Params { // Currently the circuit uses random access that only supports vectors of length 64. // With max_input_main_pods=3 we need random access to a vector of length 73. - max_input_recursive_pods: 0, + max_input_pods: 0, max_custom_predicate_batches: 0, max_custom_predicate_verifications: 0, ..Default::default() @@ -819,7 +844,7 @@ pub mod tests { vds.push(rec_main_pod_circuit_data(¶ms).1.verifier_only.clone()); let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap(); - let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(¶ms); + let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_dict_builders(¶ms); let signer = Signer(SecretKey(BigUint::one())); let gov_id_pod = gov_id_builder.sign(&signer)?; let signer = Signer(SecretKey(2u64.into())); @@ -850,9 +875,8 @@ pub mod tests { #[test] fn test_mini_0() { let params = middleware::Params { - max_input_signed_pods: 1, - max_input_recursive_pods: 1, - max_signed_pod_values: 6, + max_signed_by: 1, + max_input_pods: 1, max_statements: 8, max_public_statements: 4, max_input_pods_public_statements: 10, @@ -862,7 +886,7 @@ pub mod tests { vds.push(rec_main_pod_circuit_data(¶ms).1.verifier_only.clone()); let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap(); - let mut gov_id_builder = frontend::SignedPodBuilder::new(¶ms); + let mut gov_id_builder = frontend::SignedDictBuilder::new(¶ms); gov_id_builder.insert("idNumber", "4242424242"); gov_id_builder.insert("dateOfBirth", 1169909384); gov_id_builder.insert("socialSecurityNumber", "G2121210"); @@ -870,7 +894,10 @@ pub mod tests { let gov_id = gov_id_builder.sign(&signer).unwrap(); let now_minus_18y: i64 = 1169909388; let mut kyc_builder = frontend::MainPodBuilder::new(¶ms, &vd_set); - kyc_builder.add_signed_pod(&gov_id); + + kyc_builder + .priv_op(frontend::Operation::dict_signed_by(&gov_id)) + .unwrap(); kyc_builder .pub_op(frontend::Operation::lt( (&gov_id, "dateOfBirth"), @@ -902,9 +929,8 @@ pub mod tests { #[test] fn test_mini_1() { let params = middleware::Params { - max_input_signed_pods: 0, - max_input_recursive_pods: 0, - max_signed_pod_values: 0, + max_signed_by: 0, + max_input_pods: 0, max_statements: 2, max_public_statements: 1, max_input_pods_public_statements: 0, @@ -939,13 +965,12 @@ pub mod tests { #[test] fn test_mainpod_small_empty() { let params = middleware::Params { - max_input_signed_pods: 0, - max_input_recursive_pods: 0, + max_signed_by: 0, + max_input_pods: 0, max_input_pods_public_statements: 2, max_statements: 5, - max_signed_pod_values: 2, max_public_statements: 2, - num_public_statements_id: 4, + num_public_statements_hash: 4, max_statement_args: 4, max_operation_args: 4, max_custom_predicate_batches: 2, @@ -996,7 +1021,7 @@ pub mod tests { let alice_attestation = attest_eth_friend(¶ms, &alice, bob.public_key()); let bob_attestation = attest_eth_friend(¶ms, &bob, charlie.public_key()); - let helper = EthDosHelper::new(¶ms, vd_set, false, alice.public_key())?; + let helper = EthDosHelper::new(¶ms, vd_set, alice.public_key())?; let prover = Prover {}; let dist_1 = helper.dist_1(&alice_attestation)?.prove(&prover)?; crate::measure_gates_print!(); @@ -1010,8 +1035,8 @@ pub mod tests { #[test] fn test_main_mini_custom_1() -> frontend::Result<()> { let params = Params { - max_input_signed_pods: 0, - max_input_recursive_pods: 0, + max_signed_by: 0, + max_input_pods: 0, max_statements: 9, max_public_statements: 4, max_statement_args: 4, @@ -1020,7 +1045,7 @@ pub mod tests { max_custom_batch_size: 3, max_custom_predicate_wildcards: 4, max_custom_predicate_verifications: 2, - max_merkle_proofs_containers: 0, + max_merkle_proofs_containers: 3, max_merkle_tree_state_transition_proofs_containers: 0, ..Default::default() }; @@ -1030,17 +1055,20 @@ pub mod tests { let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap(); let mut cpb_builder = CustomPredicateBatchBuilder::new(params.clone(), "cpb".into()); - let stb0 = STB::new(NP::Equal).arg(("id", "score")).arg(literal(42)); + let stb0 = STB::new(NP::Contains) + .arg("dict") + .arg(literal("score")) + .arg(literal(42)); let stb1 = STB::new(NP::Equal) - .arg(("secret_id", "key")) - .arg(("id", "score")); + .arg(("secret_dict", "key")) + .arg(("dict", "score")); let _ = cpb_builder.predicate_and( "pred_and", - &["id"], - &["secret_id"], + &["dict"], + &["secret_dict"], &[stb0.clone(), stb1.clone()], )?; - let _ = cpb_builder.predicate_or("pred_or", &["id"], &["secret_id"], &[stb0, stb1])?; + let _ = cpb_builder.predicate_or("pred_or", &["dict"], &["secret_dict"], &[stb0, stb1])?; let cpb = cpb_builder.finish(); let cpb_and = CustomPredicateRef::new(cpb.clone(), 0); @@ -1048,9 +1076,17 @@ pub mod tests { let mut pod_builder = MainPodBuilder::new(¶ms, &vd_set); - let st0 = pod_builder.priv_op(frontend::Operation::new_entry("score", 42))?; - let st1 = pod_builder.priv_op(frontend::Operation::new_entry("key", 42))?; - let st2 = pod_builder.priv_op(frontend::Operation::eq(st1.clone(), st0.clone()))?; + let dict = dict!(32, {"score" => 42})?; + let secret_dict = dict!(32, {"key" => 42})?; + let st0 = pod_builder.priv_op(frontend::Operation::dict_contains( + dict.clone(), + "score", + 42, + ))?; + let st2 = pod_builder.priv_op(frontend::Operation::eq( + (&secret_dict.clone(), "key"), + (&dict, "score"), + ))?; let _st3 = pod_builder.priv_op(frontend::Operation::custom(cpb_and.clone(), [st0, st2]))?; @@ -1071,15 +1107,9 @@ pub mod tests { fn test_set_contains() -> frontend::Result<()> { let params = Params::default(); let mut builder = MainPodBuilder::new(¶ms, &DEFAULT_VD_SET); - let set = [1, 2, 3].into_iter().map(|n| n.into()).collect(); - let st = builder - .pub_op(frontend::Operation::new_entry( - "entry", - Set::new(params.max_depth_mt_containers, set).unwrap(), - )) - .unwrap(); - - builder.pub_op(frontend::Operation::set_contains(st, 1))?; + let set: HashSet<_> = [1, 2, 3].into_iter().map(|n| n.into()).collect(); + let set = Set::new(params.max_depth_mt_containers, set).unwrap(); + builder.pub_op(frontend::Operation::set_contains(set, 1))?; let prover = Prover {}; let proof = builder.prove(&prover).unwrap(); diff --git a/src/backends/plonky2/mainpod/operation.rs b/src/backends/plonky2/mainpod/operation.rs index b8d32c7..d7b44bb 100644 --- a/src/backends/plonky2/mainpod/operation.rs +++ b/src/backends/plonky2/mainpod/operation.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use crate::{ backends::plonky2::{ error::{Error, Result}, - mainpod::Statement, + mainpod::{SignedBy, Statement}, primitives::merkletree::{MerkleClaimAndProof, MerkleTreeStateTransitionProof}, }, middleware::{self, OperationType, Params}, @@ -35,6 +35,7 @@ pub enum OperationAux { None, MerkleProofIndex(usize), PublicKeyOfIndex(usize), + SignedByIndex(usize), MerkleTreeStateTransitionProofIndex(usize), CustomPredVerifyIndex(usize), } @@ -47,9 +48,12 @@ impl OperationAux { fn table_offset_public_key_of(params: &Params) -> usize { Self::table_offset_merkle_proof(params) + params.max_merkle_proofs_containers } - fn table_offset_merkle_tree_state_transition_proof(params: &Params) -> usize { + fn table_offset_signed_by(params: &Params) -> usize { Self::table_offset_public_key_of(params) + params.max_public_key_of } + fn table_offset_merkle_tree_state_transition_proof(params: &Params) -> usize { + Self::table_offset_signed_by(params) + params.max_signed_by + } fn table_offset_custom_pred_verify(params: &Params) -> usize { Self::table_offset_merkle_tree_state_transition_proof(params) + params.max_merkle_tree_state_transition_proofs_containers @@ -57,6 +61,7 @@ impl OperationAux { pub(crate) fn table_size(params: &Params) -> usize { 1 + params.max_merkle_proofs_containers + params.max_public_key_of + + params.max_signed_by + params.max_merkle_tree_state_transition_proofs_containers + params.max_custom_predicate_verifications } @@ -65,6 +70,7 @@ impl OperationAux { Self::None => 0, Self::MerkleProofIndex(i) => Self::table_offset_merkle_proof(params) + *i, Self::PublicKeyOfIndex(i) => Self::table_offset_public_key_of(params) + *i, + Self::SignedByIndex(i) => Self::table_offset_signed_by(params) + *i, Self::MerkleTreeStateTransitionProofIndex(i) => { Self::table_offset_merkle_tree_state_transition_proof(params) + *i } @@ -89,6 +95,7 @@ impl Operation { pub fn deref( &self, statements: &[Statement], + signatures: &[SignedBy], merkle_proofs: &[MerkleClaimAndProof], merkle_tree_state_transition_proofs: &[MerkleTreeStateTransitionProof], ) -> Result { @@ -125,6 +132,13 @@ impl Operation { .clone(), ) } + OperationAux::SignedByIndex(i) => crate::middleware::OperationAux::Signature( + signatures + .get(i) + .ok_or(Error::custom(format!("Missing SignedBy data index {}", i)))? + .sig + .clone(), + ), OperationAux::PublicKeyOfIndex(_) => crate::middleware::OperationAux::None, }; Ok(middleware::Operation::op( @@ -154,6 +168,7 @@ impl fmt::Display for Operation { OperationAux::MerkleProofIndex(i) => write!(f, " merkle_proof_{:02}", i)?, OperationAux::CustomPredVerifyIndex(i) => write!(f, " custom_pred_verify_{:02}", i)?, OperationAux::PublicKeyOfIndex(i) => write!(f, " public_key_of_{:02}", i)?, + OperationAux::SignedByIndex(i) => write!(f, " signed_by_{:02}", i)?, OperationAux::MerkleTreeStateTransitionProofIndex(i) => { write!(f, " merkle_tree_state_transition_proof_{:02}", i)? } diff --git a/src/backends/plonky2/mainpod/statement.rs b/src/backends/plonky2/mainpod/statement.rs index 82826ba..3fc4ed0 100644 --- a/src/backends/plonky2/mainpod/statement.rs +++ b/src/backends/plonky2/mainpod/statement.rs @@ -74,6 +74,7 @@ impl TryFrom for middleware::Statement { S::HashOf(a1.try_into()?, a2.try_into()?, a3.try_into()?) } (NP::PublicKeyOf, &[a1, a2]) => S::PublicKeyOf(a1.try_into()?, a2.try_into()?), + (NP::SignedBy, &[a1, a2]) => S::SignedBy(a1.try_into()?, a2.try_into()?), (NP::ContainerInsert, &[a1, a2, a3, a4]) => S::ContainerInsert( a1.try_into()?, a2.try_into()?, @@ -105,6 +106,17 @@ impl TryFrom for middleware::Statement { .collect(); S::Custom(cpr, vs) } + Predicate::Intro(ir) => { + let vs: Vec = proper_args + .into_iter() + .filter_map(|arg| match arg { + SA::None => None, + SA::Literal(v) => Some(v), + _ => unreachable!(), + }) + .collect(); + S::Intro(ir, vs) + } Predicate::BatchSelf(_) => { unreachable!() } @@ -123,6 +135,10 @@ impl From for Statement { middleware::Predicate::Custom(cpr), s.args().into_iter().collect(), ), + middleware::Predicate::Intro(ir) => Statement( + middleware::Predicate::Intro(ir), + s.args().into_iter().collect(), + ), middleware::Predicate::BatchSelf(_) => unreachable!(), } } diff --git a/src/backends/plonky2/mock/emptypod.rs b/src/backends/plonky2/mock/emptypod.rs index fcdd8ac..1e5019d 100644 --- a/src/backends/plonky2/mock/emptypod.rs +++ b/src/backends/plonky2/mock/emptypod.rs @@ -4,35 +4,36 @@ use crate::{ backends::plonky2::{ basetypes::{Proof, VerifierOnlyCircuitData}, error::{Error, Result}, - mainpod::{self, calculate_id}, - }, - middleware::{ - AnchoredKey, Params, Pod, PodId, PodType, RecursivePod, Statement, VDSet, Value, KEY_TYPE, - SELF, + mainpod::{self, calculate_statements_hash}, }, + middleware::{Hash, IntroPredicateRef, Params, Pod, PodType, Statement, VDSet, EMPTY_HASH}, }; #[derive(Clone, Debug, PartialEq, Eq)] pub struct MockEmptyPod { params: Params, - id: PodId, + sts_hash: Hash, vd_set: VDSet, } -fn type_statement() -> Statement { - Statement::equal( - AnchoredKey::from((SELF, KEY_TYPE)), - Value::from(PodType::Empty), +fn empty_statement() -> Statement { + Statement::Intro( + IntroPredicateRef { + name: "mock_empty".to_string(), + args_len: 0, + verifier_data_hash: EMPTY_HASH, + }, + vec![], ) } impl MockEmptyPod { - pub fn new_boxed(params: &Params, vd_set: VDSet) -> Box { - let statements = [mainpod::Statement::from(type_statement())]; - let id = PodId(calculate_id(&statements, params)); + pub fn new_boxed(params: &Params, vd_set: VDSet) -> Box { + let statements = [mainpod::Statement::from(empty_statement())]; + let sts_hash = calculate_statements_hash(&statements, params); Box::new(Self { params: params.clone(), - id, + sts_hash, vd_set, }) } @@ -48,28 +49,25 @@ impl Pod for MockEmptyPod { .into_iter() .map(mainpod::Statement::from) .collect_vec(); - let id = PodId(calculate_id(&statements, &self.params)); - if id != self.id { - return Err(Error::id_not_equal(self.id, id)); + let sts_hash = calculate_statements_hash(&statements, &self.params); + if sts_hash != self.sts_hash { + return Err(Error::statements_hash_not_equal(self.sts_hash, sts_hash)); } Ok(()) } - fn id(&self) -> PodId { - self.id + fn statements_hash(&self) -> Hash { + self.sts_hash } fn pod_type(&self) -> (usize, &'static str) { (PodType::MockEmpty as usize, "MockEmpty") } fn pub_self_statements(&self) -> Vec { - vec![type_statement()] + vec![empty_statement()] } - fn serialize_data(&self) -> serde_json::Value { - serde_json::Value::Null + fn verifier_data_hash(&self) -> Hash { + EMPTY_HASH } -} - -impl RecursivePod for MockEmptyPod { fn verifier_data(&self) -> VerifierOnlyCircuitData { panic!("MockEmptyPod can't be verified in a recursive MainPod circuit"); } @@ -82,13 +80,20 @@ impl RecursivePod for MockEmptyPod { fn vd_set(&self) -> &VDSet { &self.vd_set } + fn serialize_data(&self) -> serde_json::Value { + serde_json::Value::Null + } fn deserialize_data( params: Params, _data: serde_json::Value, vd_set: VDSet, - id: PodId, - ) -> Result> { - Ok(Box::new(Self { params, id, vd_set })) + id: Hash, + ) -> Result { + Ok(Self { + params, + sts_hash: id, + vd_set, + }) } } diff --git a/src/backends/plonky2/mock/mainpod.rs b/src/backends/plonky2/mock/mainpod.rs index 2025f9e..cd9361c 100644 --- a/src/backends/plonky2/mock/mainpod.rs +++ b/src/backends/plonky2/mock/mainpod.rs @@ -4,7 +4,6 @@ use std::{fmt, iter}; -use itertools::Itertools; use serde::{Deserialize, Serialize}; use crate::{ @@ -12,31 +11,25 @@ use crate::{ basetypes::{Proof, VerifierOnlyCircuitData}, error::{Error, Result}, mainpod::{ - calculate_id, extract_merkle_proofs, extract_merkle_tree_state_transition_proofs, - layout_statements, process_private_statements_operations, - process_public_statements_operations, Operation, OperationAux, Statement, + calculate_statements_hash, extract_merkle_proofs, + extract_merkle_tree_state_transition_proofs, extract_signatures, layout_statements, + process_private_statements_operations, process_public_statements_operations, Operation, + OperationAux, SignedBy, Statement, }, mock::emptypod::MockEmptyPod, primitives::merkletree::{MerkleClaimAndProof, MerkleTreeStateTransitionProof}, recursion::hash_verifier_data, - signedpod::SignedPod, }, middleware::{ - self, deserialize_pod, deserialize_signed_pod, hash_str, AnchoredKey, Hash, MainPodInputs, - NativeOperation, NativePredicate, OperationType, Params, Pod, PodId, PodProver, PodType, - Predicate, RecursivePod, StatementArg, VDSet, Value, KEY_TYPE, SELF, + self, deserialize_pod, Hash, MainPodInputs, MainPodProver, Params, Pod, PodType, VDSet, + EMPTY_HASH, }, }; pub struct MockProver {} -impl PodProver for MockProver { - fn prove( - &self, - params: &Params, - _vd_set: &VDSet, - inputs: MainPodInputs, - ) -> Result> { +impl MainPodProver for MockProver { + fn prove(&self, params: &Params, inputs: MainPodInputs) -> Result> { Ok(Box::new(MockMainPod::new(params, inputs)?)) } } @@ -44,10 +37,9 @@ impl PodProver for MockProver { #[derive(Clone, Debug, PartialEq)] pub struct MockMainPod { params: Params, - id: PodId, + sts_hash: Hash, vd_set: VDSet, - input_signed_pods: Vec>, - input_recursive_pods: Vec>, + input_pods: Vec>, // All statements (inherited + newly introduced by this pod) statements: Vec, operations: Vec, @@ -57,42 +49,28 @@ pub struct MockMainPod { merkle_proofs_containers: Vec, // All Merkle tree state transition proofs merkle_tree_state_transition_proofs_containers: Vec, + // All verified signatures + signatures: Vec, } impl Eq for MockMainPod {} impl fmt::Display for MockMainPod { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - writeln!(f, "MockMainPod ({}):", self.id)?; - let offset_input_signed_pods = Self::offset_input_signed_pods(); - let offset_input_recursive_pods = self.offset_input_recursive_pods(); + writeln!(f, "MockMainPod ({}):", self.sts_hash)?; + let offset_input_pods = self.offset_input_pods(); let offset_input_statements = self.offset_input_statements(); let offset_public_statements = self.offset_public_statements(); for (i, st) in self.statements.iter().enumerate() { - if self.params.max_input_signed_pods > 0 - && (i >= offset_input_signed_pods && i < offset_input_recursive_pods) - && (i - offset_input_signed_pods).is_multiple_of(self.params.max_signed_pod_values) - { - let index = (i - offset_input_signed_pods) / self.params.max_signed_pod_values; - let pod = &self.input_signed_pods[index]; - let id = pod.id(); - let pod_type = pod.pod_type(); - writeln!( - f, - " from input SignedPod {} (id={}, type={:?}):", - index, id, pod_type - )?; - } - if self.params.max_input_recursive_pods > 0 - && (i >= offset_input_recursive_pods) + if self.params.max_input_pods > 0 + && (i >= offset_input_pods) && (i < offset_input_statements) - && (i - offset_input_recursive_pods) + && (i - offset_input_pods) .is_multiple_of(self.params.max_input_pods_public_statements) { - let index = (i - offset_input_recursive_pods) - / self.params.max_input_pods_public_statements; - let pod = &self.input_recursive_pods[index]; - let id = pod.id(); + let index = (i - offset_input_pods) / self.params.max_input_pods_public_statements; + let pod = &self.input_pods[index]; + let id = pod.statements_hash(); let pod_type = pod.pod_type(); writeln!( f, @@ -148,26 +126,21 @@ struct Data { statements: Vec, merkle_proofs: Vec, merkle_tree_state_transition_proofs: Vec, - input_signed_pods: Vec<(usize, PodId, serde_json::Value)>, - input_recursive_pods: Vec<(usize, Params, PodId, VDSet, serde_json::Value)>, + signatures: Vec, + input_pods: Vec<(usize, Params, Hash, VDSet, serde_json::Value)>, } /// Inputs are sorted as: -/// - SignedPods -/// - MainPods +/// - Pods /// - private Statements /// - public Statements impl MockMainPod { - fn offset_input_signed_pods() -> usize { + fn offset_input_pods(&self) -> usize { 1 } - fn offset_input_recursive_pods(&self) -> usize { - Self::offset_input_signed_pods() - + self.params.max_input_signed_pods * self.params.max_signed_pod_values - } fn offset_input_statements(&self) -> usize { - self.offset_input_recursive_pods() - + self.params.max_input_recursive_pods * self.params.max_input_pods_public_statements + self.offset_input_pods() + + self.params.max_input_pods * self.params.max_input_pods_public_statements } fn offset_public_statements(&self) -> usize { self.offset_input_statements() + self.params.max_priv_statements() @@ -175,6 +148,7 @@ impl MockMainPod { pub fn new(params: &Params, inputs: MainPodInputs) -> Result { let (statements, public_statements) = layout_statements(params, true, &inputs)?; + dbg!(public_statements.len()); let mut aux_list = vec![OperationAux::None; params.max_priv_statements()]; // Extract Merkle proofs and pad. let merkle_proofs = @@ -182,6 +156,8 @@ impl MockMainPod { // Similarly for Merkle state transition proofs. let merkle_tree_state_transition_proofs = extract_merkle_tree_state_transition_proofs(params, &mut aux_list, inputs.operations)?; + let signatures = + extract_signatures(params, &mut aux_list, inputs.operations, inputs.statements)?; let operations = process_private_statements_operations( params, @@ -192,35 +168,27 @@ impl MockMainPod { let operations = process_public_statements_operations(params, &statements, operations)?; // get the id out of the public statements - let id: PodId = PodId(calculate_id(&public_statements, params)); + let sts_hash = calculate_statements_hash(&public_statements, params); - let pad_signed_pod: Box = Box::new(SignedPod::dummy()); - let input_signed_pods: Vec> = inputs - .signed_pods - .iter() - .map(|p| dyn_clone::clone_box(*p)) - .chain(iter::repeat_with(|| pad_signed_pod.clone())) - .take(params.max_input_signed_pods) - .collect(); let pad_pod = MockEmptyPod::new_boxed(params, inputs.vd_set.clone()); - let input_recursive_pods: Vec> = inputs - .recursive_pods + let input_pods: Vec> = inputs + .pods .iter() .map(|p| dyn_clone::clone_box(*p)) .chain(iter::repeat_with(|| pad_pod.clone())) - .take(params.max_input_recursive_pods) + .take(params.max_input_pods) .collect(); Ok(Self { params: params.clone(), - id, + sts_hash, vd_set: inputs.vd_set, - input_signed_pods, - input_recursive_pods, + input_pods, public_statements, statements, operations, merkle_proofs_containers: merkle_proofs, merkle_tree_state_transition_proofs_containers: merkle_tree_state_transition_proofs, + signatures, }) } @@ -233,13 +201,15 @@ impl Pod for MockMainPod { fn params(&self) -> &Params { &self.params } + fn is_mock(&self) -> bool { + true + } + fn is_main(&self) -> bool { + true + } fn verify(&self) -> Result<()> { - // 1. Verify input pods - for pod in &self.input_signed_pods { - pod.verify()?; - } - for pod in &self.input_recursive_pods { + for pod in &self.input_pods { pod.verify()?; if pod.vd_set().root() != self.vd_set.root() { return Err(Error::custom(format!( @@ -248,67 +218,38 @@ impl Pod for MockMainPod { self.vd_set.root(), ))); } - let (pod_type, _) = pod.pod_type(); - // If the pod is not mock, check that its verifier data is in the set - if pod_type != PodType::MockMain as usize && pod_type != PodType::MockEmpty as usize { + // If the pod is not mock and main (MainPod family) check that its verifier data is in + // the set + if !pod.is_mock() && pod.is_main() { let verifier_data = pod.verifier_data(); let verifier_data_hash = hash_verifier_data(&verifier_data); if !self.vd_set.contains(verifier_data_hash) { return Err(Error::custom(format!( - "vds_root in input recursive pod not in the set: {} not in {}", + "vds_root in input recursive MainPod not in the set: {} not in {}", Hash(verifier_data_hash.elements), self.vd_set.root(), ))); } } + // Introduction pods can only have Introduction or None statements + if !pod.is_main() { + for self_st in pod.pub_self_statements() { + match self_st { + middleware::Statement::None | middleware::Statement::Intro(_, _) => {} + _ => { + return Err(Error::custom(format!( + "Introduction Pod has a non-introduction statement: {}", + self_st, + ))) + } + } + } + } } let input_statement_offset = self.offset_input_statements(); // get the input_statements from the self.statements let input_statements = &self.statements[input_statement_offset..]; - // 2. get the id out of the public statements, and ensure it is equal to self.id - if self.id != PodId(calculate_id(&self.public_statements, &self.params)) { - return Err(Error::pod_id_invalid()); - } - // 4. Verify type - // find a ValueOf statement from the public statements with key=KEY_TYPE and check that the - // value is PodType::MockMainPod - let type_statement = &self.public_statements[0]; - let type_statement_ok = type_statement.0 == Predicate::Native(NativePredicate::Equal) - && { - if let [StatementArg::Key(AnchoredKey { pod_id, ref key }), StatementArg::Literal(pod_type)] = - &type_statement.1[..2] - { - pod_id == &SELF - && key.hash() == hash_str(KEY_TYPE) - && *pod_type == Value::from(PodType::MockMain) - } else { - false - } - }; - if !type_statement_ok { - return Err(Error::not_type_statement()); - } - // 3. check that all `NewEntry` operations have unique keys - // (no duplicates) - let value_ofs_unique = input_statements - .iter() - .zip(self.operations.iter()) - .filter_map(|(s, o)| { - if matches!(o.0, OperationType::Native(NativeOperation::NewEntry)) { - match s.1.get(0) { - Some(StatementArg::Key(k)) => Some(k), - // malformed NewEntry operations are caught in step 5 - _ => None, - } - } else { - None - } - }) - .all_unique(); - if !value_ofs_unique { - return Err(Error::repeated_value_of()); - } // 5. verify that all `input_statements` are correctly generated // by `self.operations` (where each operation can only access previous statements) @@ -319,6 +260,7 @@ impl Pod for MockMainPod { self.operations[i] .deref( &self.statements[..input_statement_offset + i], + &self.signatures, &self.merkle_proofs_containers, &self.merkle_tree_state_transition_proofs_containers, )? @@ -332,8 +274,8 @@ impl Pod for MockMainPod { Ok(()) } - fn id(&self) -> PodId { - self.id + fn statements_hash(&self) -> Hash { + self.sts_hash } fn pod_type(&self) -> (usize, &'static str) { (PodType::MockMain as usize, "MockMain") @@ -346,20 +288,31 @@ impl Pod for MockMainPod { .collect() } + fn verifier_data_hash(&self) -> Hash { + EMPTY_HASH + } + fn verifier_data(&self) -> VerifierOnlyCircuitData { + panic!("MockMainPod can't be verified in a recursive MainPod circuit"); + } + fn common_hash(&self) -> String { + panic!("MockMainPod can't be verified in a recursive MainPod circuit"); + } + fn proof(&self) -> Proof { + panic!("MockMainPod can't be verified in a recursive MainPod circuit"); + } + fn vd_set(&self) -> &VDSet { + &self.vd_set + } + fn serialize_data(&self) -> serde_json::Value { - let input_signed_pods = self - .input_signed_pods - .iter() - .map(|p| (p.pod_type().0, p.id(), p.serialize_data())) - .collect(); - let input_recursive_pods = self - .input_recursive_pods + let input_pods = self + .input_pods .iter() .map(|p| { ( p.pod_type().0, p.params().clone(), - p.id(), + p.statements_hash(), p.vd_set().clone(), p.serialize_data(), ) @@ -373,26 +326,11 @@ impl Pod for MockMainPod { merkle_tree_state_transition_proofs: self .merkle_tree_state_transition_proofs_containers .clone(), - input_signed_pods, - input_recursive_pods, + signatures: self.signatures.clone(), + input_pods, }) .expect("serialization to json") } -} - -impl RecursivePod for MockMainPod { - fn verifier_data(&self) -> VerifierOnlyCircuitData { - panic!("MockMainPod can't be verified in a recursive MainPod circuit"); - } - fn common_hash(&self) -> String { - panic!("MockMainPod can't be verified in a recursive MainPod circuit"); - } - fn proof(&self) -> Proof { - panic!("MockMainPod can't be verified in a recursive MainPod circuit"); - } - fn vd_set(&self) -> &VDSet { - &self.vd_set - } // MockMainPods include some internal private state which is necessary // for verification. In non-mock Pods, this state will not be necessary, // as the public statements can be verified using a ZK proof. @@ -400,39 +338,35 @@ impl RecursivePod for MockMainPod { params: Params, data: serde_json::Value, vd_set: VDSet, - id: PodId, - ) -> Result> { + id: Hash, + ) -> Result { let Data { public_statements, operations, statements, merkle_proofs, merkle_tree_state_transition_proofs, - input_signed_pods, - input_recursive_pods, + signatures, + input_pods, } = serde_json::from_value(data)?; - let input_signed_pods = input_signed_pods - .into_iter() - .map(|(pod_type, id, data)| deserialize_signed_pod(pod_type, id, data)) - .collect::>>()?; - let input_recursive_pods = input_recursive_pods + let input_pods = input_pods .into_iter() .map(|(pod_type, params, id, vd_set, data)| { deserialize_pod(pod_type, params, id, vd_set, data) }) .collect::>>()?; - Ok(Box::new(Self { + Ok(Self { params, - id, + sts_hash: id, vd_set, - input_signed_pods, - input_recursive_pods, + input_pods, public_statements, operations, statements, merkle_proofs_containers: merkle_proofs, merkle_tree_state_transition_proofs_containers: merkle_tree_state_transition_proofs, - })) + signatures, + }) } } @@ -442,23 +376,24 @@ pub mod tests { use super::*; use crate::{ - backends::plonky2::{primitives::ec::schnorr::SecretKey, signedpod::Signer}, + backends::plonky2::{primitives::ec::schnorr::SecretKey, signer::Signer}, examples::{ great_boy_pod_full_flow, tickets_pod_full_flow, zu_kyc_pod_builder, zu_kyc_pod_request, - zu_kyc_sign_pod_builders, MOCK_VD_SET, + zu_kyc_sign_dict_builders, MOCK_VD_SET, }, frontend, middleware, + middleware::{Signer as _, Value}, }; #[test] fn test_mock_main_zu_kyc() -> frontend::Result<()> { let params = middleware::Params::default(); let vd_set = &*MOCK_VD_SET; - let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(¶ms); - let signer = Signer(SecretKey(1u32.into())); - let gov_id_pod = gov_id_builder.sign(&signer)?; - let signer = Signer(SecretKey(2u32.into())); - let pay_stub_pod = pay_stub_builder.sign(&signer)?; + let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_dict_builders(¶ms); + let gov_id_signer = Signer(SecretKey(1u32.into())); + let gov_id_pod = gov_id_builder.sign(&gov_id_signer)?; + let pay_stub_signer = Signer(SecretKey(2u32.into())); + let pay_stub_pod = pay_stub_builder.sign(&pay_stub_signer)?; let kyc_builder = zu_kyc_pod_builder(¶ms, vd_set, &gov_id_pod, &pay_stub_pod)?; let prover = MockProver {}; @@ -472,8 +407,8 @@ pub mod tests { pod.verify()?; let request = zu_kyc_pod_request( - gov_id_pod.get("_signer").unwrap(), - pay_stub_pod.get("_signer").unwrap(), + &Value::from(gov_id_signer.public_key()), + &Value::from(pay_stub_signer.public_key()), )?; assert!(request.exact_match_pod(&*pod).is_ok()); diff --git a/src/backends/plonky2/mod.rs b/src/backends/plonky2/mod.rs index ece9083..fef391d 100644 --- a/src/backends/plonky2/mod.rs +++ b/src/backends/plonky2/mod.rs @@ -7,7 +7,7 @@ pub mod mock; pub mod primitives; pub mod recursion; pub mod serialization; -pub mod signedpod; +pub mod signer; use std::iter; @@ -49,7 +49,7 @@ pub fn cache_get_standard_rec_main_pod_common_circuit_data( let circuit_data = timed!( "recursive MainPod circuit_data", RecursiveCircuit::::target_and_circuit_data( - params.max_input_recursive_pods, + params.max_input_pods, NUM_PUBLIC_INPUTS, params ) diff --git a/src/backends/plonky2/primitives/ec/curve.rs b/src/backends/plonky2/primitives/ec/curve.rs index d9e7ab5..907b2a2 100644 --- a/src/backends/plonky2/primitives/ec/curve.rs +++ b/src/backends/plonky2/primitives/ec/curve.rs @@ -24,6 +24,7 @@ use plonky2::{ util::serialization::{Read, Write}, }; use rand::rngs::OsRng; +use schemars::JsonSchema; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::backends::plonky2::{ @@ -102,6 +103,16 @@ pub struct Point { pub u: ECField, } +impl JsonSchema for Point { + fn schema_name() -> String { + "Point".to_string() + } + + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + ::json_schema(gen) + } +} + impl fmt::Display for Point { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[allow(clippy::collapsible_else_if)] diff --git a/src/backends/plonky2/primitives/ec/schnorr.rs b/src/backends/plonky2/primitives/ec/schnorr.rs index 6de3bff..df3336e 100644 --- a/src/backends/plonky2/primitives/ec/schnorr.rs +++ b/src/backends/plonky2/primitives/ec/schnorr.rs @@ -20,6 +20,7 @@ use plonky2::{ plonk::circuit_builder::CircuitBuilder, }; use rand::rngs::OsRng; +use schemars::JsonSchema; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use super::curve::Point; @@ -43,6 +44,16 @@ pub struct Signature { pub e: BigUint, } +impl JsonSchema for Signature { + fn schema_name() -> String { + "Signature".to_string() + } + + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + ::json_schema(gen) + } +} + impl Signature { pub fn verify(&self, public_key: Point, msg: RawValue) -> bool { let r = &self.s * Point::generator() + &self.e * public_key; diff --git a/src/backends/plonky2/signedpod.rs b/src/backends/plonky2/signedpod.rs deleted file mode 100644 index 8d890e0..0000000 --- a/src/backends/plonky2/signedpod.rs +++ /dev/null @@ -1,246 +0,0 @@ -use std::{collections::HashMap, sync::LazyLock}; - -use itertools::Itertools; -use num_bigint::{BigUint, RandBigInt}; -use rand::rngs::OsRng; -use serde::{Deserialize, Serialize}; - -use crate::{ - backends::plonky2::{ - error::{Error, Result}, - primitives::{ - ec::{ - curve::{Point, GROUP_ORDER}, - schnorr::{SecretKey, Signature}, - }, - merkletree::MerkleTree, - }, - }, - middleware::{ - containers::Dictionary, AnchoredKey, Hash, Key, Params, Pod, PodId, PodSigner, PodType, - RawValue, Statement, Value, KEY_SIGNER, KEY_TYPE, SELF, - }, - timed, -}; - -pub struct Signer(pub SecretKey); - -impl Signer { - fn sign_with_nonce( - &self, - params: &Params, - nonce: BigUint, - kvs: &HashMap, - ) -> Result { - let mut kvs = kvs.clone(); - let pubkey = self.0.public_key(); - kvs.insert(Key::from(KEY_SIGNER), Value::from(pubkey)); - kvs.insert(Key::from(KEY_TYPE), Value::from(PodType::Signed)); - - let dict = Dictionary::new(params.max_depth_mt_containers, kvs)?; - let id = RawValue::from(dict.commitment()); // PodId as Value - - let signature: Signature = timed!("SignedPod::sign", self.0.sign(id, &nonce)); - Ok(SignedPod { - id: PodId(Hash::from(id)), - signature, - signer: pubkey, - dict, - }) - } - fn _sign(&self, params: &Params, kvs: &HashMap) -> Result { - let nonce = OsRng.gen_biguint_below(&GROUP_ORDER); - self.sign_with_nonce(params, nonce, kvs) - } - - pub fn public_key(&self) -> Value { - Value::from(self.0.public_key()) - } -} - -impl PodSigner for Signer { - fn sign(&self, params: &Params, kvs: &HashMap) -> Result> { - Ok(self._sign(params, kvs).map(Box::new)?) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct SignedPod { - pub id: PodId, - pub signature: Signature, - pub signer: Point, - pub dict: Dictionary, -} - -#[derive(Serialize, Deserialize)] -struct Data { - signer: Point, - signature: Signature, - kvs: Dictionary, -} - -static DUMMY_POD: LazyLock = LazyLock::new(dummy); - -fn dummy() -> SignedPod { - let nonce = BigUint::from(2u32); - Signer(SecretKey(BigUint::from(1u32))) - .sign_with_nonce(&Params::default(), nonce, &HashMap::new()) - .expect("valid") -} - -impl SignedPod { - pub(crate) fn deserialize(id: PodId, data: serde_json::Value) -> Result> { - let data: Data = serde_json::from_value(data)?; - Ok(Box::new(Self { - id, - signature: data.signature, - signer: data.signer, - dict: data.kvs, - })) - } - - /// Generate a valid SignedPod with a public deterministic secret key and nonce and no other - /// key-values than the default ones. This is used for padding. - pub fn dummy() -> SignedPod { - DUMMY_POD.clone() - } -} - -impl Pod for SignedPod { - fn params(&self) -> &Params { - panic!("SignedPod doesn't have params"); - } - fn verify(&self) -> Result<()> { - // 1. Verify type - let value_at_type = self.dict.get(&Key::from(KEY_TYPE))?; - if Value::from(PodType::Signed) != *value_at_type { - return Err(Error::type_not_equal( - PodType::Signed, - value_at_type.clone(), - )); - } - - // 2. Verify id - let mt = MerkleTree::new( - self.dict.max_depth(), - &self - .dict - .kvs() - .iter() - .map(|(k, v)| (k.raw(), v.raw())) - .collect::>(), - )?; - let id = PodId(mt.root()); - if id != self.id { - return Err(Error::id_not_equal(self.id, id)); - } - - // 3. Verify signature - let embedded_pk_value = self.dict.get(&Key::from(KEY_SIGNER))?; - let pk = self.signer; - let pk_value = Value::from(pk); - if &pk_value != embedded_pk_value { - return Err(Error::signer_not_equal(embedded_pk_value.clone(), pk_value)); - } - self.signature - .verify(pk, RawValue::from(id.0)) - .then_some(()) - .ok_or(Error::custom("Invalid signature!".into())) - } - - fn id(&self) -> PodId { - self.id - } - fn pod_type(&self) -> (usize, &'static str) { - (PodType::Signed as usize, "Signed") - } - - fn pub_self_statements(&self) -> Vec { - // By convention we put the KEY_TYPE first and KEY_SIGNER second - let mut kvs: HashMap = self.dict.kvs().clone(); - let key_type = Key::from(KEY_TYPE); - let value_type = kvs.remove(&key_type).expect("KEY_TYPE"); - let key_signer = Key::from(KEY_SIGNER); - let value_signer = kvs.remove(&key_signer).expect("KEY_SIGNER"); - [(key_type, value_type), (key_signer, value_signer)] - .into_iter() - .chain(kvs.into_iter().sorted_by_key(|kv| kv.0.hash())) - .map(|(k, v)| Statement::equal(AnchoredKey::from((SELF, k)), v)) - .collect() - } - - fn serialize_data(&self) -> serde_json::Value { - serde_json::to_value(Data { - signer: self.signer, - signature: self.signature.clone(), - kvs: self.dict.clone(), - }) - .expect("serialization to json") - } -} - -#[cfg(test)] -pub mod tests { - use std::{any::Any, iter}; - - use plonky2::field::types::Field; - - use super::*; - use crate::{ - frontend, - middleware::{self, EMPTY_VALUE, F}, - }; - - #[test] - fn test_signed_0() -> Result<()> { - let params = middleware::Params::default(); - let mut pod = frontend::SignedPodBuilder::new(¶ms); - pod.insert("idNumber", "4242424242"); - pod.insert("dateOfBirth", 1169909384); - pod.insert("socialSecurityNumber", "G2121210"); - - let sk = SecretKey(123u64.into()); - let signer = Signer(sk); - let pod = pod.sign(&signer).unwrap(); - let pod = (pod.pod as Box).downcast::().unwrap(); - - pod.verify()?; - println!("id: {}", pod.id()); - println!("kvs: {:?}", pod.kvs()); - - let mut bad_pod = pod.clone(); - let nonce = 456u64.into(); - bad_pod.signature = signer.0.sign(RawValue::from(42_i64), &nonce); - assert!(bad_pod.verify().is_err()); - - let mut bad_pod = pod.clone(); - bad_pod.id.0 .0[0] = F::ZERO; - assert!(bad_pod.verify().is_err()); - - let mut bad_pod = pod.clone(); - let bad_kv = (Key::from(KEY_SIGNER), Value::from(EMPTY_VALUE)); - let bad_kvs = bad_pod - .dict - .kvs() - .clone() - .into_iter() - .chain(iter::once(bad_kv)) - .collect::>(); - bad_pod.dict = Dictionary::new(params.max_depth_mt_containers, bad_kvs).unwrap(); - assert!(bad_pod.verify().is_err()); - - let mut bad_pod = pod.clone(); - let bad_kv = (Key::from(KEY_TYPE), Value::from(0)); - let bad_kvs = bad_pod - .dict - .kvs() - .clone() - .into_iter() - .chain(iter::once(bad_kv)) - .collect::>(); - bad_pod.dict = Dictionary::new(params.max_depth_mt_containers, bad_kvs).unwrap(); - assert!(bad_pod.verify().is_err()); - - Ok(()) - } -} diff --git a/src/backends/plonky2/signer.rs b/src/backends/plonky2/signer.rs new file mode 100644 index 0000000..5336fcf --- /dev/null +++ b/src/backends/plonky2/signer.rs @@ -0,0 +1,31 @@ +use num_bigint::{BigUint, RandBigInt}; +use rand::rngs::OsRng; + +use crate::{ + backends::plonky2::primitives::ec::{ + curve::{Point as PublicKey, GROUP_ORDER}, + schnorr::{SecretKey, Signature}, + }, + middleware::{self, RawValue}, + timed, +}; + +pub struct Signer(pub SecretKey); + +impl Signer { + pub(crate) fn sign_with_nonce(&self, nonce: BigUint, msg: RawValue) -> Signature { + let signature: Signature = timed!("SignedPod::sign", self.0.sign(msg, &nonce)); + signature + } +} + +impl middleware::Signer for Signer { + fn sign(&self, msg: RawValue) -> Signature { + let nonce = OsRng.gen_biguint_below(&GROUP_ORDER); + self.sign_with_nonce(nonce, msg) + } + + fn public_key(&self) -> PublicKey { + self.0.public_key() + } +} diff --git a/src/examples/custom.rs b/src/examples/custom.rs index 062b296..6d2f6ae 100644 --- a/src/examples/custom.rs +++ b/src/examples/custom.rs @@ -5,28 +5,15 @@ use hex::ToHex; use crate::{ frontend::{PodRequest, Result}, lang::parse, - middleware::{CustomPredicateBatch, Params, PodType, Value, KEY_SIGNER, KEY_TYPE}, + middleware::{CustomPredicateBatch, Params}, }; -macro_rules! render { - ($tmpl: expr, $($arg:tt)*) => {{ - format!( - $tmpl, - KEY_TYPE = Value::from(KEY_TYPE), - KEY_SIGNER = Value::from(KEY_SIGNER), - $($arg)* - ) - }}; -} - /// Instantiates an ETHDos batch pub fn eth_dos_batch(params: &Params) -> Result> { - let input = render!( - r#" - eth_friend(src, dst, private: attestation_pod) = AND( - Equal(?attestation_pod[{KEY_TYPE}], {pod_type}) - Equal(?attestation_pod[{KEY_SIGNER}], ?src) - Equal(?attestation_pod["attestation"], ?dst) + let input = r#" + eth_friend(src, dst, private: attestation) = AND( + SignedBy(?attestation, ?src) + Contains(?attestation, "attestation", ?dst) ) eth_dos_base(src, dst, distance) = AND( @@ -44,10 +31,8 @@ pub fn eth_dos_batch(params: &Params) -> Result> { eth_dos_base(?src, ?dst, ?distance) eth_dos_ind(?src, ?dst, ?distance) ) - "#, - pod_type = Value::from(PodType::Signed), - ); - let batch = parse(&input, params, &[]).expect("lang parse").custom_batch; + "#; + let batch = parse(input, params, &[]).expect("lang parse").custom_batch; println!("a.0. {}", batch.predicates[0]); println!("a.1. {}", batch.predicates[1]); println!("a.2. {}", batch.predicates[2]); diff --git a/src/examples/mod.rs b/src/examples/mod.rs index 862399f..8ab2d55 100644 --- a/src/examples/mod.rs +++ b/src/examples/mod.rs @@ -8,26 +8,26 @@ use num::BigUint; pub static MOCK_VD_SET: LazyLock = LazyLock::new(|| VDSet::new(6, &[]).unwrap()); use crate::{ - backends::plonky2::{primitives::ec::schnorr::SecretKey, signedpod::Signer}, + backends::plonky2::{primitives::ec::schnorr::SecretKey, signer::Signer}, frontend::{ - MainPod, MainPodBuilder, Operation, PodRequest, Result, SignedPod, SignedPodBuilder, + MainPod, MainPodBuilder, Operation, PodRequest, Result, SignedDict, SignedDictBuilder, }, lang::parse, middleware::{ - containers::Set, hash_values, CustomPredicateRef, Params, PodSigner, PodType, Predicate, - Statement, StatementArg, TypedValue, VDSet, Value, KEY_SIGNER, KEY_TYPE, + self, containers::Set, hash_values, CustomPredicateRef, Params, Predicate, PublicKey, + Signer as _, Statement, StatementArg, TypedValue, VDSet, Value, }, }; // ZuKYC -pub fn zu_kyc_sign_pod_builders(params: &Params) -> (SignedPodBuilder, SignedPodBuilder) { - let mut gov_id = SignedPodBuilder::new(params); +pub fn zu_kyc_sign_dict_builders(params: &Params) -> (SignedDictBuilder, SignedDictBuilder) { + let mut gov_id = SignedDictBuilder::new(params); gov_id.insert("idNumber", "4242424242"); gov_id.insert("dateOfBirth", 1169909384); gov_id.insert("socialSecurityNumber", "G2121210"); - let mut pay_stub = SignedPodBuilder::new(params); + let mut pay_stub = SignedDictBuilder::new(params); pay_stub.insert("socialSecurityNumber", "G2121210"); pay_stub.insert("startDate", 1706367566); @@ -41,8 +41,8 @@ pub const ZU_KYC_SANCTION_LIST: &[&str] = &["A343434340"]; pub fn zu_kyc_pod_builder( params: &Params, vd_set: &VDSet, - gov_id: &SignedPod, - pay_stub: &SignedPod, + gov_id: &SignedDict, + pay_stub: &SignedDict, ) -> Result { let now_minus_18y = ZU_KYC_NOW_MINUS_18Y; let now_minus_1y = ZU_KYC_NOW_MINUS_1Y; @@ -54,8 +54,9 @@ pub fn zu_kyc_pod_builder( Value::from(Set::new(params.max_depth_mt_containers, sanctions_values).unwrap()); let mut kyc = MainPodBuilder::new(params, vd_set); - kyc.add_signed_pod(gov_id); - kyc.add_signed_pod(pay_stub); + kyc.pub_op(Operation::dict_signed_by(gov_id))?; + kyc.pub_op(Operation::dict_signed_by(pay_stub))?; + kyc.pub_op(Operation::set_not_contains( sanction_set, (gov_id, "idNumber"), @@ -66,14 +67,6 @@ pub fn zu_kyc_pod_builder( (pay_stub, "socialSecurityNumber"), ))?; kyc.pub_op(Operation::eq((pay_stub, "startDate"), now_minus_1y))?; - kyc.pub_op(Operation::eq( - (gov_id, "_signer"), - gov_id.get("_signer").unwrap(), - ))?; - kyc.pub_op(Operation::eq( - (pay_stub, "_signer"), - pay_stub.get("_signer").unwrap(), - ))?; Ok(kyc) } @@ -93,8 +86,8 @@ pub fn zu_kyc_pod_request(gov_signer: &Value, pay_signer: &Value) -> Result Result SignedPod { - let mut attestation = SignedPodBuilder::new(params); +pub fn attest_eth_friend( + params: &Params, + src: &impl middleware::Signer, + dst: PublicKey, +) -> SignedDict { + let mut attestation = SignedDictBuilder::new(params); attestation.insert("attestation", dst); attestation.sign(src).unwrap() } @@ -115,16 +112,15 @@ pub fn attest_eth_friend(params: &Params, src: &impl PodSigner, dst: Value) -> S pub struct EthDosHelper { params: Params, vd_set: VDSet, - mock: bool, eth_friend: CustomPredicateRef, eth_dos_base: CustomPredicateRef, eth_dos_ind: CustomPredicateRef, eth_dos: CustomPredicateRef, - src: Value, + src: PublicKey, } impl EthDosHelper { - pub fn new(params: &Params, vd_set: &VDSet, mock: bool, src: Value) -> Result { + pub fn new(params: &Params, vd_set: &VDSet, src: PublicKey) -> Result { let eth_dos_batch = eth_dos_batch(params)?; let eth_friend = eth_dos_batch.predicate_ref_by_name("eth_friend").unwrap(); let eth_dos_base = eth_dos_batch.predicate_ref_by_name("eth_dos_base").unwrap(); @@ -133,7 +129,6 @@ impl EthDosHelper { Ok(Self { params: params.clone(), vd_set: vd_set.clone(), - mock, eth_friend, eth_dos_base, eth_dos_ind, @@ -142,16 +137,13 @@ impl EthDosHelper { }) } - pub fn dist_1(&self, src_attestation: &SignedPod) -> Result { - assert_eq!( - &self.src, - src_attestation.get(KEY_SIGNER).expect("get KEY_SIGNER") - ); + pub fn dist_1(&self, src_attestation: &SignedDict) -> Result { + assert_eq!(self.src, src_attestation.public_key); let mut pod = MainPodBuilder::new(&self.params, &self.vd_set); - pod.add_signed_pod(src_attestation); + pod.pub_op(Operation::dict_signed_by(src_attestation))?; - let src_eq_src = pod.priv_op(Operation::eq(self.src.clone(), self.src.clone()))?; + let src_eq_src = pod.priv_op(Operation::eq(self.src, self.src))?; let distance_eq_zero = pod.priv_op(Operation::eq(0, 0))?; let eth_dos_src_to_src_base = pod.priv_op(Operation::custom( self.eth_dos_base.clone(), @@ -171,20 +163,10 @@ impl EthDosHelper { pub fn dist_n_plus_1( &self, eth_dos_src_to_int_pod: &MainPod, - int_attestation: &SignedPod, // int signs dst + int_attestation: &SignedDict, // int signs dst ) -> Result { - assert_eq!( - Value::from(if self.mock { - PodType::MockMain - } else { - PodType::Main - }), - eth_dos_src_to_int_pod.get(KEY_TYPE).expect("get KEY_TYPE") - ); - let mut pod = MainPodBuilder::new(&self.params, &self.vd_set); - pod.add_signed_pod(int_attestation); - pod.add_recursive_pod(eth_dos_src_to_int_pod.clone()); + pod.add_pod(eth_dos_src_to_int_pod.clone()); let eth_dos_int_to_dst = eth_dos_src_to_int_pod .pod @@ -200,10 +182,7 @@ impl EthDosHelper { _ => panic!("expected StatementArg::Literal"), }) }; - assert_eq!( - &int, - int_attestation.get(KEY_SIGNER).expect("get KEY_SIGNER") - ); + assert_eq!(int, Value::from(int_attestation.public_key)); let n_i64 = if let TypedValue::Int(x) = n.typed() { *x @@ -221,25 +200,15 @@ impl EthDosHelper { &self, pod: &mut MainPodBuilder, eth_dos_int_to_dst: Statement, - int_attestation: &SignedPod, + int_attestation: &SignedDict, n: i64, ) -> Result<()> { - assert_eq!( - &Value::from(PodType::Signed), - int_attestation.get(KEY_TYPE).expect("get KEY_TYPE") - ); - // eth_friend statement - let attestation_is_signed_pod = int_attestation.get_statement(KEY_TYPE).unwrap(); - let attestation_signed_by_int = int_attestation.get_statement(KEY_SIGNER).unwrap(); + let attestation_signed_by_int = pod.priv_op(Operation::dict_signed_by(int_attestation))?; let int_attests_to_dst = int_attestation.get_statement("attestation").unwrap(); let ethfriends_int_dst = pod.priv_op(Operation::custom( self.eth_friend.clone(), - [ - attestation_is_signed_pod, - attestation_signed_by_int, - int_attests_to_dst, - ], + [attestation_signed_by_int, int_attests_to_dst], ))?; // distance = n + 1 @@ -259,17 +228,17 @@ impl EthDosHelper { // GreatBoy -pub fn good_boy_sign_pod_builder(params: &Params, user: &Value, age: i64) -> SignedPodBuilder { - let mut good_boy = SignedPodBuilder::new(params); - good_boy.insert("user", user.clone()); +pub fn good_boy_sign_pod_builder(params: &Params, user: &PublicKey, age: i64) -> SignedDictBuilder { + let mut good_boy = SignedDictBuilder::new(params); + good_boy.insert("user", *user); good_boy.insert("age", age); good_boy } -pub fn friend_sign_pod_builder(params: &Params, friend: &Value) -> SignedPodBuilder { - let mut friend_pod = SignedPodBuilder::new(params); - friend_pod.insert("friend", friend.clone()); +pub fn friend_sign_pod_builder(params: &Params, friend: &PublicKey) -> SignedDictBuilder { + let mut friend_pod = SignedDictBuilder::new(params); + friend_pod.insert("friend", *friend); friend_pod } @@ -277,10 +246,10 @@ pub fn friend_sign_pod_builder(params: &Params, friend: &Value) -> SignedPodBuil pub fn great_boy_pod_builder( params: &Params, vd_set: &VDSet, - good_boy_pods: [&SignedPod; 4], - friend_pods: [&SignedPod; 2], + good_boy_signed_dicts: [&SignedDict; 4], + friend_signed_dicts: [&SignedDict; 2], good_boy_issuers: &Value, - receiver: &Value, + receiver: &PublicKey, ) -> Result { // Attestment chain (issuer -> good boy -> great boy): // issuer 0 -> good_boy_pods[0] => good boy 0 @@ -291,51 +260,41 @@ pub fn great_boy_pod_builder( // good boy 1 -> friend_pods[1] => receiver let mut great_boy = MainPodBuilder::new(params, vd_set); - for good_boy_pod in good_boy_pods { - great_boy.add_signed_pod(good_boy_pod); + for good_boy_signed_dict in good_boy_signed_dicts { + great_boy.pub_op(Operation::dict_signed_by(good_boy_signed_dict))?; } - for friend_pod in friend_pods { - great_boy.add_signed_pod(friend_pod); + for friend_signed_dict in friend_signed_dicts { + great_boy.pub_op(Operation::dict_signed_by(friend_signed_dict))?; } for good_boy_idx in 0..2 { - // Type check - great_boy.pub_op(Operation::eq( - (friend_pods[good_boy_idx], KEY_TYPE), - PodType::Signed as i64, - ))?; for issuer_idx in 0..2 { - // Type check - great_boy.pub_op(Operation::eq( - (good_boy_pods[good_boy_idx * 2 + issuer_idx], KEY_TYPE), - PodType::Signed as i64, - ))?; // Each good boy POD comes from a valid issuer great_boy.pub_op(Operation::set_contains( good_boy_issuers, - (good_boy_pods[good_boy_idx * 2 + issuer_idx], KEY_SIGNER), + good_boy_signed_dicts[good_boy_idx * 2 + issuer_idx].public_key, ))?; // Each good boy has 2 good boy pods great_boy.pub_op(Operation::eq( - (good_boy_pods[good_boy_idx * 2 + issuer_idx], "user"), - (friend_pods[good_boy_idx], KEY_SIGNER), + (good_boy_signed_dicts[good_boy_idx * 2 + issuer_idx], "user"), + friend_signed_dicts[good_boy_idx].public_key, ))?; } // The good boy PODs from each good boy have different issuers great_boy.pub_op(Operation::ne( - (good_boy_pods[good_boy_idx * 2], KEY_SIGNER), - (good_boy_pods[good_boy_idx * 2 + 1], KEY_SIGNER), + good_boy_signed_dicts[good_boy_idx * 2].public_key, + good_boy_signed_dicts[good_boy_idx * 2 + 1].public_key, ))?; // Each good boy is receivers' friend great_boy.pub_op(Operation::eq( - (friend_pods[good_boy_idx], "friend"), - receiver.clone(), + (friend_signed_dicts[good_boy_idx], "friend"), + *receiver, ))?; } // The two good boys are different great_boy.pub_op(Operation::ne( - (friend_pods[0], KEY_SIGNER), - (friend_pods[1], KEY_SIGNER), + friend_signed_dicts[0].public_key, + friend_signed_dicts[1].public_key, ))?; Ok(great_boy) @@ -343,11 +302,11 @@ pub fn great_boy_pod_builder( pub fn great_boy_pod_full_flow() -> Result { let params = Params { - max_input_signed_pods: 6, - max_input_recursive_pods: 0, + max_signed_by: 6, + max_input_pods: 0, max_statements: 100, max_public_statements: 50, - num_public_statements_id: 50, + num_public_statements_hash: 50, ..Default::default() }; let vd_set = &*MOCK_VD_SET; @@ -413,13 +372,13 @@ pub fn great_boy_pod_full_flow() -> Result { pub const TICKET_OWNER_SECRET_KEY: SecretKey = SecretKey(BigUint::ZERO); -pub fn tickets_sign_pod_builder(params: &Params) -> SignedPodBuilder { +pub fn tickets_sign_pod_builder(params: &Params) -> SignedDictBuilder { // Create a signed pod with all atomic types (string, int, bool) - let mut builder = SignedPodBuilder::new(params); + let mut builder = SignedDictBuilder::new(params); builder.insert("eventId", 123); builder.insert("productId", 456); // Removed temporarily to make the example fit in 8 entries. - //builder.insert("attendeeName", "John Doe"); + // builder.insert("attendeeName", "John Doe"); builder.insert("attendeeEmail", "john.doe@example.com"); builder.insert("attendeePublicKey", TICKET_OWNER_SECRET_KEY.public_key()); builder.insert("isConsumed", true); @@ -430,7 +389,7 @@ pub fn tickets_sign_pod_builder(params: &Params) -> SignedPodBuilder { pub fn tickets_pod_builder( params: &Params, vd_set: &VDSet, - signed_pod: &SignedPod, + signed_dict: &SignedDict, expected_event_id: i64, expect_consumed: bool, blacklisted_emails: &Set, @@ -438,28 +397,28 @@ pub fn tickets_pod_builder( let blacklisted_email_set_value = Value::from(TypedValue::Set(blacklisted_emails.clone())); // Create a main pod referencing this signed pod with some statements let mut builder = MainPodBuilder::new(params, vd_set); - builder.add_signed_pod(signed_pod); - builder.pub_op(Operation::eq((signed_pod, "eventId"), expected_event_id))?; - builder.pub_op(Operation::eq((signed_pod, "isConsumed"), expect_consumed))?; - builder.pub_op(Operation::eq((signed_pod, "isRevoked"), false))?; + builder.pub_op(Operation::dict_signed_by(signed_dict))?; + builder.pub_op(Operation::eq((signed_dict, "eventId"), expected_event_id))?; + builder.pub_op(Operation::eq((signed_dict, "isConsumed"), expect_consumed))?; + builder.pub_op(Operation::eq((signed_dict, "isRevoked"), false))?; builder.pub_op(Operation::dict_not_contains( blacklisted_email_set_value, - (signed_pod, "attendeeEmail"), + (signed_dict, "attendeeEmail"), ))?; // This isn't the most fool-proof way to prove ownership (it requires // verifier to check pod ID on an anchored key to confirm statement wasn't // copied), but it's the simplest. - let st_sk = builder.priv_literal(TICKET_OWNER_SECRET_KEY)?; + let sk = TICKET_OWNER_SECRET_KEY; builder.pub_op(Operation::public_key_of( - (signed_pod, "attendeePublicKey"), - st_sk.clone(), + (signed_dict, "attendeePublicKey"), + sk.clone(), ))?; // Nullifier calculation is public, but based on the private sk. let external_nullifier = "external nullifier"; let nullifier = hash_values(&[TICKET_OWNER_SECRET_KEY.into(), external_nullifier.into()]); - builder.pub_op(Operation::hash_of(nullifier, st_sk, external_nullifier))?; + builder.pub_op(Operation::hash_of(nullifier, sk, external_nullifier))?; Ok(builder) } @@ -467,11 +426,11 @@ pub fn tickets_pod_builder( pub fn tickets_pod_full_flow(params: &Params, vd_set: &VDSet) -> Result { let builder = tickets_sign_pod_builder(params); - let signed_pod = builder.sign(&Signer(SecretKey(1u32.into()))).unwrap(); + let signed_dict = builder.sign(&Signer(SecretKey(1u32.into()))).unwrap(); tickets_pod_builder( params, vd_set, - &signed_pod, + &signed_dict, 123, true, &Set::new(params.max_depth_mt_containers, HashSet::new())?, diff --git a/src/frontend/custom.rs b/src/frontend/custom.rs index d83e0a1..dd2b517 100644 --- a/src/frontend/custom.rs +++ b/src/frontend/custom.rs @@ -6,7 +6,7 @@ use schemars::JsonSchema; use crate::{ frontend::{AnchoredKey, Error, Result, Statement, StatementArg}, middleware::{ - self, hash_str, CustomPredicate, CustomPredicateBatch, Key, NativePredicate, Params, PodId, + self, hash_str, CustomPredicate, CustomPredicateBatch, Hash, Key, NativePredicate, Params, Predicate, StatementTmpl, StatementTmplArg, ToFields, Value, Wildcard, }, }; @@ -181,8 +181,8 @@ impl CustomPredicateBatchBuilder { .map(|a| { Ok::<_, Error>(match a { BuilderArg::Literal(v) => StatementTmplArg::Literal(v.clone()), - BuilderArg::Key(pod_id_wc, key_str) => StatementTmplArg::AnchoredKey( - resolve_wildcard(args, priv_args, pod_id_wc)?, + BuilderArg::Key(root_wc, key_str) => StatementTmplArg::AnchoredKey( + resolve_wildcard(args, priv_args, root_wc)?, Key::from(key_str), ), BuilderArg::WildcardLiteral(v) => { @@ -223,7 +223,7 @@ fn resolve_wildcard(args: &[&str], priv_args: &[&str], s: &str) -> Result 1 - let s1 = mp_builder.priv_op(Operation::new_entry("s1_key", Value::from(2)))?; - let s2 = mp_builder.priv_op(Operation::new_entry("s2_key", Value::from(1)))?; - // Adding a gt operation will produce a desugared lt operation - let desugared_gt = mp_builder.pub_op(Operation::gt(s1, s2))?; + let desugared_gt = mp_builder.pub_op(Operation::gt(2, 1))?; assert_eq!( desugared_gt.predicate(), Predicate::Native(NativePredicate::Lt) @@ -324,12 +316,12 @@ mod tests { CustomPredicateBatchBuilder::new(params.clone(), "set_contains_custom_pred".into()); let set_contains_stb = StatementTmplBuilder::new(NativePredicate::SetContains) - .arg(("s1_origin", "s1_key")) - .arg(("s2_origin", "s2_key")); + .arg("s1") + .arg("s2"); builder.predicate_and( "set_contains_custom_pred", - &["s1_origin", "s2_origin"], + &["s1", "s2"], &[], &[set_contains_stb], )?; @@ -339,11 +331,8 @@ mod tests { let mut mp_builder = MainPodBuilder::new(¶ms, vd_set); let set_values: HashSet = [1, 2, 3].iter().map(|i| Value::from(*i)).collect(); - let s1 = mp_builder.priv_op(Operation::new_entry( - "s1_key", - Value::from(Set::new(params.max_depth_mt_containers, set_values)?), - ))?; - let s2 = mp_builder.priv_op(Operation::new_entry("s2_key", Value::from(1)))?; + let s1 = Set::new(params.max_depth_mt_containers, set_values)?; + let s2 = 1; let set_contains = mp_builder.pub_op(Operation::set_contains(s1, s2))?; assert_eq!( diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs index 158714f..29c93e9 100644 --- a/src/frontend/mod.rs +++ b/src/frontend/mod.rs @@ -4,13 +4,15 @@ use std::{collections::HashMap, convert::From, fmt}; use itertools::Itertools; +use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -pub use serialization::{SerializedMainPod, SerializedSignedPod}; +pub use serialization::SerializedMainPod; use crate::middleware::{ - self, check_custom_pred, check_st_tmpl, hash_op, hash_str, max_op, prod_op, sum_op, - AnchoredKey, Key, MainPodInputs, NativeOperation, OperationAux, OperationType, Params, PodId, - PodProver, PodSigner, Statement, StatementArg, VDSet, Value, ValueRef, KEY_TYPE, SELF, + self, check_custom_pred, check_st_tmpl, containers::Dictionary, hash_op, max_op, prod_op, + sum_op, AnchoredKey, Hash, Key, MainPodInputs, MainPodProver, NativeOperation, OperationAux, + OperationType, Params, PublicKey, RawValue, Signature, Signer, Statement, StatementArg, VDSet, + Value, ValueRef, }; mod custom; @@ -24,14 +26,14 @@ pub use operation::*; pub use pod_request::*; #[derive(Clone, Debug)] -pub struct SignedPodBuilder { +pub struct SignedDictBuilder { pub params: Params, pub kvs: HashMap, } -impl fmt::Display for SignedPodBuilder { +impl fmt::Display for SignedDictBuilder { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "SignedPodBuilder:")?; + writeln!(f, "SignedDictBuilder:")?; for (k, v) in self.kvs.iter().sorted_by_key(|kv| kv.0.hash()) { writeln!(f, " - {}: {}", k, v)?; } @@ -39,7 +41,7 @@ impl fmt::Display for SignedPodBuilder { } } -impl SignedPodBuilder { +impl SignedDictBuilder { pub fn new(params: &Params) -> Self { Self { params: params.clone(), @@ -51,65 +53,66 @@ impl SignedPodBuilder { self.kvs.insert(key.into(), value.into()); } - pub fn sign(&self, signer: &S) -> Result { - // Sign POD with committed KV store. - let pod = signer.sign(&self.params, &self.kvs)?; + pub fn sign(&self, signer: &S) -> Result { + // Sign committed KV store. + let dict = Dictionary::new(self.params.max_depth_mt_containers, self.kvs.clone())?; + // NOTE: This is the same way that `TypedValue::Dictionary` computes the `RawValue` + let msg_raw = RawValue::from(dict.commitment()); + let signature = signer.sign(msg_raw); - Ok(SignedPod::new(pod)) + Ok(SignedDict { + dict, + public_key: signer.public_key(), + signature, + }) } } -/// SignedPod is a wrapper on top of backend::SignedPod, which additionally stores the -/// string<-->hash relation of the keys. -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(try_from = "SerializedSignedPod", into = "SerializedSignedPod")] -pub struct SignedPod { - pub pod: Box, - // We store a copy of the key values for quick access - kvs: HashMap, +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +// #[serde(try_from = "SerializedSignedDict", into = "SerializedSignedDict")] +pub struct SignedDict { + pub dict: Dictionary, + pub public_key: PublicKey, + pub signature: Signature, } -impl fmt::Display for SignedPod { +impl fmt::Display for SignedDict { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "SignedPod (id:{}):", self.id())?; + writeln!(f, "SignedDict (raw:{}):", self.dict.commitment())?; // Note: current version iterates sorting by keys of the kvs, but the merkletree defined at // https://0xparc.github.io/pod2/merkletree.html will not need it since it will be // deterministic based on the keys values not on the order of the keys when added into the // tree. - for (k, v) in self.pod.kvs().iter().sorted_by_key(|kv| kv.0.key.hash()) { + for (k, v) in self.dict.kvs().iter().sorted_by_key(|kv| kv.0.hash()) { writeln!(f, " - {} = {}", k, v)?; } Ok(()) } } -impl SignedPod { - pub fn new(pod: Box) -> Self { - let kvs = pod - .kvs() - .into_iter() - .map(|(AnchoredKey { key, .. }, v)| (key, v)) - .collect(); - Self { pod, kvs } - } - pub fn id(&self) -> PodId { - self.pod.id() - } +impl SignedDict { pub fn verify(&self) -> Result<()> { - self.pod.verify().map_err(Error::Backend) + self.signature + .verify(self.public_key, RawValue::from(self.dict.commitment())) + .then_some(()) + .ok_or(Error::custom("Invalid signature!")) } pub fn kvs(&self) -> &HashMap { - &self.kvs + self.dict.kvs() } pub fn get(&self, key: impl Into) -> Option<&Value> { - self.kvs.get(&key.into()) + self.kvs().get(&key.into()) } - // Returns the Equal statement that defines key if it exists. + // Returns the Contains statement that defines key if it exists. pub fn get_statement(&self, key: impl Into) -> Option { let key: Key = key.into(); - self.kvs() - .get(&key) - .map(|value| Statement::equal(AnchoredKey::from((self.id(), key)), value.clone())) + self.kvs().get(&key).map(|value| { + Statement::Contains( + ValueRef::Literal(Value::from(self.dict.clone())), + ValueRef::Literal(Value::from(key.name())), + ValueRef::Literal(value.clone()), + ) + }) } } @@ -119,27 +122,20 @@ impl SignedPod { pub struct MainPodBuilder { pub params: Params, pub vd_set: VDSet, - pub input_signed_pods: Vec, - pub input_recursive_pods: Vec, + pub input_pods: Vec, pub statements: Vec, pub operations: Vec, pub public_statements: Vec, // Internal state - /// Counter for constants created from literals - const_cnt: usize, - /// Map from (public, Value) to Key of already created literals via Equal statements. - literals: HashMap<(bool, Value), Key>, + // TODO: track contains ops with literals added explicitly as well. + dict_contains: Vec<(Value, Value)>, // (root, key) } impl fmt::Display for MainPodBuilder { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "MainPod:")?; - writeln!(f, " input_signed_pods:")?; - for in_pod in &self.input_signed_pods { - writeln!(f, " - {}", in_pod.id())?; - } writeln!(f, " input_main_pods:")?; - for in_pod in &self.input_recursive_pods { + for in_pod in &self.input_pods { writeln!(f, " - {}", in_pod.id())?; } writeln!(f, " statements:")?; @@ -157,20 +153,15 @@ impl MainPodBuilder { Self { params: params.clone(), vd_set: vd_set.clone(), - input_signed_pods: Vec::new(), - input_recursive_pods: Vec::new(), + input_pods: Vec::new(), statements: Vec::new(), operations: Vec::new(), public_statements: Vec::new(), - const_cnt: 0, - literals: HashMap::new(), + dict_contains: Vec::new(), } } - pub fn add_signed_pod(&mut self, pod: &SignedPod) { - self.input_signed_pods.push(pod.clone()); - } - pub fn add_recursive_pod(&mut self, pod: MainPod) { - self.input_recursive_pods.push(pod); + pub fn add_pod(&mut self, pod: MainPod) { + self.input_pods.push(pod); } pub fn insert(&mut self, public: bool, st_op: (Statement, Operation)) -> Result<()> { // TODO: Do error handling instead of panic @@ -384,12 +375,9 @@ impl MainPodBuilder { let st = match op.0 { OperationType::Native(o) => { let native_arg_error = move || Error::op_invalid_args(format!("{o:?}")); - match (o, &op.1.as_slice()) { - (None, &[]) => Statement::None, - (NewEntry, &[OperationArg::Entry(k, v)]) => { - Statement::equal(AnchoredKey::from((SELF, k.as_str())), v.clone()) - } - (EqualFromEntries, &[a1, a2]) => { + match (o, &op.1.as_slice(), &op.2) { + (None, &[], _) => Statement::None, + (EqualFromEntries, &[a1, a2], _) => { let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?; let (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?; if v1 == v2 { @@ -398,7 +386,7 @@ impl MainPodBuilder { return Err(native_arg_error()); } } - (NotEqualFromEntries, &[a1, a2]) => { + (NotEqualFromEntries, &[a1, a2], _) => { let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?; let (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?; if v1 != v2 { @@ -407,7 +395,7 @@ impl MainPodBuilder { return Err(native_arg_error()); } } - (LtFromEntries, &[a1, a2]) => { + (LtFromEntries, &[a1, a2], _) => { let (r1, v1) = a1.int_value_and_ref().ok_or_else(native_arg_error)?; let (r2, v2) = a2.int_value_and_ref().ok_or_else(native_arg_error)?; if v1 < v2 { @@ -416,7 +404,7 @@ impl MainPodBuilder { return Err(native_arg_error()); } } - (LtEqFromEntries, &[a1, a2]) => { + (LtEqFromEntries, &[a1, a2], _) => { let (r1, v1) = a1.int_value_and_ref().ok_or_else(native_arg_error)?; let (r2, v2) = a2.int_value_and_ref().ok_or_else(native_arg_error)?; if v1 <= v2 { @@ -425,10 +413,11 @@ impl MainPodBuilder { return Err(native_arg_error()); } } - (CopyStatement, &[OperationArg::Statement(s)]) => s.clone(), + (CopyStatement, &[OperationArg::Statement(s)], _) => s.clone(), ( TransitiveEqualFromStatements, &[OperationArg::Statement(Statement::Equal(r1, r2)), OperationArg::Statement(Statement::Equal(r3, r4))], + _, ) => { if r2 == r3 { Statement::Equal(r1.clone(), r4.clone()) @@ -436,10 +425,10 @@ impl MainPodBuilder { return Err(native_arg_error()); } } - (LtToNotEqual, &[OperationArg::Statement(Statement::Lt(r1, r2))]) => { + (LtToNotEqual, &[OperationArg::Statement(Statement::Lt(r1, r2))], _) => { Statement::NotEqual(r1.clone(), r2.clone()) } - (SumOf, &[a1, a2, a3]) => { + (SumOf, &[a1, a2, a3], _) => { let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?; let (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?; let (r3, v3) = a3.value_and_ref().ok_or_else(native_arg_error)?; @@ -449,7 +438,7 @@ impl MainPodBuilder { return Err(native_arg_error()); } } - (ProductOf, &[a1, a2, a3]) => { + (ProductOf, &[a1, a2, a3], _) => { let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?; let (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?; let (r3, v3) = a3.value_and_ref().ok_or_else(native_arg_error)?; @@ -459,7 +448,7 @@ impl MainPodBuilder { return Err(native_arg_error()); } } - (MaxOf, &[a1, a2, a3]) => { + (MaxOf, &[a1, a2, a3], _) => { let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?; let (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?; let (r3, v3) = a3.value_and_ref().ok_or_else(native_arg_error)?; @@ -469,7 +458,7 @@ impl MainPodBuilder { return Err(native_arg_error()); } } - (HashOf, &[a1, a2, a3]) => { + (HashOf, &[a1, a2, a3], _) => { let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?; let (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?; let (r3, v3) = a3.value_and_ref().ok_or_else(native_arg_error)?; @@ -479,20 +468,20 @@ impl MainPodBuilder { return Err(native_arg_error()); } } - (ContainsFromEntries, &[a1, a2, a3]) => { + (ContainsFromEntries, &[a1, a2, a3], _) => { let (r1, _v1) = a1.value_and_ref().ok_or_else(native_arg_error)?; let (r2, _v2) = a2.value_and_ref().ok_or_else(native_arg_error)?; let (r3, _v3) = a3.value_and_ref().ok_or_else(native_arg_error)?; // TODO: validate proof Statement::Contains(r1, r2, r3) } - (NotContainsFromEntries, &[a1, a2]) => { + (NotContainsFromEntries, &[a1, a2], _) => { let (r1, _v1) = a1.value_and_ref().ok_or_else(native_arg_error)?; let (r2, _v2) = a2.value_and_ref().ok_or_else(native_arg_error)?; // TODO: validate proof Statement::NotContains(r1, r2) } - (PublicKeyOf, &[a1, a2]) => { + (PublicKeyOf, &[a1, a2], _) => { let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?; let (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?; if middleware::Operation::check_public_key(v1, v2)? { @@ -501,7 +490,16 @@ impl MainPodBuilder { return Err(native_arg_error()); } } - (ContainerInsertFromEntries, &[a1, a2, a3, a4]) => { + (SignedBy, &[a1, a2], OperationAux::Signature(sig)) => { + let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?; + let (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?; + if middleware::Operation::check_signed_by(v1, v2, sig)? { + Statement::SignedBy(r1, r2) + } else { + return Err(native_arg_error()); + } + } + (ContainerInsertFromEntries, &[a1, a2, a3, a4], _) => { let (r1, _v1) = a1.value_and_ref().ok_or_else(native_arg_error)?; let (r2, _v2) = a2.value_and_ref().ok_or_else(native_arg_error)?; let (r3, _v3) = a3.value_and_ref().ok_or_else(native_arg_error)?; @@ -509,7 +507,7 @@ impl MainPodBuilder { // TODO: validate proof Statement::ContainerInsert(r1, r2, r3, r4) } - (ContainerUpdateFromEntries, &[a1, a2, a3, a4]) => { + (ContainerUpdateFromEntries, &[a1, a2, a3, a4], _) => { let (r1, _v1) = a1.value_and_ref().ok_or_else(native_arg_error)?; let (r2, _v2) = a2.value_and_ref().ok_or_else(native_arg_error)?; let (r3, _v3) = a3.value_and_ref().ok_or_else(native_arg_error)?; @@ -517,14 +515,14 @@ impl MainPodBuilder { // TODO: validate proof Statement::ContainerUpdate(r1, r2, r3, r4) } - (ContainerDeleteFromEntries, &[a1, a2, a3]) => { + (ContainerDeleteFromEntries, &[a1, a2, a3], _) => { let (r1, _v1) = a1.value_and_ref().ok_or_else(native_arg_error)?; let (r2, _v2) = a2.value_and_ref().ok_or_else(native_arg_error)?; let (r3, _v3) = a3.value_and_ref().ok_or_else(native_arg_error)?; // TODO: validate proof Statement::ContainerDelete(r1, r2, r3) } - (t, _) => { + (t, _, _) => { if t.is_syntactic_sugar() { return Err(Error::custom(format!( "Unexpected syntactic sugar: {:?}", @@ -583,7 +581,28 @@ impl MainPodBuilder { Ok(st) } + /// For every operation that has Entry statements as arguments we add a Contains statement to + /// open the dictionary. + fn add_entries_contains(&mut self, op: &Operation) -> Result<()> { + for arg in &op.1 { + if let OperationArg::Statement(Statement::Contains( + ValueRef::Literal(dict), + ValueRef::Literal(key), + ValueRef::Literal(v), + )) = arg + { + let root_key = (dict.clone(), key.clone()); + if !self.dict_contains.contains(&root_key) { + self.dict_contains.push(root_key); + self.priv_op(Operation::dict_contains(dict, key, v))?; + } + } + } + Ok(()) + } + fn op(&mut self, public: bool, op: Operation) -> Result { + self.add_entries_contains(&op)?; let op = Self::fill_in_aux(Self::lower_op(op)?)?; let st = self.op_statement(op.clone())?; self.insert(public, (st, op))?; @@ -591,48 +610,13 @@ impl MainPodBuilder { Ok(self.statements[self.statements.len() - 1].clone()) } - /// Convenience method for introducing public constants. - pub fn pub_literal(&mut self, v: impl Into) -> Result { - self.literal(true, v.into()) - } - - /// Convenience method for introducing private constants. - pub fn priv_literal(&mut self, v: impl Into) -> Result { - self.literal(false, v.into()) - } - - fn literal(&mut self, public: bool, value: Value) -> Result { - let public_value = (public, value); - if let Some(key) = self.literals.get(&public_value) { - Ok(Statement::equal( - AnchoredKey::new(SELF, key.clone()), - public_value.1, - )) - } else { - let key = format!("c{}", self.const_cnt); - self.literals - .insert(public_value.clone(), Key::new(key.clone())); - self.const_cnt += 1; - self.op( - public, - Operation( - OperationType::Native(NativeOperation::NewEntry), - vec![OperationArg::Entry(key.clone(), public_value.1)], - OperationAux::None, - ), - ) - } - } - pub fn reveal(&mut self, st: &Statement) { self.public_statements.push(st.clone()); } - pub fn prove(&self, prover: &dyn PodProver) -> Result { + pub fn prove(&self, prover: &dyn MainPodProver) -> Result { let compiler = MainPodCompiler::new(&self.params); let inputs = MainPodCompilerInputs { - // signed_pods: &self.input_signed_pods, - // main_pods: &self.input_main_pods, statements: &self.statements, operations: &self.operations, public_statements: &self.public_statements, @@ -641,67 +625,18 @@ impl MainPodBuilder { let (statements, operations, public_statements) = compiler.compile(inputs, &self.params)?; let inputs = MainPodInputs { - signed_pods: &self - .input_signed_pods - .iter() - .map(|p| p.pod.as_ref()) - .collect_vec(), - recursive_pods: &self - .input_recursive_pods - .iter() - .map(|p| p.pod.as_ref()) - .collect_vec(), + pods: &self.input_pods.iter().map(|p| p.pod.as_ref()).collect_vec(), statements: &statements, operations: &operations, public_statements: &public_statements, vd_set: self.vd_set.clone(), }; - let pod = prover.prove(&self.params, &self.vd_set, inputs)?; - - // Gather public statements, making sure to inject the type - // information specified by the backend. - let pod_id = pod.id(); - let type_key_hash = hash_str(KEY_TYPE); - let type_statement = pod - .pub_statements() - .into_iter() - .find_map(|s| match s.as_entry() { - Some((AnchoredKey { pod_id: id, key }, _)) - if id == &pod_id && key.hash() == type_key_hash => - { - Some(s) - } - _ => None, - }) - .ok_or(Error::custom(format!( - // TODO use a specific Error - "Missing POD type information in POD: {:?}", - pod - )))?; - // Replace instances of `SELF` with the POD ID for consistency - // with `pub_statements` method. - let public_statements = [type_statement] - .into_iter() - .chain(self.public_statements.clone().into_iter().map(|s| { - let s_type = s.predicate(); - let s_args = s - .args() - .into_iter() - .map(|arg| match arg { - StatementArg::Key(AnchoredKey { pod_id: id, key }) if id == SELF => { - StatementArg::Key(AnchoredKey::new(pod_id, key)) - } - _ => arg, - }) - .collect(); - Statement::from_args(s_type, s_args).expect("valid arguments") - })) - .collect(); + let pod = prover.prove(&self.params, inputs)?; Ok(MainPod { pod, params: self.params.clone(), - public_statements, + public_statements: self.public_statements.clone(), }) } } @@ -709,30 +644,26 @@ impl MainPodBuilder { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(try_from = "SerializedMainPod", into = "SerializedMainPod")] pub struct MainPod { - pub pod: Box, + pub pod: Box, pub public_statements: Vec, pub params: Params, } impl fmt::Display for MainPod { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "MainPod: {}", self.pod.id())?; + writeln!(f, "MainPod: {}", self.pod.statements_hash())?; writeln!(f, " valid? {}", self.pod.verify().is_ok())?; writeln!(f, " statements:")?; for st in &self.pod.pub_statements() { writeln!(f, " - {}", st)?; } - writeln!(f, " kvs:")?; - for (k, v) in &self.pod.kvs() { - writeln!(f, " - {}: {}", k, v)?; - } Ok(()) } } impl MainPod { - pub fn id(&self) -> PodId { - self.pod.id() + pub fn id(&self) -> Hash { + self.pod.statements_hash() } /// Returns the value of a Equal statement with self id that defines key if it exists. @@ -742,7 +673,7 @@ impl MainPod { .iter() .find_map(|st| match st { Statement::Equal(ValueRef::Key(ak), ValueRef::Literal(value)) - if ak.pod_id == self.id() && ak.key.hash() == key.hash() => + if ak.root == self.id() && ak.key.hash() == key.hash() => { Some(value) } @@ -753,8 +684,6 @@ impl MainPod { } struct MainPodCompilerInputs<'a> { - // pub signed_pods: &'a [Box], - // pub main_pods: &'a [Box], pub statements: &'a [Statement], pub operations: &'a [Operation], pub public_statements: &'a [Statement], @@ -832,8 +761,6 @@ impl MainPodCompiler { Vec, // public statements )> { let MainPodCompilerInputs { - // signed_pods: _, - // main_pods: _, statements, operations, public_statements, @@ -853,16 +780,17 @@ pub mod tests { use super::*; use crate::{ backends::plonky2::{ - mock::mainpod::MockProver, primitives::ec::schnorr::SecretKey, signedpod::Signer, + mock::mainpod::MockProver, primitives::ec::schnorr::SecretKey, signer::Signer, }, + dict, examples::{ attest_eth_friend, custom::eth_dos_request, great_boy_pod_full_flow, tickets_pod_full_flow, zu_kyc_pod_builder, zu_kyc_pod_request, - zu_kyc_sign_pod_builders, EthDosHelper, MOCK_VD_SET, + zu_kyc_sign_dict_builders, EthDosHelper, MOCK_VD_SET, }, middleware::{ - containers::{Array, Dictionary, Set}, - Value, + containers::{Array, Set}, + Signer as _, Value, }, }; @@ -874,44 +802,21 @@ pub mod tests { Ok(()) } - // Check that frontend key-values agree with those embedded in a - // SignedPod. - fn check_kvs(pod: &SignedPod) -> Result<()> { - let kvs = pod.kvs.clone().into_iter().collect::>(); - let embedded_kvs = pod - .pod - .kvs() - .into_iter() - .map(|(middleware::AnchoredKey { key, .. }, v)| (key, v)) - .collect::>(); - - if kvs == embedded_kvs { - Ok(()) - } else { - Err(Error::custom(format!( - "KVs {:?} do not agree with those embedded in the POD: {:?}", - kvs, embedded_kvs - ))) - } - } - #[test] fn test_front_zu_kyc() -> Result<()> { let params = Params::default(); let vd_set = &*MOCK_VD_SET; - let (gov_id, pay_stub) = zu_kyc_sign_pod_builders(¶ms); + let (gov_id, pay_stub) = zu_kyc_sign_dict_builders(¶ms); println!("{}", gov_id); println!("{}", pay_stub); - let signer = Signer(SecretKey(1u32.into())); - let gov_id = gov_id.sign(&signer)?; - check_kvs(&gov_id)?; + let gov_id_signer = Signer(SecretKey(1u32.into())); + let gov_id = gov_id.sign(&gov_id_signer)?; println!("{}", gov_id); - let signer = Signer(SecretKey(2u32.into())); - let pay_stub = pay_stub.sign(&signer)?; - check_kvs(&pay_stub)?; + let pay_stub_signer = Signer(SecretKey(2u32.into())); + let pay_stub = pay_stub.sign(&pay_stub_signer)?; println!("{}", pay_stub); let kyc_builder = zu_kyc_pod_builder(¶ms, vd_set, &gov_id, &pay_stub)?; @@ -923,14 +828,22 @@ pub mod tests { println!("{}", kyc); + kyc.pod.verify()?; + let request = zu_kyc_pod_request( - gov_id.get("_signer").unwrap(), - pay_stub.get("_signer").unwrap(), + &Value::from(gov_id_signer.public_key()), + &Value::from(pay_stub_signer.public_key()), )?; // Check the bindings of the "gov" and "pay" wildcards from the PodRequest let bindings = request.exact_match_pod(&*kyc.pod).unwrap(); - assert_eq!(*bindings.get("gov").unwrap(), gov_id.id().into()); - assert_eq!(*bindings.get("pay").unwrap(), pay_stub.id().into()); + assert_eq!( + *bindings.get("gov").unwrap(), + gov_id.dict.commitment().into() + ); + assert_eq!( + *bindings.get("pay").unwrap(), + pay_stub.dict.commitment().into() + ); check_public_statements(&kyc) } @@ -950,7 +863,7 @@ pub mod tests { let charlie = Signer(SecretKey(3u32.into())); let david = Signer(SecretKey(4u32.into())); - let helper = EthDosHelper::new(¶ms, vd_set, true, alice.public_key())?; + let helper = EthDosHelper::new(¶ms, vd_set, alice.public_key())?; let prover = MockProver {}; @@ -960,8 +873,8 @@ pub mod tests { let request = eth_dos_request()?; assert!(request.exact_match_pod(&*dist_1.pod).is_ok()); let bindings = request.exact_match_pod(&*dist_1.pod).unwrap(); - assert_eq!(*bindings.get("src").unwrap(), alice.public_key()); - assert_eq!(*bindings.get("dst").unwrap(), bob.public_key()); + assert_eq!(*bindings.get("src").unwrap(), alice.public_key().into()); + assert_eq!(*bindings.get("dst").unwrap(), bob.public_key().into()); assert_eq!(*bindings.get("distance").unwrap(), 1.into()); let bob_attestation = attest_eth_friend(¶ms, &bob, charlie.public_key()); @@ -971,8 +884,8 @@ pub mod tests { dist_2.pod.verify()?; assert!(request.exact_match_pod(&*dist_2.pod).is_ok()); let bindings = request.exact_match_pod(&*dist_2.pod).unwrap(); - assert_eq!(*bindings.get("src").unwrap(), alice.public_key()); - assert_eq!(*bindings.get("dst").unwrap(), charlie.public_key()); + assert_eq!(*bindings.get("src").unwrap(), alice.public_key().into()); + assert_eq!(*bindings.get("dst").unwrap(), charlie.public_key().into()); assert_eq!(*bindings.get("distance").unwrap(), 2.into()); let charlie_attestation = attest_eth_friend(¶ms, &charlie, david.public_key()); @@ -982,8 +895,8 @@ pub mod tests { dist_3.pod.verify()?; assert!(request.exact_match_pod(&*dist_3.pod).is_ok()); let bindings = request.exact_match_pod(&*dist_3.pod).unwrap(); - assert_eq!(*bindings.get("src").unwrap(), alice.public_key()); - assert_eq!(*bindings.get("dst").unwrap(), david.public_key()); + assert_eq!(*bindings.get("src").unwrap(), alice.public_key().into()); + assert_eq!(*bindings.get("dst").unwrap(), david.public_key().into()); assert_eq!(*bindings.get("distance").unwrap(), 3.into()); Ok(()) @@ -1014,25 +927,22 @@ pub mod tests { let params = Params::default(); let vd_set = &*MOCK_VD_SET; - let mut signed_builder = SignedPodBuilder::new(¶ms); + let mut signed_builder = SignedDictBuilder::new(¶ms); signed_builder.insert("a", 1); signed_builder.insert("b", 1); let signer = Signer(SecretKey(1u32.into())); - let signed_pod = signed_builder.sign(&signer).unwrap(); + let signed_dict = signed_builder.sign(&signer).unwrap(); let mut builder = MainPodBuilder::new(¶ms, vd_set); - builder.add_signed_pod(&signed_pod); - - //let op_val1 = Operation{ - // OperationType::Native(NativeOperation::CopyStatement), - // signed_pod. - //} + builder + .pub_op(Operation::dict_signed_by(&signed_dict)) + .unwrap(); let op_eq1 = Operation( OperationType::Native(NativeOperation::EqualFromEntries), vec![ - OperationArg::from((&signed_pod, "a")), - OperationArg::from((&signed_pod, "b")), + OperationArg::from((&signed_dict, "a")), + OperationArg::from((&signed_dict, "b")), ], OperationAux::None, ); @@ -1040,8 +950,8 @@ pub mod tests { let op_eq2 = Operation( OperationType::Native(NativeOperation::EqualFromEntries), vec![ - OperationArg::from((&signed_pod, "b")), - OperationArg::from((&signed_pod, "a")), + OperationArg::from((&signed_dict, "b")), + OperationArg::from((&signed_dict, "a")), ], OperationAux::None, ); @@ -1065,17 +975,21 @@ pub mod tests { fn test_false_st() { let params = Params::default(); let vd_set = &*MOCK_VD_SET; - let mut builder = SignedPodBuilder::new(¶ms); + let mut builder = SignedDictBuilder::new(¶ms); builder.insert("num", 2); let signer = Signer(SecretKey(1u32.into())); - let pod = builder.sign(&signer).unwrap(); + let signed_dict = builder.sign(&signer).unwrap(); - println!("{}", pod); + println!("{}", signed_dict); let mut builder = MainPodBuilder::new(¶ms, vd_set); - builder.add_signed_pod(&pod); - builder.pub_op(Operation::gt((&pod, "num"), 5)).unwrap(); + builder + .pub_op(Operation::dict_signed_by(&signed_dict)) + .unwrap(); + builder + .pub_op(Operation::gt((&signed_dict, "num"), 5)) + .unwrap(); let prover = MockProver {}; let false_pod = builder.prove(&prover).unwrap(); @@ -1088,26 +1002,28 @@ pub mod tests { fn test_dictionaries() -> Result<()> { let params = Params::default(); let vd_set = &*MOCK_VD_SET; - let mut builder = SignedPodBuilder::new(¶ms); + let mut builder = SignedDictBuilder::new(¶ms); - let mut my_dict_kvs: HashMap = HashMap::new(); - my_dict_kvs.insert(Key::from("a"), Value::from(1)); - my_dict_kvs.insert(Key::from("b"), Value::from(2)); - my_dict_kvs.insert(Key::from("c"), Value::from(3)); - // let my_dict_as_mt = MerkleTree::new(5, &my_dict_kvs).unwrap(); - // let dict = Dictionary { mt: my_dict_as_mt }; - let dict = Dictionary::new(params.max_depth_mt_containers, my_dict_kvs)?; + let dict = dict!(params.max_depth_mt_containers, { + "a" => 1, + "b" => 2, + "c" => 3, + })?; let dict_root = Value::from(dict.clone()); builder.insert("dict", dict_root); let signer = Signer(SecretKey(1u32.into())); - let pod = builder.sign(&signer).unwrap(); + let signed_dict = builder.sign(&signer).unwrap(); let mut builder = MainPodBuilder::new(¶ms, vd_set); - builder.add_signed_pod(&pod); - let st0 = pod.get_statement("dict").unwrap(); - let st1 = builder.op(true, Operation::new_entry("key", "a")).unwrap(); - let st2 = builder.literal(false, Value::from(1)).unwrap(); + builder + .pub_op(Operation::dict_signed_by(&signed_dict)) + .unwrap(); + let st0 = signed_dict.get_statement("dict").unwrap(); + let local = dict!(32, {"key" => "a"})?; + let st1 = builder + .op(true, Operation::dict_contains(local, "key", "a")) + .unwrap(); builder.pub_op(Operation( // OperationType @@ -1116,7 +1032,7 @@ pub mod tests { vec![ OperationArg::Statement(st0.clone()), OperationArg::Statement(st1), - OperationArg::Statement(st2), + OperationArg::Literal(Value::from(1)), ], OperationAux::MerkleProof(dict.prove(&Key::from("a")).unwrap().1), ))?; @@ -1259,25 +1175,35 @@ pub mod tests { let sk = SecretKey::new_rand(); let pk = sk.public_key(); - // Signed POD contains public key as owner - let mut builder = SignedPodBuilder::new(¶ms); + // Signed Dict contains public key as owner + let mut builder = SignedDictBuilder::new(¶ms); builder.insert("owner", Value::from(pk)); builder.insert("other_data", Value::from(123)); let signer = Signer(SecretKey(1u32.into())); - let signed_pod = builder.sign(&signer).unwrap(); + let signed_dict = builder.sign(&signer).unwrap(); // Main POD proves ownership of the owner's secret key. let mut builder = MainPodBuilder::new(¶ms, vd_set); - builder.add_signed_pod(&signed_pod); - let st0 = signed_pod.get_statement("owner").unwrap(); - let st1 = builder - .priv_op(Operation::new_entry("known_secret", Value::from(sk))) - .unwrap(); + builder.pub_op(Operation::signed_by( + Value::from(signed_dict.dict.clone()), + Value::from(signed_dict.public_key), + signed_dict.signature.clone(), + ))?; + + let st0 = builder.priv_op(Operation::dict_contains( + signed_dict.dict, + "owner", + Value::from(pk), + ))?; + let local = dict!(32, { "known_secret" => sk.clone() })?; + let st1 = builder.priv_op(Operation::dict_contains( + local, + "known_secret", + Value::from(sk), + ))?; builder .pub_op(Operation( - // OperationType OperationType::Native(NativeOperation::PublicKeyOf), - // Vec vec![OperationArg::Statement(st0), OperationArg::Statement(st1)], OperationAux::None, )) @@ -1301,22 +1227,25 @@ pub mod tests { let pk = sk.public_key(); // Signed POD contains public key as owner - let mut builder = SignedPodBuilder::new(¶ms); + let mut builder = SignedDictBuilder::new(¶ms); builder.insert("owner", Value::from(pk)); builder.insert("other_data", Value::from(123)); let signer = Signer(SecretKey(1u32.into())); - let signed_pod = builder.sign(&signer).unwrap(); + let signed_dict = builder.sign(&signer).unwrap(); // Try to build with the wrong secret key. The pre-proving checks // will catch this. let mut builder = MainPodBuilder::new(¶ms, vd_set); - builder.add_signed_pod(&signed_pod); - let st0 = signed_pod.get_statement("owner").unwrap(); + builder + .pub_op(Operation::dict_signed_by(&signed_dict)) + .unwrap(); + let st0 = signed_dict.get_statement("owner").unwrap(); + let local = dict!(32, {"known_secret" => SecretKey(BigUint::from(123u32))})?; let st1 = builder - .priv_op(Operation::new_entry( - "known_secret", - Value::from(SecretKey(BigUint::from(123u32))), - )) + .op( + true, + Operation::dict_contains(local, "known_secret", SecretKey(BigUint::from(123u32))), + ) .unwrap(); assert!(builder .pub_op(Operation( @@ -1341,33 +1270,30 @@ pub mod tests { // Try to build with wrong type in 1st arg let mut builder = MainPodBuilder::new(¶ms, vd_set); - let st_pk = builder.literal(false, Value::from(pk)).unwrap(); - let st_int1 = builder.literal(false, Value::from(123)).unwrap(); + let int2 = Value::from(123); + let sk = Value::from(sk); assert!(builder .pub_op(Operation( // OperationType OperationType::Native(NativeOperation::PublicKeyOf), // Vec - vec![ - OperationArg::Statement(st_pk), - OperationArg::Statement(st_int1), - ], + vec![OperationArg::Literal(int2), OperationArg::Literal(sk),], OperationAux::None, )) .is_err()); // Try to build with wrong type in 2nd arg - builder = MainPodBuilder::new(¶ms, vd_set); - let st_sk = builder.literal(false, Value::from(pk)).unwrap(); - let st_int2 = builder.literal(false, Value::from(123)).unwrap(); + let mut builder = MainPodBuilder::new(¶ms, vd_set); + let pk = Value::from(pk); + let int1 = Value::from(123); assert!(builder .pub_op(Operation( // OperationType OperationType::Native(NativeOperation::PublicKeyOf), // Vec vec![ - OperationArg::Statement(st_int2), - OperationArg::Statement(st_sk), + OperationArg::Literal(pk.clone()), + OperationArg::Literal(int1), ], OperationAux::None, )) @@ -1376,33 +1302,6 @@ pub mod tests { Ok(()) } - #[should_panic] - #[test] - fn test_reject_duplicate_new_entry() { - // try to insert the same key multiple times - // right now this is not caught when you build the pod, - // but it is caught on verify - env_logger::init(); - - let params = Params::default(); - let vd_set = &*MOCK_VD_SET; - let mut builder = MainPodBuilder::new(¶ms, vd_set); - let st = Statement::equal(AnchoredKey::from((SELF, "a")), Value::from(3)); - let op_new_entry = Operation( - OperationType::Native(NativeOperation::NewEntry), - vec![], - OperationAux::None, - ); - builder.insert(false, (st, op_new_entry.clone())).unwrap(); - - let st = Statement::equal(AnchoredKey::from((SELF, "a")), Value::from(28)); - builder.insert(false, (st, op_new_entry.clone())).unwrap(); - - let prover = MockProver {}; - let pod = builder.prove(&prover).unwrap(); - pod.pod.verify().unwrap(); - } - #[should_panic] #[test] fn test_reject_unsound_statement() { @@ -1411,23 +1310,25 @@ pub mod tests { let params = Params::default(); let vd_set = &*MOCK_VD_SET; let mut builder = MainPodBuilder::new(¶ms, vd_set); - let self_a = AnchoredKey::from((SELF, "a")); - let self_b = AnchoredKey::from((SELF, "b")); - let value_of_a = Statement::equal(self_a.clone(), Value::from(3)); - let value_of_b = Statement::equal(self_b.clone(), Value::from(27)); + let local = dict!(32, {"a" => 3, "b" => 27}).unwrap(); + let value_of_a = Statement::contains(local.clone(), "a", 3); + let value_of_b = Statement::contains(local.clone(), "b", 27); - let op_new_entry = Operation( - OperationType::Native(NativeOperation::NewEntry), + let op_contains = Operation( + OperationType::Native(NativeOperation::DictContainsFromEntries), vec![], OperationAux::None, ); builder - .insert(false, (value_of_a.clone(), op_new_entry.clone())) + .insert(false, (value_of_a.clone(), op_contains.clone())) .unwrap(); builder - .insert(false, (value_of_b.clone(), op_new_entry)) + .insert(false, (value_of_b.clone(), op_contains)) .unwrap(); - let st = Statement::equal(self_a, self_b); + let st = Statement::equal( + AnchoredKey::from((&local, "a")), + AnchoredKey::from((&local, "b")), + ); let op = Operation( OperationType::Native(NativeOperation::EqualFromEntries), vec![ diff --git a/src/frontend/operation.rs b/src/frontend/operation.rs index 967928a..a61623c 100644 --- a/src/frontend/operation.rs +++ b/src/frontend/operation.rs @@ -1,10 +1,10 @@ use std::fmt; use crate::{ - frontend::{MainPod, SignedPod}, + frontend::SignedDict, middleware::{ - AnchoredKey, CustomPredicateRef, NativeOperation, OperationAux, OperationType, Statement, - TypedValue, Value, ValueRef, + containers::Dictionary, root_key_to_ak, CustomPredicateRef, NativeOperation, OperationAux, + OperationType, Signature, Statement, TypedValue, Value, ValueRef, }, }; @@ -16,12 +16,12 @@ pub enum OperationArg { } impl OperationArg { - /// Extracts the value underlying literal and `ValueOf` statement + /// Extracts the value underlying literal and `Contains` statement /// operation args. pub(crate) fn value(&self) -> Option<&Value> { match self { Self::Literal(v) => Some(v), - Self::Statement(Statement::Equal(_, ValueRef::Literal(v))) => Some(v), + Self::Statement(Statement::Contains(_, _, ValueRef::Literal(v))) => Some(v), _ => None, } } @@ -29,7 +29,11 @@ impl OperationArg { pub(crate) fn value_and_ref(&self) -> Option<(ValueRef, &Value)> { match self { Self::Literal(v) => Some((ValueRef::Literal(v.clone()), v)), - Self::Statement(Statement::Equal(k, ValueRef::Literal(v))) => Some((k.clone(), v)), + Self::Statement(Statement::Contains( + ValueRef::Literal(root), + ValueRef::Literal(key), + ValueRef::Literal(v), + )) => root_key_to_ak(root, key).map(|ak| (ValueRef::Key(ak), v)), _ => None, } } @@ -64,27 +68,21 @@ impl From<&Value> for OperationArg { } } -impl From<(&SignedPod, &str)> for OperationArg { - fn from((pod, key): (&SignedPod, &str)) -> Self { - // TODO: TryFrom. - let value = pod - .kvs() - .get(&key.into()) - .cloned() - .unwrap_or_else(|| panic!("Key {} is not present in POD: {}", key, pod)); - Self::Statement(Statement::Equal( - AnchoredKey::from((pod.id(), key)).into(), +impl From<(&Dictionary, &str)> for OperationArg { + fn from((dict, key): (&Dictionary, &str)) -> Self { + // TODO: Use TryFrom + let value = dict.get(&key.into()).cloned().unwrap(); + Self::Statement(Statement::Contains( + dict.clone().into(), + key.into(), value.into(), )) } } -impl From<(&MainPod, &str)> for OperationArg { - fn from((pod, key): (&MainPod, &str)) -> Self { - // TODO: TryFrom. - let value = pod - .get(key) - .unwrap_or_else(|| panic!("Key {} is not present in POD: {}", key, pod)); - Self::Statement(Statement::equal(AnchoredKey::from((pod.id(), key)), value)) + +impl From<(&SignedDict, &str)> for OperationArg { + fn from((signed_dict, key): (&SignedDict, &str)) -> Self { + OperationArg::from((&signed_dict.dict, key)) } } @@ -186,13 +184,6 @@ macro_rules! op_impl_st { } impl Operation { - pub fn new_entry(a1: impl Into, a2: impl Into) -> Self { - Self( - OperationType::Native(NativeOperation::NewEntry), - vec![OperationArg::Entry(a1.into(), a2.into())], - OperationAux::None, - ) - } op_impl_oa!(eq, EqualFromEntries, 2); op_impl_oa!(ne, NotEqualFromEntries, 2); op_impl_oa!(gt_eq, GtEqFromEntries, 2); @@ -229,4 +220,22 @@ impl Operation { op_impl_oa!(set_insert, SetInsertFromEntries, 3); op_impl_oa!(set_delete, SetDeleteFromEntries, 3); op_impl_oa!(array_update, ArrayUpdateFromEntries, 4); + pub fn signed_by( + msg: impl Into, + pk: impl Into, + sig: Signature, + ) -> Self { + Self( + OperationType::Native(NativeOperation::SignedBy), + vec![msg.into(), pk.into()], + OperationAux::Signature(sig), + ) + } + pub fn dict_signed_by(signed_dict: &SignedDict) -> Self { + Self::signed_by( + Value::from(signed_dict.dict.clone()), + Value::from(signed_dict.public_key), + signed_dict.signature.clone(), + ) + } } diff --git a/src/frontend/pod_request.rs b/src/frontend/pod_request.rs index 770b6b3..c057c2b 100644 --- a/src/frontend/pod_request.rs +++ b/src/frontend/pod_request.rs @@ -128,8 +128,8 @@ impl PodRequest { } // Try to bind wildcard to the POD ID - let pod_id_value = Value::from(stmt_key.pod_id); - self.try_bind_wildcard(&wildcard.name, pod_id_value, current_bindings, new_bindings) + let root_value = Value::from(stmt_key.root); + self.try_bind_wildcard(&wildcard.name, root_value, current_bindings, new_bindings) } // Other combinations don't match @@ -176,12 +176,14 @@ impl Display for PodRequest { mod tests { use crate::{ backends::plonky2::{ - mock::mainpod::MockProver, primitives::ec::schnorr::SecretKey, signedpod::Signer, + mock::mainpod::MockProver, primitives::ec::schnorr::SecretKey, signer::Signer, + }, + examples::{ + zu_kyc_pod_builder, zu_kyc_pod_request, zu_kyc_sign_dict_builders, MOCK_VD_SET, }, - examples::{zu_kyc_pod_builder, zu_kyc_pod_request, zu_kyc_sign_pod_builders, MOCK_VD_SET}, frontend::{MainPodBuilder, Operation}, lang::parse, - middleware::Params, + middleware::{Params, Value}, }; #[test] @@ -189,7 +191,7 @@ mod tests { let params = Params::default(); let vd_set = &*MOCK_VD_SET; - let (gov_id, pay_stub) = zu_kyc_sign_pod_builders(¶ms); + let (gov_id, pay_stub) = zu_kyc_sign_dict_builders(¶ms); let gov_id = gov_id.sign(&Signer(SecretKey(1u32.into()))).unwrap(); let pay_stub = pay_stub.sign(&Signer(SecretKey(2u32.into()))).unwrap(); let builder = zu_kyc_pod_builder(&Params::default(), vd_set, &gov_id, &pay_stub).unwrap(); @@ -198,8 +200,8 @@ mod tests { // This request matches the POD let request = zu_kyc_pod_request( - gov_id.get("_signer").unwrap(), - pay_stub.get("_signer").unwrap(), + &Value::from(gov_id.public_key), + &Value::from(pay_stub.public_key), ) .unwrap(); assert!(request.exact_match_pod(&*kyc.pod).is_ok()); diff --git a/src/frontend/serialization.rs b/src/frontend/serialization.rs index dd141f9..8bcc39b 100644 --- a/src/frontend/serialization.rs +++ b/src/frontend/serialization.rs @@ -1,73 +1,30 @@ -use std::collections::HashMap; - use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use super::Error; use crate::{ - frontend::{MainPod, SignedPod}, - middleware::{ - deserialize_pod, deserialize_signed_pod, Key, Params, PodId, Statement, VDSet, Value, - }, + frontend::MainPod, + middleware::{deserialize_pod, Hash, Params, Statement, VDSet}, }; -#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)] -#[serde(rename_all = "camelCase")] -#[schemars(rename = "SignedPod")] -pub struct SerializedSignedPod { - pod_type: (usize, String), - id: PodId, - entries: HashMap, - data: serde_json::Value, -} - -impl SerializedSignedPod { - pub fn id(&self) -> PodId { - self.id - } -} - #[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] #[schemars(rename = "MainPod")] pub struct SerializedMainPod { params: Params, pod_type: (usize, String), - id: PodId, + id: Hash, vd_set: VDSet, public_statements: Vec, data: serde_json::Value, } impl SerializedMainPod { - pub fn id(&self) -> PodId { + pub fn id(&self) -> Hash { self.id } } -impl From for SerializedSignedPod { - fn from(pod: SignedPod) -> Self { - let (pod_type, pod_type_name_str) = pod.pod.pod_type(); - let data = pod.pod.serialize_data(); - SerializedSignedPod { - pod_type: (pod_type, pod_type_name_str.to_string()), - id: pod.id(), - entries: pod.kvs().clone(), - data, - } - } -} - -impl TryFrom for SignedPod { - type Error = Error; - - fn try_from(serialized: SerializedSignedPod) -> Result { - let pod = deserialize_signed_pod(serialized.pod_type.0, serialized.id, serialized.data)?; - let kvs = pod.kvs().into_iter().map(|(ak, v)| (ak.key, v)).collect(); - Ok(Self { pod, kvs }) - } -} - impl From for SerializedMainPod { fn from(pod: MainPod) -> Self { let (pod_type, pod_type_name_str) = pod.pod.pod_type(); @@ -116,17 +73,17 @@ mod tests { mainpod::{rec_main_pod_circuit_data, Prover}, mock::mainpod::MockProver, primitives::ec::schnorr::SecretKey, - signedpod::Signer, + signer::Signer, }, examples::{ - attest_eth_friend, zu_kyc_pod_builder, zu_kyc_sign_pod_builders, EthDosHelper, + attest_eth_friend, zu_kyc_pod_builder, zu_kyc_sign_dict_builders, EthDosHelper, MOCK_VD_SET, }, - frontend::{Result, SignedPodBuilder}, + frontend::{Result, SignedDict, SignedDictBuilder}, middleware::{ self, containers::{Array, Dictionary, Set}, - Params, TypedValue, DEFAULT_VD_LIST, + Params, Signer as _, TypedValue, DEFAULT_VD_LIST, }, }; @@ -182,9 +139,9 @@ mod tests { } } - fn signed_pod_builder() -> SignedPodBuilder { + fn signed_dict_builder() -> SignedDictBuilder { let params = &Params::default(); - let mut builder = SignedPodBuilder::new(params); + let mut builder = SignedDictBuilder::new(params); builder.insert("name", "test"); builder.insert("age", 30); builder.insert("very_large_int", 1152921504606846976); @@ -228,46 +185,29 @@ mod tests { } #[test] - fn test_signed_pod_serialization() { - let builder = signed_pod_builder(); + fn test_signed_dict_serialization() { + let builder = signed_dict_builder(); let signer = Signer(SecretKey(1u32.into())); - let pod = builder.sign(&signer).unwrap(); + let signed_dict = builder.sign(&signer).unwrap(); - let serialized = serde_json::to_string_pretty(&pod).unwrap(); + let serialized = serde_json::to_string_pretty(&signed_dict).unwrap(); println!("serialized: {}", serialized); - let deserialized: SignedPod = serde_json::from_str(&serialized).unwrap(); + let deserialized: SignedDict = serde_json::from_str(&serialized).unwrap(); println!( "deserialized: {}", serde_json::to_string_pretty(&deserialized).unwrap() ); - assert_eq!(pod.kvs, deserialized.kvs); - assert_eq!(pod.verify().is_ok(), deserialized.verify().is_ok()); - assert_eq!(pod.id(), deserialized.id()) - } - - #[test] - fn test_mock_signed_pod_serialization() { - let builder = signed_pod_builder(); - let signer = Signer(SecretKey(1u32.into())); - let pod = builder.sign(&signer).unwrap(); - - let serialized = serde_json::to_string_pretty(&pod).unwrap(); - println!("serialized: {}", serialized); - let deserialized: SignedPod = serde_json::from_str(&serialized).unwrap(); - println!( - "deserialized: {}", - serde_json::to_string_pretty(&deserialized).unwrap() - ); - assert_eq!(pod.kvs, deserialized.kvs); - assert_eq!(pod.verify().is_ok(), deserialized.verify().is_ok()); - assert_eq!(pod.id(), deserialized.id()) + assert_eq!(signed_dict.dict.kvs(), deserialized.dict.kvs()); + assert_eq!(signed_dict.public_key, deserialized.public_key); + assert_eq!(signed_dict.signature, deserialized.signature); + assert_eq!(signed_dict.verify().is_ok(), deserialized.verify().is_ok()); } fn build_mock_zukyc_pod() -> Result { let params = middleware::Params::default(); let vd_set = &*MOCK_VD_SET; - let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(¶ms); + let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_dict_builders(¶ms); let signer = Signer(SecretKey(1u32.into())); let gov_id_pod = gov_id_builder.sign(&signer).unwrap(); let signer = Signer(SecretKey(2u32.into())); @@ -283,18 +223,19 @@ mod tests { let params = middleware::Params { // Currently the circuit uses random access that only supports vectors of length 64. // With max_input_main_pods=3 we need random access to a vector of length 73. - max_input_recursive_pods: 1, + max_input_pods: 1, ..Default::default() }; let mut vds = DEFAULT_VD_LIST.clone(); vds.push(rec_main_pod_circuit_data(¶ms).1.verifier_only.clone()); let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap(); - let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(¶ms); + let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_dict_builders(¶ms); let signer = Signer(SecretKey(1u32.into())); let gov_id_pod = gov_id_builder.sign(&signer)?; let signer = Signer(SecretKey(2u32.into())); let pay_stub_pod = pay_stub_builder.sign(&signer)?; + let _signer = Signer(SecretKey(3u32.into())); let kyc_builder = zu_kyc_pod_builder(¶ms, &vd_set, &gov_id_pod, &pay_stub_pod)?; let prover = Prover {}; @@ -311,7 +252,10 @@ mod tests { let deserialized: MainPod = serde_json::from_str(&serialized).unwrap(); assert_eq!(kyc_pod.public_statements, deserialized.public_statements); - assert_eq!(kyc_pod.pod.id(), deserialized.pod.id()); + assert_eq!( + kyc_pod.pod.statements_hash(), + deserialized.pod.statements_hash() + ); assert_eq!(kyc_pod.pod.verify()?, deserialized.pod.verify()?); Ok(()) @@ -324,7 +268,10 @@ mod tests { let deserialized: MainPod = serde_json::from_str(&serialized).unwrap(); assert_eq!(kyc_pod.public_statements, deserialized.public_statements); - assert_eq!(kyc_pod.pod.id(), deserialized.pod.id()); + assert_eq!( + kyc_pod.pod.statements_hash(), + deserialized.pod.statements_hash() + ); assert_eq!(kyc_pod.pod.verify()?, deserialized.pod.verify()?); Ok(()) @@ -348,7 +295,7 @@ mod tests { let alice_attestation = attest_eth_friend(¶ms, &alice, bob.public_key()); let bob_attestation = attest_eth_friend(¶ms, &bob, charlie.public_key()); - let helper = EthDosHelper::new(¶ms, vd_set, true, alice.public_key())?; + let helper = EthDosHelper::new(¶ms, vd_set, alice.public_key())?; let prover = MockProver {}; let dist_1 = helper.dist_1(&alice_attestation)?.prove(&prover)?; let dist_2 = helper @@ -360,28 +307,28 @@ mod tests { #[test] // This tests that we can generate JSON Schemas for the MainPod and - // SignedPod types, and that we can validate Signed and Main Pods + // SignedDict types, and that we can validate Signed and Main Pods // against the schemas. Since both Mock and Plonky2 PODs have the same // public interface, we can assume that the schema works for both. fn test_schema() { let mainpod_schema = schema_for!(SerializedMainPod); - let signedpod_schema = schema_for!(SerializedSignedPod); + let signeddict_schema = schema_for!(SignedDict); let kyc_pod = build_mock_zukyc_pod().unwrap(); - let signed_pod = signed_pod_builder() + let signed_dict = signed_dict_builder() .sign(&Signer(SecretKey(1u32.into()))) .unwrap(); let ethdos_pod = build_ethdos_pod().unwrap(); let mainpod_schema_value = serde_json::to_value(&mainpod_schema).unwrap(); - let signedpod_schema_value = serde_json::to_value(&signedpod_schema).unwrap(); + let signed_dict_schema_value = serde_json::to_value(&signeddict_schema).unwrap(); let kyc_pod_value = serde_json::to_value(&kyc_pod).unwrap(); let mainpod_valid = jsonschema::validate(&mainpod_schema_value, &kyc_pod_value); assert!(mainpod_valid.is_ok(), "{:#?}", mainpod_valid); - let signed_pod_value = serde_json::to_value(&signed_pod).unwrap(); - let signedpod_valid = jsonschema::validate(&signedpod_schema_value, &signed_pod_value); - assert!(signedpod_valid.is_ok(), "{:#?}", signedpod_valid); + let signed_dict_value = serde_json::to_value(&signed_dict).unwrap(); + let signed_dict_valid = jsonschema::validate(&signed_dict_schema_value, &signed_dict_value); + assert!(signed_dict_valid.is_ok(), "{:#?}", signed_dict_valid); let ethdos_pod_value = serde_json::to_value(ðdos_pod).unwrap(); let ethdos_pod_valid = jsonschema::validate(&mainpod_schema_value, ðdos_pod_value); diff --git a/src/lang/grammar.pest b/src/lang/grammar.pest index 2eeab0b..1dbf6b4 100644 --- a/src/lang/grammar.pest +++ b/src/lang/grammar.pest @@ -15,8 +15,6 @@ identifier = @{ !("private") ~ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* private_kw = { "private:" } -self_keyword = @{ "SELF" } - // Define wildcard names (start with '?') wildcard = @{ "?" ~ identifier } @@ -66,10 +64,8 @@ literal_value = { literal_array | literal_bool | literal_raw | - literal_pod_id | literal_string | - literal_int | - self_keyword + literal_int } // Primitive literal types @@ -81,7 +77,6 @@ literal_bool = @{ "true" | "false" } hash_hex = @{ "0x" ~ (ASCII_HEX_DIGIT ~ ASCII_HEX_DIGIT){32} } literal_raw = { "Raw" ~ "(" ~ hash_hex ~ ")" } -literal_pod_id = { hash_hex } // String literal parsing based on https://pest.rs/book/examples/json.html literal_string = ${ "\"" ~ inner ~ "\"" } // Compound atomic string rule @@ -114,7 +109,6 @@ test_wildcard = { SOI ~ wildcard ~ EOI } test_literal_int = { SOI ~ literal_int ~ EOI } test_hash_hex = { SOI ~ hash_hex ~ EOI } test_literal_raw = { SOI ~ literal_raw ~ EOI } -test_literal_pod_id = { SOI ~ literal_pod_id ~ EOI } test_literal_value = { SOI ~ literal_value ~ EOI } test_statement = { SOI ~ statement ~ EOI } test_custom_predicate_def = { SOI ~ custom_predicate_def ~ EOI } diff --git a/src/lang/mod.rs b/src/lang/mod.rs index 05abce4..b97646a 100644 --- a/src/lang/mod.rs +++ b/src/lang/mod.rs @@ -32,9 +32,8 @@ mod tests { backends::plonky2::primitives::ec::schnorr::SecretKey, lang::error::ProcessorError, middleware::{ - hash_str, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, Key, - NativePredicate, Params, PodId, PodType, Predicate, RawValue, StatementTmpl, - StatementTmplArg, Value, Wildcard, KEY_SIGNER, KEY_TYPE, SELF, + CustomPredicate, CustomPredicateBatch, CustomPredicateRef, Key, NativePredicate, + Params, Predicate, RawValue, StatementTmpl, StatementTmplArg, Value, Wildcard, }, }; @@ -107,7 +106,7 @@ mod tests { fn test_e2e_simple_request() -> Result<(), LangError> { let input = r#" REQUEST( - Equal(?ConstPod["my_val"], 0x0000000000000000000000000000000000000000000000000000000000000001) + Equal(?ConstPod["my_val"], Raw(0x0000000000000000000000000000000000000000000000000000000000000001)) Lt(?GovPod["dob"], ?ConstPod["my_val"]) ) "#; @@ -482,10 +481,8 @@ mod tests { #[test] fn test_e2e_ethdos_predicates() -> Result<(), LangError> { let params = Params { - max_input_signed_pods: 3, - max_input_recursive_pods: 3, + max_input_pods: 3, max_statements: 31, - max_signed_pod_values: 8, max_public_statements: 10, max_statement_args: 6, max_operation_args: 5, @@ -496,10 +493,9 @@ mod tests { }; let input = r#" - eth_friend(src, dst, private: attestation_pod) = AND( - Equal(?attestation_pod["_type"], 1) - Equal(?attestation_pod["_signer"], ?src) - Equal(?attestation_pod["attestation"], ?dst) + eth_friend(src, dst, private: attestation_dict) = AND( + SignedBy(?attestation_dict, ?src) + Equal(?attestation_dict["attestation"], ?dst) ) eth_dos_distance_base(src, dst, distance) = AND( @@ -536,23 +532,13 @@ mod tests { // eth_friend (Index 0) let expected_friend_stmts = vec![ StatementTmpl { - pred: Predicate::Native(NativePredicate::Equal), - args: vec![ - sta_ak(("attestation_pod", 2), "_type"), // Pub(0-1), Priv(2) - sta_lit(PodType::Signed), - ], + pred: Predicate::Native(NativePredicate::SignedBy), + args: vec![sta_wc_lit("attestation_dict", 2), sta_wc_lit("src", 0)], }, StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), args: vec![ - sta_ak(("attestation_pod", 2), "_signer"), - sta_wc_lit("src", 0), // Pub arg 0 - ], - }, - StatementTmpl { - pred: Predicate::Native(NativePredicate::Equal), - args: vec![ - sta_ak(("attestation_pod", 2), "attestation"), + sta_ak(("attestation_dict", 2), "attestation"), sta_wc_lit("dst", 1), // Pub arg 1 ], }, @@ -563,7 +549,7 @@ mod tests { true, // AND expected_friend_stmts, 2, // public_args_len: src, dst - names(&["src", "dst", "attestation_pod"]), + names(&["src", "dst", "attestation_dict"]), )?; // eth_dos_distance_base (Index 1) @@ -853,7 +839,6 @@ mod tests { #[test] fn test_e2e_literals() -> Result<(), LangError> { let pk = crate::backends::plonky2::primitives::ec::curve::Point::generator(); - let pod_id = PodId(hash_str("test")); let raw = RawValue::from(1); let string = "hello"; let int = 123; @@ -864,17 +849,14 @@ mod tests { r#" REQUEST( Equal(?A["pk"], {}) - Equal(?B["pod_id"], {}) - Equal(?C["raw"], {}) - Equal(?D["string"], {}) - Equal(?E["int"], {}) - Equal(?F["bool"], {}) - Equal(?G["sk"], {}) - Equal(?H["self"], SELF) + Equal(?B["raw"], {}) + Equal(?C["string"], {}) + Equal(?D["int"], {}) + Equal(?E["bool"], {}) + Equal(?F["sk"], {}) ) "#, Value::from(pk).to_podlang_string(), - Value::from(pod_id).to_podlang_string(), Value::from(raw).to_podlang_string(), Value::from(string).to_podlang_string(), Value::from(int).to_podlang_string(), @@ -884,7 +866,6 @@ mod tests { /* REQUEST( Equal(?A["pk"], PublicKey(3t9fNuU194n7mSJPRdeaJRMqw6ZQCUddzvECWNe1k2b1rdBezXpJxF)) - Equal(?B["pod_id"], 0x735b31d3aad0f5b66002ffe1dc7d2eaa0ee9c59c09b641e8261530c5f3a02f29) Equal(?C["raw"], Raw(0x0000000000000000000000000000000000000000000000000000000000000001)) Equal(?D["string"], "hello") Equal(?E["int"], 123) @@ -905,31 +886,23 @@ mod tests { }, StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), - args: vec![sta_ak(("B", 1), "pod_id"), sta_lit(Value::from(pod_id))], + args: vec![sta_ak(("B", 1), "raw"), sta_lit(Value::from(raw))], }, StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), - args: vec![sta_ak(("C", 2), "raw"), sta_lit(Value::from(raw))], + args: vec![sta_ak(("C", 2), "string"), sta_lit(Value::from(string))], }, StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), - args: vec![sta_ak(("D", 3), "string"), sta_lit(Value::from(string))], + args: vec![sta_ak(("D", 3), "int"), sta_lit(Value::from(int))], }, StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), - args: vec![sta_ak(("E", 4), "int"), sta_lit(Value::from(int))], + args: vec![sta_ak(("E", 4), "bool"), sta_lit(Value::from(bool))], }, StatementTmpl { pred: Predicate::Native(NativePredicate::Equal), - args: vec![sta_ak(("F", 5), "bool"), sta_lit(Value::from(bool))], - }, - StatementTmpl { - pred: Predicate::Native(NativePredicate::Equal), - args: vec![sta_ak(("G", 6), "sk"), sta_lit(Value::from(sk))], - }, - StatementTmpl { - pred: Predicate::Native(NativePredicate::Equal), - args: vec![sta_ak(("H", 7), "self"), sta_lit(Value::from(SELF))], + args: vec![sta_ak(("F", 5), "sk"), sta_lit(Value::from(sk))], }, ]; @@ -972,21 +945,13 @@ mod tests { let params = Params::default(); let available_batches = &[]; - let input = format!( - r#" - identity_verified(username, private: identity_pod) = AND( - Equal(?identity_pod["{key_type}"], {signed_pod_type}) - Equal(?identity_pod["{key_signer}"], {identity_server_pk}) - Equal(?identity_pod["username"], ?username) - Equal(?identity_pod["user_public_key"], ?user_public_key) + let input = r#" + identity_verified(username, private: identity_dict) = AND( + Equal(?identity_dict["username"], ?username) + Equal(?identity_dict["user_public_key"], ?user_public_key) ) - "#, - key_type = KEY_TYPE, - signed_pod_type = PodType::Signed as u32, - key_signer = KEY_SIGNER, - identity_server_pk = - "0x0000000000000000000000000000000000000000000000000000000000000000" - ); + "# + .to_string(); let result = parse(&input, ¶ms, available_batches); diff --git a/src/lang/parser.rs b/src/lang/parser.rs index bdbf535..0fc4fd4 100644 --- a/src/lang/parser.rs +++ b/src/lang/parser.rs @@ -117,7 +117,7 @@ mod tests { // Use anchored rule for failure cases assert_fails( Rule::test_literal_raw, - "0x0000000000000000000000000000000000000000000000000000000000000000)", + "0x0000000000000000000000000000000000000000000000000000000000000000", ); // Missing Raw() wrapper assert_fails(Rule::test_literal_raw, "Raw(0xabc)"); // Fails (string is too short) assert_fails(Rule::test_literal_raw, "Raw(0x)"); // Fails (needs at least one pair) @@ -126,22 +126,6 @@ mod tests { &format!("Raw(0x{})", "a".repeat(66)), ); // Fails (string is too long) - // PodId (essentially identical to Raw but without the wrapper) - assert_parses( - Rule::literal_pod_id, - "0x0000000000000000000000000000000000000000000000000000000000000000", - ); - assert_parses( - Rule::literal_pod_id, - "0xabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd", - ); - let long_valid_pod_id = format!("0x{}", "a".repeat(64)); - assert_parses(Rule::literal_pod_id, &long_valid_pod_id); - - assert_fails(Rule::test_literal_pod_id, "0xabc"); // Fails (string is too short) - assert_fails(Rule::test_literal_pod_id, "0x"); // Fails (needs at least one pair) - assert_fails(Rule::test_literal_pod_id, &format!("0x{}", "a".repeat(66))); // Fails (string is too long) - // String assert_parses(Rule::literal_string, "\"hello\""); assert_parses(Rule::literal_string, "\"escaped \\\" quote\""); @@ -163,7 +147,7 @@ mod tests { assert_parses(Rule::literal_set, "#[1, 2, 3]"); assert_parses( Rule::literal_set, - "#[ \"a\", 0x0000000000000000000000000000000000000000000000000000000000000000 ]", + "#[ \"a\", Raw(0x0000000000000000000000000000000000000000000000000000000000000000) ]", ); // Dict @@ -172,7 +156,7 @@ mod tests { assert_parses(Rule::literal_dict, "{ \"nested\": { \"key\": 1 } }"); assert_parses( Rule::literal_dict, - "{ \"raw_val\": 0x0000000000000000000000000000000000000000000000000000000000000000 } ", + "{ \"raw_val\": Raw(0x0000000000000000000000000000000000000000000000000000000000000000) } ", ); assert_fails(Rule::literal_dict, "{ name: \"Alice\" }"); // Key must be string literal with quotes } diff --git a/src/lang/pretty_print.rs b/src/lang/pretty_print.rs index 6aeadd6..6a3a800 100644 --- a/src/lang/pretty_print.rs +++ b/src/lang/pretty_print.rs @@ -64,6 +64,9 @@ impl StatementTmpl { Predicate::Custom(custom_ref) => { write!(w, "{}", custom_ref.predicate().name)?; } + Predicate::Intro(intro_ref) => { + write!(w, "{}", intro_ref.name)?; + } Predicate::BatchSelf(index) => { if let Some(batch) = batch_context { if let Some(predicate) = batch.predicates.get(*index) { @@ -523,16 +526,6 @@ mod tests { assert_round_trip(&input); } - #[test] - fn test_round_trip_self() { - let input = r#" - self_test(Pod) = AND( - Equal(?Pod["self"], SELF) - ) - "#; - assert_round_trip(input); - } - #[test] fn test_pretty_print_demonstration() { let input = r#" diff --git a/src/lang/processor.rs b/src/lang/processor.rs index c976c94..1d36039 100644 --- a/src/lang/processor.rs +++ b/src/lang/processor.rs @@ -43,6 +43,7 @@ pub fn native_predicate_from_string(s: &str) -> Option { "MaxOf" => Some(NativePredicate::MaxOf), "HashOf" => Some(NativePredicate::HashOf), "PublicKeyOf" => Some(NativePredicate::PublicKeyOf), + "SignedBy" => Some(NativePredicate::SignedBy), "DictContains" => Some(NativePredicate::DictContains), "DictNotContains" => Some(NativePredicate::DictNotContains), "ArrayContains" => Some(NativePredicate::ArrayContains), @@ -328,17 +329,17 @@ fn pest_pair_to_builder_arg( } Rule::anchored_key => { let mut inner_ak_pairs = arg_content_pair.clone().into_inner(); - let pod_id_pair = inner_ak_pairs.next().unwrap(); - let pod_id_wc_str = pod_id_pair.as_str().strip_prefix("?").unwrap(); + let root_pair = inner_ak_pairs.next().unwrap(); + let root_wc_str = root_pair.as_str().strip_prefix("?").unwrap(); if let StatementContext::CustomPredicate { argument_names, pred_name, } = context { - if !argument_names.contains(pod_id_wc_str) { + if !argument_names.contains(root_wc_str) { return Err(ProcessorError::UndefinedWildcard { - name: pod_id_wc_str.to_string(), + name: root_wc_str.to_string(), pred_name: pred_name.to_string(), span: Some(get_span(arg_content_pair)), }); @@ -347,12 +348,44 @@ fn pest_pair_to_builder_arg( let key_part_pair = inner_ak_pairs.next().unwrap(); let key_str = parse_pest_string_literal(&key_part_pair)?; - Ok(BuilderArg::Key(pod_id_wc_str.to_string(), key_str)) + Ok(BuilderArg::Key(root_wc_str.to_string(), key_str)) } _ => unreachable!("Unexpected rule: {:?}", arg_content_pair.as_rule()), } } +fn validate_dyn_len_predicate( + stmt_name_str: &str, + args: &[BuilderArg], + expected_arity: usize, + stmt_span: (usize, usize), + stmt_name_span: (usize, usize), +) -> Result<(), ProcessorError> { + if args.len() != expected_arity { + return Err(ProcessorError::ArgumentCountMismatch { + predicate: stmt_name_str.to_string(), + expected: expected_arity, + found: args.len(), + span: Some(stmt_name_span), + }); + } + for (idx, arg) in args.iter().enumerate() { + if !matches!(arg, BuilderArg::WildcardLiteral(_) | BuilderArg::Literal(_)) { + return Err(ProcessorError::TypeError { + expected: "Wildcard or Literal".to_string(), + found: format!("{:?}", arg), + item: format!( + "argument {} of custom predicate call '{}'", + idx + 1, + stmt_name_str + ), + span: Some(stmt_span), + }); + } + } + Ok(()) +} + fn validate_and_build_statement_template( stmt_name_str: &str, pred: &Predicate, @@ -374,7 +407,8 @@ fn validate_and_build_statement_template( | NativePredicate::DictNotContains | NativePredicate::SetNotContains | NativePredicate::NotContains - | NativePredicate::PublicKeyOf => 2, + | NativePredicate::PublicKeyOf + | NativePredicate::SignedBy => 2, NativePredicate::Contains | NativePredicate::ArrayContains | NativePredicate::DictContains @@ -405,28 +439,23 @@ fn validate_and_build_statement_template( } Predicate::Custom(custom_ref) => { let expected_arity = custom_ref.predicate().args_len; - if args.len() != expected_arity { - return Err(ProcessorError::ArgumentCountMismatch { - predicate: stmt_name_str.to_string(), - expected: expected_arity, - found: args.len(), - span: Some(stmt_name_span), - }); - } - for (idx, arg) in args.iter().enumerate() { - if !matches!(arg, BuilderArg::WildcardLiteral(_) | BuilderArg::Literal(_)) { - return Err(ProcessorError::TypeError { - expected: "Wildcard or Literal".to_string(), - found: format!("{:?}", arg), - item: format!( - "argument {} of custom predicate call '{}'", - idx + 1, - stmt_name_str - ), - span: Some(stmt_span), - }); - } - } + validate_dyn_len_predicate( + stmt_name_str, + &args, + expected_arity, + stmt_span, + stmt_name_span, + )?; + } + Predicate::Intro(intro_ref) => { + let expected_arity = intro_ref.args_len; + validate_dyn_len_predicate( + stmt_name_str, + &args, + expected_arity, + stmt_span, + stmt_name_span, + )?; } Predicate::BatchSelf(_) => { let (_original_pred_idx, expected_arity_val) = processing_ctx @@ -650,8 +679,8 @@ fn process_statement_template( for arg in &builder_args { match arg { BuilderArg::WildcardLiteral(name) => temp_stmt_wildcard_names.push(name.clone()), - BuilderArg::Key(pod_id_wc_str, _key_str) => { - temp_stmt_wildcard_names.push(pod_id_wc_str.clone()); + BuilderArg::Key(root_wc_str, _key_str) => { + temp_stmt_wildcard_names.push(root_wc_str.clone()); } _ => {} } @@ -742,14 +771,6 @@ fn process_literal_value( }) .map(Value::from) } - Rule::literal_pod_id => { - let hex_str_no_prefix = inner_lit - .as_str() - .strip_prefix("0x") - .unwrap_or(inner_lit.as_str()); - let pod_id = parse_hex_str_to_pod_id(hex_str_no_prefix)?; - Ok(Value::from(pod_id)) - } Rule::literal_public_key => { let pk_str_pair = inner_lit.into_inner().next().unwrap(); let pk_b58 = pk_str_pair.as_str(); @@ -826,7 +847,6 @@ fn process_literal_value( })?; Ok(Value::from(secret_key)) } - Rule::self_keyword => Ok(Value::from(middleware::SELF)), _ => unreachable!("Unexpected rule: {:?}", inner_lit.as_rule()), } } @@ -912,11 +932,6 @@ fn parse_hex_str_to_raw_value(hex_str: &str) -> Result Result { - let raw = parse_hex_str_to_raw_value(hex_str)?; - Ok(middleware::PodId(raw.into())) -} - // Helper to resolve a wildcard name string to an indexed middleware::Wildcard // based on an ordered list of names from the current scope (e.g., request or predicate def). fn resolve_wildcard( @@ -945,10 +960,10 @@ fn resolve_request_statement_builder( for builder_arg in stb.args { let mw_arg = match builder_arg { BuilderArg::Literal(v) => StatementTmplArg::Literal(v), - BuilderArg::Key(pod_id_wc_str, key_str) => { - let pod_id_wc = resolve_wildcard(ordered_request_wildcard_names, &pod_id_wc_str)?; + BuilderArg::Key(root_wc_str, key_str) => { + let root_wc = resolve_wildcard(ordered_request_wildcard_names, &root_wc_str)?; let key = Key::from(key_str); - StatementTmplArg::AnchoredKey(pod_id_wc, key) + StatementTmplArg::AnchoredKey(root_wc, key) } BuilderArg::WildcardLiteral(wc_name) => { let wc = resolve_wildcard(ordered_request_wildcard_names, &wc_name)?; diff --git a/src/middleware/containers.rs b/src/middleware/containers.rs index 71ee6c1..7fb3e96 100644 --- a/src/middleware/containers.rs +++ b/src/middleware/containers.rs @@ -27,6 +27,18 @@ pub struct Dictionary { kvs: HashMap, } +#[macro_export] +macro_rules! dict { + ($max_depth:expr, { $($key:expr => $val:expr),* , }) => ( + $crate::dict!($max_depth, { $($key => $val),* }) + ); + ($max_depth:expr, { $($key:expr => $val:expr),* }) => ({ + let mut map = ::std::collections::HashMap::new(); + $( map.insert($crate::middleware::Key::from($key), $crate::middleware::Value::from($val)); )* + $crate::middleware::containers::Dictionary::new($max_depth, map) + }); +} + impl Dictionary { /// max_depth determines the depth of the underlying MerkleTree, allowing to /// store 2^max_depth elements in the Dictionary diff --git a/src/middleware/custom.rs b/src/middleware/custom.rs index 123f4d6..9b2efc1 100644 --- a/src/middleware/custom.rs +++ b/src/middleware/custom.rs @@ -33,7 +33,7 @@ impl fmt::Display for Wildcard { impl ToFields for Wildcard { fn to_fields(&self, _params: &Params) -> Vec { - vec![F::from_canonical_u64(self.index as u64 + 1)] + vec![F::from_canonical_u64(self.index as u64)] } } @@ -110,8 +110,8 @@ impl fmt::Display for StatementTmplArg { match self { Self::None => write!(f, "none"), Self::Literal(v) => v.fmt(f), - Self::AnchoredKey(pod_id, key) => { - pod_id.fmt(f)?; + Self::AnchoredKey(root, key) => { + root.fmt(f)?; write!(f, "[")?; key.fmt(f)?; write!(f, "]") @@ -451,10 +451,13 @@ impl CustomPredicateRef { #[cfg(test)] mod tests { use super::*; - use crate::middleware::{ - AnchoredKey, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, Key, - NativePredicate, Operation, Params, PodType, Predicate, Statement, StatementTmpl, - StatementTmplArg, SELF, + use crate::{ + dict, + middleware::{ + AnchoredKey, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, Key, + NativePredicate, Operation, Params, Predicate, Statement, StatementTmpl, + StatementTmplArg, + }, }; fn st(p: Predicate, args: Vec) -> StatementTmpl { @@ -513,18 +516,25 @@ mod tests { )?], ); + let d0 = dict!(32, { + "a" => 10, + })?; + let d1 = dict!(32, { + "b" => 15, + "c" => 17, + })?; let custom_statement = Statement::Custom( CustomPredicateRef::new(cust_pred_batch.clone(), 0), - vec![Value::from(SELF)], + vec![Value::from(d0.clone())], ); let custom_deduction = Operation::Custom( CustomPredicateRef::new(cust_pred_batch, 0), vec![ - Statement::equal(AnchoredKey::from((SELF, "c")), 2), + Statement::equal(AnchoredKey::from((&d1, "c")), 2), Statement::product_of( - AnchoredKey::from((SELF, "a")), - AnchoredKey::from((SELF, "b")), + AnchoredKey::from((&d0, "a")), + AnchoredKey::from((&d1, "b")), Value::from(3), ), ], @@ -548,18 +558,8 @@ mod tests { "eth_friend".into(), vec![ st( - P::Native(NP::Equal), - vec![ - STA::AnchoredKey(wc(2), Key::from("_type")), - STA::Literal(PodType::Signed.into()), - ], - ), - st( - P::Native(NP::Equal), - vec![ - STA::AnchoredKey(wc(2), Key::from("_signer")), - STA::Wildcard(wc(0)), - ], + P::Native(NP::SignedBy), + vec![STA::Wildcard(wc(2)), STA::Wildcard(wc(0))], ), st( P::Native(NP::Equal), diff --git a/src/middleware/error.rs b/src/middleware/error.rs index da23f3d..b1e5b02 100644 --- a/src/middleware/error.rs +++ b/src/middleware/error.rs @@ -3,7 +3,7 @@ use std::{backtrace::Backtrace, fmt::Debug}; use crate::middleware::{ - CustomPredicate, Key, Operation, PodId, Predicate, Statement, StatementArg, StatementTmplArg, + CustomPredicate, Hash, Key, Operation, Predicate, Statement, StatementArg, StatementTmplArg, Value, Wildcard, }; @@ -24,7 +24,7 @@ pub enum MiddlewareInnerError { #[error("{0} should be assigned the value {1} but has previously been assigned {2}")] InvalidWildcardAssignment(Wildcard, Value, Value), #[error("{0} matches POD ID {1}, yet the template key {2} does not match {3}")] - MismatchedAnchoredKeyInStatementTmplArg(Wildcard, PodId, Key, Key), + MismatchedAnchoredKeyInStatementTmplArg(Wildcard, Hash, Key, Key), #[error("{0} does not match against {1}")] MismatchedStatementTmplArg(StatementTmplArg, StatementArg), #[error("Expected a statement of type {0}, got {1}")] @@ -90,14 +90,14 @@ impl Error { new!(InvalidWildcardAssignment(wildcard, value, prev_value)) } pub(crate) fn mismatched_anchored_key_in_statement_tmpl_arg( - pod_id_wildcard: Wildcard, - pod_id: PodId, + root_wildcard: Wildcard, + root: Hash, key_tmpl: Key, key: Key, ) -> Self { new!(MismatchedAnchoredKeyInStatementTmplArg( - pod_id_wildcard, - pod_id, + root_wildcard, + root, key_tmpl, key )) diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index aadc778..dfd2d97 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -6,6 +6,7 @@ use std::sync::Arc; use hex::ToHex; use itertools::Itertools; use strum_macros::FromRepr; + mod basetypes; use std::{cmp::PartialEq, hash}; @@ -19,7 +20,7 @@ mod operation; mod pod_deserialization; pub mod serialization; mod statement; -use std::{any::Any, collections::HashMap, fmt}; +use std::{any::Any, fmt}; pub use basetypes::*; pub use custom::*; @@ -30,13 +31,10 @@ pub use pod_deserialization::*; use serialization::*; pub use statement::*; -use crate::backends::plonky2::primitives::{ - ec::{curve::Point as PublicKey, schnorr::SecretKey}, - merkletree::{MerkleProof, MerkleTreeStateTransitionProof}, +use crate::backends::plonky2::primitives::merkletree::{ + MerkleProof, MerkleTreeStateTransitionProof, }; -pub const SELF: PodId = PodId(SELF_ID_HASH); - // TODO: Move all value-related types to to `value.rs` #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] // TODO #[schemars(transform = serialization::transform_value_schema)] @@ -64,7 +62,6 @@ pub enum TypedValue { PublicKey(PublicKey), // Schnorr secret key variant (scalar) SecretKey(SecretKey), - PodId(PodId), // UNTAGGED TYPES: #[serde(untagged)] Set(Set), @@ -120,12 +117,6 @@ impl From for TypedValue { } } -impl From for TypedValue { - fn from(id: PodId) -> Self { - TypedValue::PodId(id) - } -} - impl From for TypedValue { fn from(s: Set) -> Self { TypedValue::Set(s) @@ -150,12 +141,6 @@ impl From for TypedValue { } } -impl From for TypedValue { - fn from(t: PodType) -> Self { - TypedValue::from(t as i64) - } -} - impl TryFrom<&TypedValue> for i64 { type Error = Error; fn try_from(v: &TypedValue) -> std::result::Result { @@ -167,30 +152,23 @@ impl TryFrom<&TypedValue> for i64 { } } -impl TryFrom for Key { +impl TryFrom<&TypedValue> for String { type Error = Error; - fn try_from(tv: TypedValue) -> Result { + fn try_from(tv: &TypedValue) -> Result { match tv { - TypedValue::String(s) => Ok(Key::new(s)), + TypedValue::String(s) => Ok(s.clone()), _ => Err(Error::custom(format!( - "Value {} cannot be converted to a key.", + "Value {} cannot be converted to a string.", tv ))), } } } -impl TryFrom<&TypedValue> for PodId { +impl TryFrom<&TypedValue> for Key { type Error = Error; - fn try_from(v: &TypedValue) -> Result { - match v { - TypedValue::PodId(id) => Ok(*id), - TypedValue::Raw(v) => Ok(PodId(Hash(v.0))), - _ => Err(Error::custom(format!( - "Value {} cannot be converted to a PodId.", - v - ))), - } + fn try_from(tv: &TypedValue) -> Result { + Ok(Key::new(String::try_from(tv)?)) } } @@ -262,13 +240,6 @@ impl fmt::Display for TypedValue { } TypedValue::PublicKey(p) => write!(f, "PublicKey({})", p), TypedValue::SecretKey(p) => write!(f, "SecretKey({})", p), - TypedValue::PodId(p) => { - if *p == SELF { - write!(f, "SELF") - } else { - write!(f, "0x{}", p.0.encode_hex::()) - } - } TypedValue::Raw(r) => { write!(f, "Raw(0x{})", r.encode_hex::()) } @@ -288,7 +259,6 @@ impl From<&TypedValue> for RawValue { TypedValue::Raw(v) => *v, TypedValue::PublicKey(p) => RawValue::from(hash_fields(&p.as_fields())), TypedValue::SecretKey(sk) => RawValue::from(hash_fields(&sk.to_limbs())), - TypedValue::PodId(id) => RawValue::from(id.0), } } } @@ -342,13 +312,13 @@ impl JsonSchema for TypedValue { ..Default::default() }; - let pod_id_schema = schemars::schema::SchemaObject { + let root_schema = schemars::schema::SchemaObject { instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))), object: Some(Box::new(schemars::schema::ObjectValidation { - properties: [("PodId".to_string(), gen.subschema_for::())] + properties: [("Root".to_string(), gen.subschema_for::())] .into_iter() .collect(), - required: ["PodId".to_string()].into_iter().collect(), + required: ["Root".to_string()].into_iter().collect(), ..Default::default() })), ..Default::default() @@ -390,7 +360,7 @@ impl JsonSchema for TypedValue { Schema::Object(SchemaObject { subschemas: Some(Box::new(schemars::schema::SubschemaValidation { any_of: Some(vec![ - Schema::Object(pod_id_schema), + Schema::Object(root_schema), Schema::Object(int_schema), Schema::Object(raw_schema), Schema::Object(public_key_schema), @@ -498,7 +468,7 @@ impl Value { key, self )))?, }, - TypedValue::Dictionary(d) => d.prove(&key.typed().clone().try_into()?), + TypedValue::Dictionary(d) => d.prove(&key.typed().try_into()?), TypedValue::Set(s) => Ok((key, s.prove(key)?)), _ => Err(Error::custom(format!( "Invalid container value {}", @@ -512,7 +482,7 @@ impl Value { TypedValue::Array(_) => Err(Error::custom( "Arrays do not support `NotContains` operation.".to_string(), )), - TypedValue::Dictionary(d) => d.prove_nonexistence(&key.typed().clone().try_into()?), + TypedValue::Dictionary(d) => d.prove_nonexistence(&key.typed().try_into()?), TypedValue::Set(s) => s.prove_nonexistence(key), _ => Err(Error::custom(format!( "Invalid container value {}", @@ -529,7 +499,7 @@ impl Value { ) -> Result { let container = self.typed().clone(); match container { - TypedValue::Dictionary(mut d) => d.insert(&key.typed().clone().try_into()?, value), + TypedValue::Dictionary(mut d) => d.insert(&key.typed().try_into()?, value), TypedValue::Set(mut s) => s.insert(value), _ => Err(Error::custom(format!( "Invalid container value {}", @@ -553,7 +523,7 @@ impl Value { key, self )))?, }, - TypedValue::Dictionary(mut d) => d.update(&key.typed().clone().try_into()?, value), + TypedValue::Dictionary(mut d) => d.update(&key.typed().try_into()?, value), _ => Err(Error::custom(format!( "Invalid container value {} for update op", self.typed() @@ -565,7 +535,7 @@ impl Value { pub(crate) fn prove_deletion(&self, key: &Value) -> Result { let container = self.typed().clone(); match container { - TypedValue::Dictionary(mut d) => d.delete(&key.typed().clone().try_into()?), + TypedValue::Dictionary(mut d) => d.delete(&key.typed().try_into()?), TypedValue::Set(mut s) => s.delete(key), _ => Err(Error::custom(format!( "Invalid container value {}", @@ -585,26 +555,6 @@ where } } -impl fmt::Display for PodId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if *self == SELF { - write!(f, "self") - } else if self.0 == EMPTY_HASH { - write!(f, "null") - } else if f.alternate() { - write!(f, "{:#}", self.0) - } else { - write!(f, "{}", self.0) - } - } -} - -impl From<&Value> for Hash { - fn from(v: &Value) -> Self { - Self(v.raw.0) - } -} - #[derive(Clone, Debug, Eq)] pub struct Key { name: String, @@ -708,32 +658,32 @@ impl JsonSchema for Key { #[derive(Clone, Debug, Eq, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "camelCase")] pub struct AnchoredKey { - pub pod_id: PodId, + pub root: Hash, pub key: Key, } impl AnchoredKey { - pub fn new(pod_id: PodId, key: Key) -> Self { - Self { pod_id, key } + pub fn new(root: Hash, key: Key) -> Self { + Self { root, key } } } impl hash::Hash for AnchoredKey { fn hash(&self, state: &mut H) { - self.pod_id.hash(state); + self.root.hash(state); self.key.hash.hash(state); } } impl PartialEq for AnchoredKey { fn eq(&self, other: &Self) -> bool { - self.pod_id == other.pod_id && self.key.hash == other.key.hash + self.root == other.root && self.key.hash == other.key.hash } } impl fmt::Display for AnchoredKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.pod_id.fmt(f)?; + self.root.fmt(f)?; write!(f, "[")?; self.key.fmt(f)?; write!(f, "]")?; @@ -741,31 +691,30 @@ impl fmt::Display for AnchoredKey { } } -impl From<(PodId, T)> for AnchoredKey +impl From<(Hash, T)> for AnchoredKey where T: Into, { - fn from((pod_id, t): (PodId, T)) -> Self { - Self::new(pod_id, t.into()) + fn from((root, t): (Hash, T)) -> Self { + Self::new(root, t.into()) } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize, JsonSchema)] -pub struct PodId(pub Hash); - -impl ToFields for PodId { - fn to_fields(&self, params: &Params) -> Vec { - self.0.to_fields(params) +impl From<(&Dictionary, T)> for AnchoredKey +where + T: Into, +{ + fn from((dict, t): (&Dictionary, T)) -> Self { + Self::new(dict.commitment(), t.into()) } } #[derive(Clone, Copy, Debug, PartialEq, Eq, FromRepr, Serialize, Deserialize, JsonSchema)] pub enum PodType { - Signed = 1, - Main = 2, - Empty = 3, - MockMain = 102, - MockEmpty = 103, + Main = 1, + Empty = 2, + MockMain = 101, + MockEmpty = 102, } impl fmt::Display for PodType { @@ -773,7 +722,6 @@ impl fmt::Display for PodType { match self { PodType::MockMain => write!(f, "MockMain"), PodType::MockEmpty => write!(f, "MockEmpty"), - PodType::Signed => write!(f, "Signed"), PodType::Main => write!(f, "Main"), PodType::Empty => write!(f, "Empty"), } @@ -784,11 +732,9 @@ impl fmt::Display for PodType { #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Hash)] #[serde(rename_all = "camelCase")] pub struct Params { - pub max_input_signed_pods: usize, - pub max_input_recursive_pods: usize, + pub max_input_pods: usize, pub max_input_pods_public_statements: usize, pub max_statements: usize, - pub max_signed_pod_values: usize, pub max_public_statements: usize, pub max_operation_args: usize, // max number of custom predicates batches that a MainPod can use @@ -809,13 +755,15 @@ pub struct Params { pub max_depth_mt_vds: usize, // maximum number of public key derivations used for PublicKeyOf operation pub max_public_key_of: usize, + // maximum number of signature verifications used for SignedBy operation + pub max_signed_by: usize, // // The following parameters define how a pod id is calculated. They need to be the same among // different circuits to be compatible in their verification. // - // Number of public statements to hash to calculate the id. Must be equal or greater than - // `max_public_statements`. - pub num_public_statements_id: usize, + // Number of public statements to hash to calculate the public inputs. Must be equal or + // greater than `max_public_statements`. + pub num_public_statements_hash: usize, pub max_statement_args: usize, // // The following parameters define how a custom predicate batch id is calculated. @@ -829,13 +777,11 @@ pub struct Params { impl Default for Params { fn default() -> Self { Self { - max_input_signed_pods: 3, - max_input_recursive_pods: 2, + max_input_pods: 2, max_input_pods_public_statements: 10, - max_statements: 20, - max_signed_pod_values: 8, + max_statements: 40, max_public_statements: 10, - num_public_statements_id: 16, + num_public_statements_hash: 16, max_statement_args: 5, max_operation_args: 5, max_custom_predicate_batches: 2, @@ -843,11 +789,12 @@ impl Default for Params { max_custom_predicate_arity: 5, max_custom_predicate_wildcards: 10, max_custom_batch_size: 5, // TODO: Move down to 4? - max_merkle_proofs_containers: 5, + max_merkle_proofs_containers: 16, max_merkle_tree_state_transition_proofs_containers: 5, max_depth_mt_containers: 32, max_depth_mt_vds: 6, // up to 64 (2^6) different pod circuits max_public_key_of: 2, + max_signed_by: 3, } } } @@ -888,15 +835,13 @@ impl Params { /// Total size of the statement table including None, input statements from signed pods and /// input recursive pods and new statements (public & private) pub fn statement_table_size(&self) -> usize { - 1 + self.max_input_signed_pods * self.max_signed_pod_values - + self.max_input_recursive_pods * self.max_input_pods_public_statements - + self.max_statements + 1 + self.max_input_pods * self.max_input_pods_public_statements + self.max_statements } /// Parameters that define how the id is calculated pub fn id_params(&self) -> Vec { vec![ - self.num_public_statements_id, + self.num_public_statements_hash, self.max_statement_args, self.max_custom_predicate_arity, self.max_custom_batch_size, @@ -920,23 +865,19 @@ impl Params { } } -/// Replace references to SELF by `self_id`. -pub fn normalize_statement(statement: &Statement, self_id: PodId) -> Statement { - let predicate = statement.predicate(); - let args = statement - .args() - .iter() - .map(|sa| match &sa { - StatementArg::Key(AnchoredKey { pod_id, key }) if *pod_id == SELF => { - StatementArg::Key(AnchoredKey::new(self_id, key.clone())) - } - StatementArg::Literal(value) if value.raw.0 == SELF.0 .0 => { - StatementArg::Literal(self_id.into()) - } - _ => sa.clone(), - }) - .collect(); - Statement::from_args(predicate, args).expect("statement was valid before normalization") +/// Replace EMPTY_HASH in IntroPredicateRef by verifier_data_hash +pub fn normalize_statement(statement: &Statement, verifier_data_hash: Hash) -> Statement { + match statement { + Statement::Intro(ir, args) if ir.verifier_data_hash == EMPTY_HASH => Statement::Intro( + IntroPredicateRef { + name: ir.name.clone(), + args_len: ir.args_len, + verifier_data_hash, + }, + args.clone(), + ), + s => s.clone(), + } } pub trait EqualsAny { @@ -953,10 +894,27 @@ impl EqualsAny for T { } } +/// Trait for pods that are generated with a plonky2 circuit and that can be verified by a +/// recursive MainPod circuit (with the exception of mock types). A Pod implementing this trait +/// does not necesarilly come from recursion: for example an introduction Pod in general is not +/// recursive. pub trait Pod: fmt::Debug + DynClone + Sync + Send + Any + EqualsAny { fn params(&self) -> &Params; fn verify(&self) -> Result<(), BackendError>; - fn id(&self) -> PodId; + /// Overwrite this method to return true in a mock pod to skip plonky2 verification + fn is_mock(&self) -> bool { + false + } + /// Overwrite this method to return true in a MainPod to generate verifier key inclusion proof + /// into the vd set + fn is_main(&self) -> bool { + false + } + /// Hash of the public statements. This can be used to identify a Pod. Different pods can + /// have the same `statements_hash` if they expose the same public statements even if they + /// arrive to them through different private inputs. + fn statements_hash(&self) -> Hash; + // TODO: String instead of &str /// Return a uuid of the pod type and its name. The name is only used as metadata. fn pod_type(&self) -> (usize, &'static str); /// Statements as internally generated, where self-referencing arguments use SELF in the @@ -965,29 +923,39 @@ pub trait Pod: fmt::Debug + DynClone + Sync + Send + Any + EqualsAny { /// Normalized statements, where self-referencing arguments use the pod id instead of SELF in /// the anchored key. fn pub_statements(&self) -> Vec { + let verifier_data_hash = self.verifier_data_hash(); self.pub_self_statements() .into_iter() - .map(|statement| normalize_statement(&statement, self.id())) + .map(|statement| normalize_statement(&statement, verifier_data_hash)) .collect() } /// Return this Pods data serialized into a json value. This serialization can skip `params, /// id, vds_root` fn serialize_data(&self) -> serde_json::Value; - /// Extract key-values from ValueOf public statements - fn kvs(&self) -> HashMap { - self.pub_statements() - .into_iter() - .filter_map(|st| match st { - Statement::Equal(ValueRef::Key(ak), ValueRef::Literal(v)) => Some((ak, v)), - _ => None, - }) - .collect() - } + /// Returns the deserialized Pod. + fn deserialize_data( + params: Params, + data: serde_json::Value, + vd_set: VDSet, + id: Hash, + ) -> Result + where + Self: Sized; fn equals(&self, other: &dyn Pod) -> bool { self.equals_any(other as &dyn Any) } + + fn verifier_data(&self) -> VerifierOnlyCircuitData; + fn verifier_data_hash(&self) -> Hash { + Hash(hash_verifier_data(&self.verifier_data()).elements) + } + /// Return a hash of the CommonCircuitData that uniquely identifies the circuit + /// configuration and list of custom gates. + fn common_hash(&self) -> String; + fn proof(&self) -> Proof; + fn vd_set(&self) -> &VDSet; } impl PartialEq for Box { fn eq(&self, other: &Self) -> bool { @@ -1000,66 +968,24 @@ impl Eq for Box {} // impl Clone for Box dyn_clone::clone_trait_object!(Pod); -/// Trait for pods that are generated with a plonky2 circuit and that can be verified by a -/// recursive MainPod circuit. A Pod implementing this trait does not necesarilly come from -/// recursion: for example an introduction Pod in general is not recursive. -pub trait RecursivePod: Pod { - fn verifier_data(&self) -> VerifierOnlyCircuitData; - /// Return a hash of the CommonCircuitData that uniquely identifies the circuit - /// configuration and list of custom gates. - fn common_hash(&self) -> String; - fn proof(&self) -> Proof; - fn vd_set(&self) -> &VDSet; - - /// Returns the deserialized RecursivePod. - fn deserialize_data( - params: Params, - data: serde_json::Value, - vd_set: VDSet, - id: PodId, - ) -> Result, BackendError> - where - Self: Sized; -} -impl PartialEq for Box { - fn eq(&self, other: &Self) -> bool { - self.equals(&**other) - } -} - -impl Eq for Box {} - -// impl Clone for Box -dyn_clone::clone_trait_object!(RecursivePod); - -pub trait PodSigner { - fn sign( - &self, - params: &Params, - kvs: &HashMap, - ) -> Result, BackendError>; +pub trait Signer { + fn sign(&self, msg: RawValue) -> Signature; + fn public_key(&self) -> PublicKey; } #[derive(Debug)] pub struct MainPodInputs<'a> { - pub signed_pods: &'a [&'a dyn Pod], - pub recursive_pods: &'a [&'a dyn RecursivePod], + pub pods: &'a [&'a dyn Pod], pub statements: &'a [Statement], pub operations: &'a [Operation], /// Statements that need to be made public (they can come from input pods or input /// statements) pub public_statements: &'a [Statement], - // TODO: REMOVE THIS pub vd_set: VDSet, } -pub trait PodProver { - fn prove( - &self, - params: &Params, - vd_set: &VDSet, - inputs: MainPodInputs, - ) -> Result, BackendError>; +pub trait MainPodProver { + fn prove(&self, params: &Params, inputs: MainPodInputs) -> Result, BackendError>; } pub trait ToFields { diff --git a/src/middleware/operation.rs b/src/middleware/operation.rs index 4b378e2..e85d1d5 100644 --- a/src/middleware/operation.rs +++ b/src/middleware/operation.rs @@ -8,14 +8,14 @@ use crate::{ backends::plonky2::primitives::{ ec::{ curve::{Point as PublicKey, GROUP_ORDER}, - schnorr::SecretKey, + schnorr::{SecretKey, Signature}, }, merkletree::{MerkleProof, MerkleTree, MerkleTreeOp, MerkleTreeStateTransitionProof}, }, middleware::{ - hash_values, AnchoredKey, CustomPredicate, CustomPredicateRef, Error, NativePredicate, - Params, Predicate, Result, Statement, StatementArg, StatementTmpl, StatementTmplArg, - ToFields, TypedValue, Value, ValueRef, Wildcard, F, SELF, + hash_values, AnchoredKey, CustomPredicate, CustomPredicateRef, Error, Hash, Key, + NativePredicate, Params, Predicate, Result, Statement, StatementArg, StatementTmpl, + StatementTmplArg, ToFields, TypedValue, Value, ValueRef, Wildcard, F, }, }; @@ -30,6 +30,7 @@ pub enum OperationAux { None, MerkleProof(MerkleProof), MerkleTreeStateTransitionProof(MerkleTreeStateTransitionProof), + Signature(Signature), } impl fmt::Display for OperationAux { @@ -41,6 +42,7 @@ impl fmt::Display for OperationAux { Self::MerkleTreeStateTransitionProof(pf) => { write!(f, "merkle_tree_state_transition_proof({:?})", pf)? } + Self::Signature(sig) => write!(f, "signature({:?})", sig)?, } Ok(()) } @@ -67,24 +69,24 @@ impl ToFields for OperationType { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, std::hash::Hash, Serialize, Deserialize)] pub enum NativeOperation { None = 0, - NewEntry = 1, - CopyStatement = 2, - EqualFromEntries = 3, - NotEqualFromEntries = 4, - LtEqFromEntries = 5, - LtFromEntries = 6, - TransitiveEqualFromStatements = 7, - LtToNotEqual = 8, - ContainsFromEntries = 9, - NotContainsFromEntries = 10, - SumOf = 11, - ProductOf = 12, - MaxOf = 13, - HashOf = 14, - PublicKeyOf = 15, + CopyStatement = 1, + EqualFromEntries = 2, + NotEqualFromEntries = 3, + LtEqFromEntries = 4, + LtFromEntries = 5, + TransitiveEqualFromStatements = 6, + LtToNotEqual = 7, + ContainsFromEntries = 8, + NotContainsFromEntries = 9, + SumOf = 10, + ProductOf = 11, + MaxOf = 12, + HashOf = 13, + PublicKeyOf = 14, + SignedBy = 15, ContainerInsertFromEntries = 16, ContainerUpdateFromEntries = 17, ContainerDeleteFromEntries = 18, @@ -127,7 +129,6 @@ impl OperationType { match self { OperationType::Native(native_op) => match native_op { NativeOperation::None => Some(Predicate::Native(NativePredicate::None)), - NativeOperation::NewEntry => Some(Predicate::Native(NativePredicate::Equal)), NativeOperation::CopyStatement => None, NativeOperation::EqualFromEntries => { Some(Predicate::Native(NativePredicate::Equal)) @@ -154,6 +155,7 @@ impl OperationType { NativeOperation::PublicKeyOf => { Some(Predicate::Native(NativePredicate::PublicKeyOf)) } + NativeOperation::SignedBy => Some(Predicate::Native(NativePredicate::SignedBy)), NativeOperation::ContainerInsertFromEntries => { Some(Predicate::Native(NativePredicate::ContainerInsert)) } @@ -174,7 +176,6 @@ impl OperationType { #[derive(Clone, Debug, PartialEq)] pub enum Operation { None, - NewEntry, CopyStatement(Statement), EqualFromEntries(Statement, Statement), NotEqualFromEntries(Statement, Statement), @@ -198,6 +199,7 @@ pub enum Operation { MaxOf(Statement, Statement, Statement), HashOf(Statement, Statement, Statement), PublicKeyOf(Statement, Statement), + SignedBy(Statement, Statement, Signature), ContainerInsertFromEntries( /* new_root */ Statement, /* old_root */ Statement, @@ -243,7 +245,6 @@ impl Operation { use NativeOperation::*; match self { Self::None => OT::Native(None), - Self::NewEntry => OT::Native(NewEntry), Self::CopyStatement(_) => OT::Native(CopyStatement), Self::EqualFromEntries(_, _) => OT::Native(EqualFromEntries), Self::NotEqualFromEntries(_, _) => OT::Native(NotEqualFromEntries), @@ -258,6 +259,7 @@ impl Operation { Self::MaxOf(_, _, _) => OT::Native(MaxOf), Self::HashOf(_, _, _) => OT::Native(HashOf), Self::PublicKeyOf(_, _) => OT::Native(PublicKeyOf), + Self::SignedBy(_, _, _) => OT::Native(SignedBy), Self::ContainerInsertFromEntries(_, _, _, _, _) => { OT::Native(ContainerInsertFromEntries) } @@ -272,7 +274,6 @@ impl Operation { pub fn args(&self) -> Vec { match self.clone() { Self::None => vec![], - Self::NewEntry => vec![], Self::CopyStatement(s) => vec![s], Self::EqualFromEntries(s1, s2) => vec![s1, s2], Self::NotEqualFromEntries(s1, s2) => vec![s1, s2], @@ -287,6 +288,7 @@ impl Operation { Self::MaxOf(s1, s2, s3) => vec![s1, s2, s3], Self::HashOf(s1, s2, s3) => vec![s1, s2, s3], Self::PublicKeyOf(s1, s2) => vec![s1, s2], + Self::SignedBy(s1, s2, _sig) => vec![s1, s2], Self::ContainerInsertFromEntries(s1, s2, s3, s4, _pf) => vec![s1, s2, s3, s4], Self::ContainerUpdateFromEntries(s1, s2, s3, s4, _pf) => vec![s1, s2, s3, s4], Self::ContainerDeleteFromEntries(s1, s2, s3, _pf) => vec![s1, s2, s3], @@ -310,7 +312,6 @@ impl Operation { Ok(match op_code { OperationType::Native(o) => match (o, &args, aux.clone()) { (NO::None, &[], OA::None) => Self::None, - (NO::NewEntry, &[], OA::None) => Self::NewEntry, (NO::CopyStatement, &[s], OA::None) => Self::CopyStatement(s.clone()), (NO::EqualFromEntries, &[s1, s2], OA::None) => { Self::EqualFromEntries(s1.clone(), s2.clone()) @@ -343,6 +344,9 @@ impl Operation { Self::HashOf(s1.clone(), s2.clone(), s3.clone()) } (NO::PublicKeyOf, &[s1, s2], OA::None) => Self::PublicKeyOf(s1.clone(), s2.clone()), + (NO::SignedBy, &[s1, s2], OA::Signature(sig)) => { + Self::SignedBy(s1.clone(), s2.clone(), sig) + } ( NO::ContainerInsertFromEntries, &[s1, s2, s3, s4], @@ -410,6 +414,11 @@ impl Operation { Ok(sk.0 < *GROUP_ORDER && pk == sk.public_key()) } + pub(crate) fn check_signed_by(msg: &Value, pk: &Value, sig: &Signature) -> Result { + let pk: PublicKey = pk.typed().try_into()?; + Ok(sig.verify(pk, msg.raw())) + } + /// Checks the given operation against a statement. pub fn check(&self, params: &Params, output_statement: &Statement) -> Result { use Statement::*; @@ -424,9 +433,6 @@ impl Operation { }; let b = match (self, output_statement) { (Self::None, None) => true, - (Self::NewEntry, Equal(ValueRef::Key(AnchoredKey { pod_id, .. }), _)) => { - pod_id == &SELF - } (Self::CopyStatement(s1), s2) => s1 == s2, (Self::EqualFromEntries(s1, s2), Equal(v3, v4)) => val(v3, s1)? == val(v4, s2)?, (Self::NotEqualFromEntries(s1, s2), NotEqual(v3, v4)) => val(v3, s1)? != val(v4, s2)?, @@ -479,6 +485,9 @@ impl Operation { (Self::PublicKeyOf(s1, s2), PublicKeyOf(v3, v4)) => { Self::check_public_key(&val(v3, s1)?, &val(v4, s2)?)? } + (Self::SignedBy(msg_s, pk_s, sig), SignedBy(msg_v, pk_v)) => { + Self::check_signed_by(&val(msg_v, msg_s)?, &val(pk_v, pk_s)?, sig)? + } ( Self::ContainerInsertFromEntries(new_root_s, old_root_s, key_s, val_s, pf), ContainerInsert(new_root_v, old_root_v, key_v, val_v), @@ -579,15 +588,15 @@ pub fn check_st_tmpl( (StatementTmplArg::None, StatementArg::None) => Ok(()), (StatementTmplArg::Literal(lhs), StatementArg::Literal(rhs)) if lhs == rhs => Ok(()), ( - StatementTmplArg::AnchoredKey(pod_id_wc, key_tmpl), - StatementArg::Key(AnchoredKey { pod_id, key }), + StatementTmplArg::AnchoredKey(root_wc, key_tmpl), + StatementArg::Key(AnchoredKey { root, key }), ) => { - let pod_id_ok = check_or_set(Value::from(*pod_id), pod_id_wc, wildcard_map); - pod_id_ok.and_then(|_| { + let root_ok = check_or_set(Value::from(*root), root_wc, wildcard_map); + root_ok.and_then(|_| { (key_tmpl == key).then_some(()).ok_or( Error::mismatched_anchored_key_in_statement_tmpl_arg( - pod_id_wc.clone(), - *pod_id, + root_wc.clone(), + *root, key_tmpl.clone(), key.clone(), ), @@ -742,14 +751,28 @@ impl fmt::Display for Operation { } } +pub(crate) fn root_key_to_ak(root: &Value, key: &Value) -> Option { + let root_hash = Hash::from(root.raw()); + Key::try_from(key.typed()) + .map(|key| AnchoredKey::new(root_hash, key)) + .ok() +} + /// Returns the value associated with `output_ref`. /// If `output_ref` is a concrete value, returns that value. -/// Otherwise, `output_ref` was constructed using an `Equal` statement, and `input_st` +/// Otherwise, `output_ref` was constructed using a `Contains` statement, and `input_st` /// must be that statement. pub(crate) fn value_from_op(input_st: &Statement, output_ref: &ValueRef) -> Option { match (input_st, output_ref) { (Statement::None, ValueRef::Literal(v)) => Some(v.clone()), - (Statement::Equal(r1, ValueRef::Literal(v)), r2) if r1 == r2 => Some(v.clone()), + ( + Statement::Contains( + ValueRef::Literal(root), + ValueRef::Literal(key), + ValueRef::Literal(v), + ), + ValueRef::Key(out_ak), + ) => root_key_to_ak(root, key).and_then(|ak| (*out_ak == ak).then(|| v.clone())), _ => None, } } @@ -761,45 +784,39 @@ mod tests { use num::BigUint; use crate::{ - backends::plonky2::primitives::{ - ec::{curve::GROUP_ORDER, schnorr::SecretKey}, - merkletree::MerkleTree, - }, - middleware::{ - hash_value, AnchoredKey, Error, Key, Operation, Params, PodId, Result, Statement, + backends::plonky2::{ + primitives::{ + ec::{curve::GROUP_ORDER, schnorr::SecretKey}, + merkletree::MerkleTree, + }, + signer::Signer, }, + middleware::{hash_value, Error, Operation, Params, Result, Signer as _, Statement, Value}, }; #[test] fn check_container_ops() -> Result<()> { let params = Params::default(); - let pod_id = PodId::default(); - let root_ak = AnchoredKey::new(pod_id, Key::new("root".into())); - let key_ak = AnchoredKey::new(pod_id, Key::new("key".into())); - let val_ak = AnchoredKey::new(pod_id, Key::new("value".into())); - // Form Merkle tree let kvs = (0..10) .map(|i| (hash_value(&i.into()).into(), i.into())) .collect::>(); let mt = MerkleTree::new(params.max_depth_mt_containers, &kvs)?; - let root_s = Statement::Equal(root_ak.clone().into(), mt.root().into()); + let root = mt.root(); // Check existence proofs kvs.iter().try_for_each(|(k, v)| { - // Form op args - let key_s = Statement::Equal(key_ak.clone().into(), (*k).into()); - let value_s = Statement::Equal(val_ak.clone().into(), (*v).into()); let (_, pf) = mt.prove(k)?; // Form op - let op = Operation::ContainsFromEntries(root_s.clone(), key_s, value_s, pf); - // Form output statement - let st = Statement::Contains( - root_ak.clone().into(), - key_ak.clone().into(), - val_ak.clone().into(), + let op = Operation::ContainsFromEntries( + Statement::None, + Statement::None, + Statement::None, + pf, ); + // Form output statement + let st = Statement::Contains(root.into(), (*k).into(), (*v).into()); // Check op against output statement op.check(¶ms, &st).and_then(|ind| { @@ -816,11 +833,10 @@ mod tests { // Check non-existence proofs similarly (50..60).try_for_each(|k| { - let key_s = Statement::Equal(key_ak.clone().into(), k.into()); let pf = mt.prove_nonexistence(&k.into())?; - let op = Operation::NotContainsFromEntries(root_s.clone(), key_s, pf); - let st = Statement::NotContains(root_ak.clone().into(), key_ak.clone().into()); + let op = Operation::NotContainsFromEntries(Statement::None, Statement::None, pf); + let st = Statement::NotContains(root.into(), k.into()); op.check(¶ms, &st).and_then(|ind| { if ind { @@ -838,11 +854,6 @@ mod tests { #[test] fn check_container_update_ops() -> Result<()> { let params = Params::default(); - let pod_id = PodId::default(); - let new_root_ak = AnchoredKey::new(pod_id, Key::new("new_root".into())); - let old_root_ak = AnchoredKey::new(pod_id, Key::new("new_root".into())); - let key_ak = AnchoredKey::new(pod_id, Key::new("key".into())); - let val_ak = AnchoredKey::new(pod_id, Key::new("value".into())); // Form Merkle tree let kvs = (0..10) @@ -854,23 +865,24 @@ mod tests { (11..20) .map(|i| (hash_value(&i.into()).into(), i.into())) .try_for_each(|(k, v)| { - let old_root_s = Statement::Equal(old_root_ak.clone().into(), mt.root().into()); + let old_root = mt.root(); let mtp = mt.insert(&k, &v)?; - // Form op args - let new_root_s = Statement::Equal(new_root_ak.clone().into(), mt.root().into()); - let key_s = Statement::Equal(key_ak.clone().into(), k.into()); - let value_s = Statement::Equal(val_ak.clone().into(), v.into()); + let new_root = mt.root(); // Form op let op = Operation::ContainerInsertFromEntries( - new_root_s, old_root_s, key_s, value_s, mtp, + Statement::None, + Statement::None, + Statement::None, + Statement::None, + mtp, ); // Form output statement let st = Statement::ContainerInsert( - new_root_ak.clone().into(), - old_root_ak.clone().into(), - key_ak.clone().into(), - val_ak.clone().into(), + new_root.into(), + old_root.into(), + k.into(), + v.into(), ); // Check op against output statement @@ -890,23 +902,24 @@ mod tests { (11..20) .map(|i| (hash_value(&i.into()).into(), (i + 1).into())) .try_for_each(|(k, v)| { - let old_root_s = Statement::Equal(old_root_ak.clone().into(), mt.root().into()); + let old_root = mt.root(); let mtp = mt.update(&k, &v)?; - // Form op args - let new_root_s = Statement::Equal(new_root_ak.clone().into(), mt.root().into()); - let key_s = Statement::Equal(key_ak.clone().into(), k.into()); - let value_s = Statement::Equal(val_ak.clone().into(), v.into()); + let new_root = mt.root(); // Form op let op = Operation::ContainerUpdateFromEntries( - new_root_s, old_root_s, key_s, value_s, mtp, + Statement::None, + Statement::None, + Statement::None, + Statement::None, + mtp, ); // Form output statement let st = Statement::ContainerUpdate( - new_root_ak.clone().into(), - old_root_ak.clone().into(), - key_ak.clone().into(), - val_ak.clone().into(), + new_root.into(), + old_root.into(), + k.into(), + v.into(), ); // Check op against output statement @@ -926,20 +939,19 @@ mod tests { (11..20) .map(|i| hash_value(&i.into()).into()) .try_for_each(|k| { - let old_root_s = Statement::Equal(old_root_ak.clone().into(), mt.root().into()); + let old_root = mt.root(); let mtp = mt.delete(&k)?; - // Form op args - let new_root_s = Statement::Equal(new_root_ak.clone().into(), mt.root().into()); - let key_s = Statement::Equal(key_ak.clone().into(), k.into()); + let new_root = mt.root(); // Form op - let op = Operation::ContainerDeleteFromEntries(new_root_s, old_root_s, key_s, mtp); - // Form output statement - let st = Statement::ContainerDelete( - new_root_ak.clone().into(), - old_root_ak.clone().into(), - key_ak.clone().into(), + let op = Operation::ContainerDeleteFromEntries( + Statement::None, + Statement::None, + Statement::None, + mtp, ); + // Form output statement + let st = Statement::ContainerDelete(new_root.into(), old_root.into(), k.into()); // Check op against output statement op.check(¶ms, &st).and_then(|ind| { @@ -979,20 +991,13 @@ mod tests { ]; let params = Params::default(); - let pod_id = PodId::default(); - let pk_ak = AnchoredKey::new(pod_id, Key::new("pubkey".into())); - let sk_ak = AnchoredKey::new(pod_id, Key::new("secret".into())); test_cases.iter().try_for_each(|(pk, sk, expect_good)| { - // Form op args - let pk_s = Statement::Equal(pk_ak.clone().into(), (*pk).into()); - let sk_s = Statement::Equal(sk_ak.clone().into(), sk.clone().into()); - // Form op - let op = Operation::PublicKeyOf(pk_s.clone(), sk_s.clone()); + let op = Operation::PublicKeyOf(Statement::None, Statement::None); // Form output statement - let st = Statement::PublicKeyOf(pk_ak.clone().into(), sk_ak.clone().into()); + let st = Statement::PublicKeyOf((*pk).into(), sk.clone().into()); // Check op.check(¶ms, &st).map(|is_good| { @@ -1011,28 +1016,37 @@ mod tests { let fixed_pk = fixed_sk.public_key(); let params = Params::default(); - let pod_id = PodId::default(); - let pk_ak = AnchoredKey::new(pod_id, Key::new("pubkey".into())); - let sk_ak = AnchoredKey::new(pod_id, Key::new("secret".into())); - - // Form op args - let pk_s = Statement::Equal(pk_ak.clone().into(), fixed_pk.into()); - let sk_s = Statement::Equal(sk_ak.clone().into(), fixed_sk.clone().into()); // Bad op and statement with bad first args - let op = Operation::PublicKeyOf(pk_s.clone(), pk_s.clone()); - let st = Statement::PublicKeyOf(pk_ak.clone().into(), pk_ak.clone().into()); + let op = Operation::PublicKeyOf(Statement::None, Statement::None); + let st = Statement::PublicKeyOf(fixed_pk.into(), fixed_pk.into()); // Check assert!(op.check(¶ms, &st).is_err()); // Bad op and statement with bad second args - let op = Operation::PublicKeyOf(sk_s.clone(), sk_s.clone()); - let st = Statement::PublicKeyOf(sk_ak.clone().into(), sk_ak.clone().into()); + let op = Operation::PublicKeyOf(Statement::None, Statement::None); + let st = Statement::PublicKeyOf(fixed_sk.clone().into(), fixed_sk.clone().into()); // Check assert!(op.check(¶ms, &st).is_err()); Ok(()) } + + #[test] + fn check_signed_by_op() -> Result<()> { + let params = Params::default(); + + let sk = SecretKey(BigUint::from(0x1234567890abcdefu64)); + let pk = sk.public_key(); + let msg = Value::from("hello"); + let sig = Signer(sk).sign(msg.raw()); + + let op = Operation::SignedBy(Statement::None, Statement::None, sig); + let st = Statement::SignedBy(msg.into(), pk.into()); + op.check(¶ms, &st)?; + + Ok(()) + } } diff --git a/src/middleware/pod_deserialization.rs b/src/middleware/pod_deserialization.rs index 135cec1..d863e04 100644 --- a/src/middleware/pod_deserialization.rs +++ b/src/middleware/pod_deserialization.rs @@ -3,14 +3,14 @@ use std::{ sync::{LazyLock, Mutex}, }; -use crate::middleware::{BackendError, Params, Pod, PodId, PodType, RecursivePod, Result, VDSet}; +use crate::middleware::{BackendError, Hash, Params, Pod, PodType, Result, VDSet}; type DeserializeFn = fn( params: Params, data: serde_json::Value, vd_set: VDSet, - id: PodId, -) -> Result, BackendError>; + id: Hash, +) -> Result, BackendError>; static DESERIALIZERS: LazyLock>> = LazyLock::new(backend::deserializers_default); @@ -25,10 +25,10 @@ pub fn register_pod_deserializer(pod_type: usize, deserialize_fn: DeserializeFn) pub fn deserialize_pod( pod_type: usize, params: Params, - id: PodId, + id: Hash, vd_set: VDSet, data: serde_json::Value, -) -> Result, BackendError> { +) -> Result, BackendError> { let deserialize_fn: DeserializeFn = *DESERIALIZERS .lock() @@ -42,14 +42,6 @@ pub fn deserialize_pod( deserialize_fn(params, data, vd_set, id) } -pub fn deserialize_signed_pod( - pod_type: usize, - id: PodId, - data: serde_json::Value, -) -> Result, BackendError> { - backend::deserialize_signed_pod(pod_type, id, data) -} - #[cfg(feature = "backend_plonky2")] mod backend { use super::*; @@ -57,30 +49,26 @@ mod backend { emptypod::EmptyPod, mainpod::MainPod, mock::{emptypod::MockEmptyPod, mainpod::MockMainPod}, - signedpod::SignedPod, }; pub(super) fn deserializers_default() -> Mutex> { + fn deserialize_data( + params: Params, + data: serde_json::Value, + vd_set: VDSet, + id: Hash, + ) -> Result, BackendError> { + Ok(Box::new(P::deserialize_data(params, data, vd_set, id)?)) + } + let mut map: HashMap = HashMap::new(); - map.insert(PodType::Empty as usize, EmptyPod::deserialize_data); - map.insert(PodType::Main as usize, MainPod::deserialize_data); - map.insert(PodType::MockEmpty as usize, MockEmptyPod::deserialize_data); - map.insert(PodType::MockMain as usize, MockMainPod::deserialize_data); + map.insert(PodType::Empty as usize, deserialize_data::); + map.insert(PodType::Main as usize, deserialize_data::); + map.insert( + PodType::MockEmpty as usize, + deserialize_data::, + ); + map.insert(PodType::MockMain as usize, deserialize_data::); Mutex::new(map) } - - pub(super) fn deserialize_signed_pod( - pod_type: usize, - id: PodId, - data: serde_json::Value, - ) -> Result, BackendError> { - if pod_type == PodType::Signed as usize { - SignedPod::deserialize(id, data) - } else { - Err(BackendError::custom(format!( - "unexpected pod_type={} for deserialize_signed_pod", - pod_type - ))) - } - } } diff --git a/src/middleware/statement.rs b/src/middleware/statement.rs index 122f398..2ae0e0e 100644 --- a/src/middleware/statement.rs +++ b/src/middleware/statement.rs @@ -9,14 +9,9 @@ use serde::{Deserialize, Serialize}; use strum_macros::FromRepr; use crate::middleware::{ - AnchoredKey, CustomPredicateRef, Error, Params, Result, ToFields, Value, F, VALUE_SIZE, + self, AnchoredKey, CustomPredicateRef, Error, Params, Result, ToFields, Value, F, VALUE_SIZE, }; -// TODO: Maybe store KEY_SIGNER and KEY_TYPE as Key with lazy_static -// hash(KEY_SIGNER) = [2145458785152392366, 15113074911296146791, 15323228995597834291, 11804480340100333725] -pub const KEY_SIGNER: &str = "_signer"; -// hash(KEY_TYPE) = [17948789436443445142, 12513915140657440811, 15878361618879468769, 938231894693848619] -pub const KEY_TYPE: &str = "_type"; pub const STATEMENT_ARG_F_LEN: usize = 8; #[derive(Clone, Copy, Debug, FromRepr, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] @@ -34,9 +29,10 @@ pub enum NativePredicate { MaxOf = 10, HashOf = 11, PublicKeyOf = 12, - ContainerInsert = 13, - ContainerUpdate = 14, - ContainerDelete = 15, + SignedBy = 13, + ContainerInsert = 14, + ContainerUpdate = 15, + ContainerDelete = 16, // Syntactic sugar predicates. These predicates are not supported by the backend. The // frontend compiler is responsible of translating these predicates into the predicates above. @@ -73,6 +69,7 @@ impl Display for NativePredicate { NativePredicate::MaxOf => "MaxOf", NativePredicate::HashOf => "HashOf", NativePredicate::PublicKeyOf => "PublicKeyOf", + NativePredicate::SignedBy => "SignedBy", NativePredicate::ContainerInsert => "ContainerInsert", NativePredicate::ContainerUpdate => "ContainerUpdate", NativePredicate::ContainerDelete => "ContainerDelete", @@ -98,12 +95,20 @@ impl ToFields for NativePredicate { } } +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct IntroPredicateRef { + pub name: String, + pub args_len: usize, + pub verifier_data_hash: middleware::Hash, +} + #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] #[serde(tag = "type", content = "value")] pub enum Predicate { Native(NativePredicate), BatchSelf(usize), Custom(CustomPredicateRef), + Intro(IntroPredicateRef), } impl From for Predicate { @@ -117,6 +122,7 @@ pub enum PredicatePrefix { Native = 1, BatchSelf = 2, Custom = 3, + Intro = 4, } impl From for F { @@ -133,6 +139,8 @@ impl ToFields for Predicate { // CustomPredicateRef(pb, i) as // (3, [hash of pb], i) -- pb hashes to 4 field elements // -- i: usize + // IntroPredicateRef(vd_hash) as + // (4, [vd_hash], 0) // in every case: pad to (hash_size + 2) field elements let mut fields: Vec = match self { @@ -148,6 +156,11 @@ impl ToFields for Predicate { .chain(iter::once(F::from_canonical_usize(*index))) .collect() } + Self::Intro(IntroPredicateRef { + verifier_data_hash, .. + }) => iter::once(F::from(PredicatePrefix::Intro)) + .chain(verifier_data_hash.0) + .collect(), }; fields.resize_with(Params::predicate_size(), || F::from_canonical_u64(0)); fields @@ -172,6 +185,7 @@ impl fmt::Display for Predicate { write!(f, "{}", batch.predicates()[*index].name) } } + Self::Intro(IntroPredicateRef { name, .. }) => write!(f, "{}", name), } } } @@ -196,6 +210,7 @@ pub enum Statement { MaxOf(ValueRef, ValueRef, ValueRef), HashOf(ValueRef, ValueRef, ValueRef), PublicKeyOf(ValueRef, ValueRef), + SignedBy(ValueRef, ValueRef), ContainerInsert( /* new_root */ ValueRef, /* old_root */ ValueRef, @@ -214,6 +229,7 @@ pub enum Statement { /* key */ ValueRef, ), Custom(CustomPredicateRef, Vec), + Intro(IntroPredicateRef, Vec), } macro_rules! statement_constructor { @@ -258,6 +274,7 @@ impl Statement { statement_constructor!(max_of, MaxOf, 3); statement_constructor!(hash_of, HashOf, 3); statement_constructor!(public_key_of, PublicKeyOf, 2); + statement_constructor!(signed_by, SignedBy, 2); statement_constructor!(insert, ContainerInsert, 4); statement_constructor!(update, ContainerUpdate, 4); statement_constructor!(delete, ContainerDelete, 3); @@ -276,10 +293,12 @@ impl Statement { Self::MaxOf(_, _, _) => Native(NativePredicate::MaxOf), Self::HashOf(_, _, _) => Native(NativePredicate::HashOf), Self::PublicKeyOf(_, _) => Native(NativePredicate::PublicKeyOf), + Self::SignedBy(_, _) => Native(NativePredicate::SignedBy), Self::ContainerInsert(_, _, _, _) => Native(NativePredicate::ContainerInsert), Self::ContainerUpdate(_, _, _, _) => Native(NativePredicate::ContainerUpdate), Self::ContainerDelete(_, _, _) => Native(NativePredicate::ContainerDelete), Self::Custom(cpr, _) => Custom(cpr.clone()), + Self::Intro(ir, _) => Intro(ir.clone()), } } pub fn args(&self) -> Vec { @@ -297,6 +316,7 @@ impl Statement { Self::MaxOf(ak1, ak2, ak3) => vec![ak1.into(), ak2.into(), ak3.into()], Self::HashOf(ak1, ak2, ak3) => vec![ak1.into(), ak2.into(), ak3.into()], Self::PublicKeyOf(ak1, ak2) => vec![ak1.into(), ak2.into()], + Self::SignedBy(ak1, ak2) => vec![ak1.into(), ak2.into()], Self::ContainerInsert(ak1, ak2, ak3, ak4) => { vec![ak1.into(), ak2.into(), ak3.into(), ak4.into()] } @@ -305,6 +325,7 @@ impl Statement { } Self::ContainerDelete(ak1, ak2, ak3) => vec![ak1.into(), ak2.into(), ak3.into()], Self::Custom(_, args) => Vec::from_iter(args.into_iter().map(Literal)), + Self::Intro(_, args) => Vec::from_iter(args.into_iter().map(Literal)), } } @@ -351,6 +372,9 @@ impl Statement { (Native(NativePredicate::PublicKeyOf), &[a1, a2]) => { Self::PublicKeyOf(a1.try_into()?, a2.try_into()?) } + (Native(NativePredicate::SignedBy), &[a1, a2]) => { + Self::SignedBy(a1.try_into()?, a2.try_into()?) + } (Native(NativePredicate::ContainerInsert), &[a1, a2, a3, a4]) => Self::ContainerInsert( a1.try_into()?, a2.try_into()?, @@ -380,6 +404,16 @@ impl Statement { .collect(); Self::Custom(cpr, v_args?) } + (Intro(ir), _) => { + let v_args: Result> = args + .iter() + .map(|x| match x { + StatementArg::Literal(v) => Ok(v.clone()), + _ => Err(Error::incorrect_statements_args()), + }) + .collect(); + Self::Intro(ir, v_args?) + } }; Ok(st) } @@ -453,7 +487,7 @@ impl ToFields for StatementArg { /// Encoding: /// - None => [0, 0, 0, 0, 0, 0, 0, 0] /// - Literal(v) => [[v], 0, 0, 0, 0] - /// - Key(pod_id, key) => [[pod_id], [key]] + /// - Key(root, key) => [[root], [key]] /// - WildcardLiteral(v) => [[v], 0, 0, 0, 0] fn to_fields(&self, params: &Params) -> Vec { // NOTE for @ax0: I removed the old comment because may `to_fields` implementations do @@ -467,7 +501,7 @@ impl ToFields for StatementArg { .chain(iter::repeat(F::ZERO).take(STATEMENT_ARG_F_LEN - VALUE_SIZE)) .collect(), StatementArg::Key(ak) => { - let mut fields = ak.pod_id.to_fields(params); + let mut fields = ak.root.to_fields(params); fields.extend(ak.key.to_fields(params)); fields } @@ -528,17 +562,3 @@ where Self::Literal(value.into()) } } - -#[cfg(test)] -mod tests { - use super::*; - use crate::middleware::hash_str; - - #[test] - fn test_print_special_keys() { - let key = hash_str(KEY_SIGNER); - println!("hash(KEY_SIGNER) = {:?}", key); - let key = hash_str(KEY_TYPE); - println!("hash(KEY_TYPE) = {:?}", key); - } -}