No Pod IDs (#394)

- middleware:
  - Add `Statement::Intro`
  - Add `SignedBy` native predicate and operation.  The signature is auxiliary data to the operation
  - Rename `PodSigner` to `Signer` with a new API (just for signing `RawValue`)
  - Removed `NewEntry` operation.  Use `ContainsFromEntries` instead
  - Remove `KEY_SIGNER` and `KEY_TYPE` which are no longer used
  - Merge `RecursivePod` and `Pod` traits
  - Change the `Pod::deserialize_data` method to use `Self` instead of `Box<dyn Pod>` 
  - Extend `Pod` trait with these methods:
    - `is_main`: when the pod is Main, in a (recursive) verification its vk will be checked to exist in the vd_set but not if it's intro pod
    - `is_mock`: skip some verifications in the recursive mock MainPod verification
    - `verifier_data_hash`
    - `pod_id` renamed to `statements_hash`
  - AnchoredKeys are now a pair of dictionary root and key
  - Entry statements are now defined as Contains with literal arguments
    - Operations that take Entries now use Contains statements with literal arguments
- frontend:
  - Rename `SignedPod` to `SignedDict` (which now contains the dict, public key and signature, and can still `verify(self)`ed)
  - The `SignedDict` keeps the method `get_statement` for convenience but now it returns a `Contains` statement that proves the existence of the key in the dict
  - The `MainPodBuilder` automatically inserts a `Contains` statement when an operation is added that uses an entry as argument that was not yet "opened".
  - Removed the `literal` methods from the `MainPodBuilder` that were loading literals to anchored keys: that was no longer needed after we introduced literal arguments
- backend
  - Only verify inclusion of the verifying key into the vd_set if the pod is MainPod.  A pod is not MainPod if the first statement is Intro.
  - Reject intro pods that have non-intro statements
  - Empty pod now returns an intro statement
  - Don't insert a type statement automatically in MainPod and MockMainPod.  We get rid of the type entry.
  - Implement `SignedBy` operation, which uses the muxed table to store signature verifications
- Rename `PodId` to `statements_hash` or `sts_hash` for short.  Now this is only used as a hash of the statements for the circuits public inputs.
- Refactor normalization of `self` statements:
  - Before: replace values that contain `SELF` by the given pod_id
  - After: place the verifying key hash into the Intro predicates
This commit is contained in:
Eduard S. 2025-08-27 13:19:40 +02:00 committed by GitHub
parent 122f9c3cac
commit 0e2f7b756e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 2127 additions and 3064 deletions

View file

@ -58,8 +58,8 @@ An example with keys, that expresses the same semantic meaning:
``` ```
ValueOf(local_varname, "local_ssn") ValueOf(local_varname, "local_ssn")
ValueOf(remote_varname, "ssn") ValueOf(remote_varname, "ssn")
ValueOf(gov_id_pod_id, 0x4030) ValueOf(gov_id_root, 0x4030)
ValueFromPodKey(local_varname, gov_id_pod_id, remote_varname) ValueFromPodKey(local_varname, gov_id_root, remote_varname)
``` ```
## Summary of additional statements in this spec ## Summary of additional statements in this spec

View file

@ -13,11 +13,11 @@ use std::env;
use pod2::{ use pod2::{
backends::plonky2::{ backends::plonky2::{
basetypes::DEFAULT_VD_SET, mainpod::Prover, mock::mainpod::MockProver, 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, lang::parse,
middleware::{Params, PodProver, PodType, VDSet, Value, KEY_SIGNER, KEY_TYPE}, middleware::{MainPodProver, Params, VDSet},
}; };
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
@ -34,7 +34,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mock_prover = MockProver {}; let mock_prover = MockProver {};
let real_prover = Prover {}; 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) (&VDSet::new(8, &[])?, &mock_prover)
} else { } else {
println!("Prebuilding circuits to calculate vd_set..."); println!("Prebuilding circuits to calculate vd_set...");
@ -50,7 +50,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let game_signer = Signer(game_sk); let game_signer = Signer(game_sk);
// Build 2 signed pods where the game assigns points to a player that has completed a level. // Build 2 signed pods where the game assigns points to a player that has completed a level.
let mut builder = SignedPodBuilder::new(&params); let mut builder = SignedDictBuilder::new(&params);
builder.insert("player", "Alice"); builder.insert("player", "Alice");
builder.insert("level", 1); builder.insert("level", 1);
builder.insert("points", 3512); builder.insert("points", 3512);
@ -58,7 +58,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
pod_points_lvl_1.verify()?; pod_points_lvl_1.verify()?;
println!("# pod_points_lvl_1:\n{}", pod_points_lvl_1); println!("# pod_points_lvl_1:\n{}", pod_points_lvl_1);
let mut builder = SignedPodBuilder::new(&params); let mut builder = SignedDictBuilder::new(&params);
builder.insert("player", "Alice"); builder.insert("player", "Alice");
builder.insert("level", 2); builder.insert("level", 2);
builder.insert("points", 5771); builder.insert("points", 5771);
@ -71,12 +71,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// Declare the custom predicate // Declare the custom predicate
let input = format!( let input = format!(
r#" r#"
points(player, level, points, private: points_pod) = AND( points(player, level, points, private: points_dict) = AND(
Equal(?points_pod["{key_type}"], {pod_type}) SignedBy(?points_dict, PublicKey({game_pk}))
Equal(?points_pod["{key_signer}"], {game_pk:#}) Contains(?points_dict, "player", ?player)
Equal(?points_pod["player"], ?player) Contains(?points_dict, "level", ?level)
Equal(?points_pod["level"], ?level) Contains(?points_dict, "points", ?points)
Equal(?points_pod["points"], ?points)
) )
over_9000(player, private: points_lvl_1, points_lvl_2, points_total) = AND( over_9000(player, private: points_lvl_1, points_lvl_2, points_total) = AND(
@ -86,10 +85,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Gt(?points_total, 9000) Gt(?points_total, 9000)
) )
"#, "#,
key_type = KEY_TYPE, game_pk = game_pk,
key_signer = KEY_SIGNER,
pod_type = PodType::Signed as usize,
game_pk = Value::from(game_pk).raw(),
); );
println!("# custom predicate batch:{}", input); println!("# custom predicate batch:{}", input);
let batch = parse(&input, &params, &[])?.custom_batch; let batch = parse(&input, &params, &[])?.custom_batch;
@ -98,37 +94,53 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// Build a pod to prove the statement `points("Alice", 1, 3512)` // Build a pod to prove the statement `points("Alice", 1, 3512)`
let mut builder = MainPodBuilder::new(&params, vd_set); let mut builder = MainPodBuilder::new(&params, vd_set);
builder.add_signed_pod(&pod_points_lvl_1); let st_signed_by = builder.priv_op(Operation::dict_signed_by(&pod_points_lvl_1))?;
let st_type = builder.priv_op(Operation::eq( let st_player = builder.priv_op(Operation::dict_contains(
(&pod_points_lvl_1, KEY_TYPE), pod_points_lvl_1.dict.clone(),
PodType::Signed, "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( let st_points_lvl_1 = builder.pub_op(Operation::custom(
points_pred.clone(), 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(); let pod_alice_lvl_1_points = builder.prove(prover).unwrap();
println!("# pod_alice_lvl_1_points\n:{}", pod_alice_lvl_1_points); println!("# pod_alice_lvl_1_points\n:{}", pod_alice_lvl_1_points);
pod_alice_lvl_1_points.pod.verify().unwrap(); pod_alice_lvl_1_points.pod.verify().unwrap();
// Build a pod to prove the statement `points("Alice", 2, 5771)` // Build a pod to prove the statement `points("Alice", 2, 5771)`
let mut builder = MainPodBuilder::new(&params, vd_set); let mut builder = MainPodBuilder::new(&params, vd_set);
builder.add_signed_pod(&pod_points_lvl_2); let st_signed_by = builder.priv_op(Operation::dict_signed_by(&pod_points_lvl_2))?;
let st_type = builder.priv_op(Operation::eq( let st_player = builder.priv_op(Operation::dict_contains(
(&pod_points_lvl_2, KEY_TYPE), pod_points_lvl_2.dict.clone(),
PodType::Signed, "player",
"Alice",
))?; ))?;
let st_signer = builder.priv_op(Operation::eq((&pod_points_lvl_2, KEY_SIGNER), game_pk))?; let st_level = builder.priv_op(Operation::dict_contains(
let st_player = builder.priv_op(Operation::eq((&pod_points_lvl_2, "player"), "Alice"))?; pod_points_lvl_2.dict.clone(),
let st_level = builder.priv_op(Operation::eq((&pod_points_lvl_2, "level"), 2))?; "level",
let st_points = builder.priv_op(Operation::eq((&pod_points_lvl_2, "points"), 5771))?; 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( let st_points_lvl_2 = builder.pub_op(Operation::custom(
points_pred, 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(); let pod_alice_lvl_2_points = builder.prove(prover).unwrap();
println!("# pod_alice_lvl_2_points\n:{}", pod_alice_lvl_2_points); println!("# pod_alice_lvl_2_points\n:{}", pod_alice_lvl_2_points);
@ -136,8 +148,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// Build a pod to prove the statement `over_9000("Alice")` // Build a pod to prove the statement `over_9000("Alice")`
let mut builder = MainPodBuilder::new(&params, vd_set); let mut builder = MainPodBuilder::new(&params, vd_set);
builder.add_recursive_pod(pod_alice_lvl_1_points); builder.add_pod(pod_alice_lvl_1_points);
builder.add_recursive_pod(pod_alice_lvl_2_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_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_gt_9000 = builder.priv_op(Operation::gt(3512 + 5771, 9000))?;
let _st_over_9000 = builder.pub_op(Operation::custom( let _st_over_9000 = builder.pub_op(Operation::custom(

View file

@ -1,27 +1,27 @@
#![allow(clippy::uninlined_format_args)] // TODO: Remove this in another PR #![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 std::collections::HashSet;
use pod2::{ use pod2::{
backends::plonky2::{primitives::ec::schnorr::SecretKey, signedpod::Signer}, backends::plonky2::{primitives::ec::schnorr::SecretKey, signer::Signer},
frontend::SignedPodBuilder, frontend::SignedDictBuilder,
middleware::{containers::Set, Params, Value}, middleware::{containers::Set, Params, Value},
}; };
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let params = Params::default(); 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 sk = SecretKey::new_rand();
let pk = sk.public_key(); let pk = sk.public_key();
println!("Public key: {}\n", pk); println!("Public key: {}\n", pk);
let signer = Signer(sk); let signer = Signer(sk);
// Build the signed pod // Build the signed dict
let mut builder = SignedPodBuilder::new(&params); let mut builder = SignedDictBuilder::new(&params);
// The values can be String, i64, bool, Array, Set, Dictionary, ... // The values can be String, i64, bool, Array, Set, Dictionary, ...
builder.insert("name", "Alice"); builder.insert("name", "Alice");
builder.insert("lucky_number", 42); builder.insert("lucky_number", 42);
@ -35,11 +35,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Set::new(params.max_merkle_proofs_containers, friends_set)?, Set::new(params.max_merkle_proofs_containers, friends_set)?,
); );
// Sign the pod and verify it // Sign the dict and verify it
let pod = builder.sign(&signer)?; let signed_dict = builder.sign(&signer)?;
pod.verify()?; signed_dict.verify()?;
println!("{}", pod); println!("{}", signed_dict);
Ok(()) Ok(())
} }

View file

@ -37,12 +37,17 @@ pub type CircuitBuilder = circuit_builder::CircuitBuilder<F, D>;
pub type Proof = proof::Proof<F, C, D>; pub type Proof = proof::Proof<F, C, D>;
pub type ProofWithPublicInputs = proof::ProofWithPublicInputs<F, C, D>; pub type ProofWithPublicInputs = proof::ProofWithPublicInputs<F, C, D>;
pub type HashOut = hash_types::HashOut<F>; pub type HashOut = hash_types::HashOut<F>;
use std::{collections::HashMap, sync::LazyLock}; 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::{ use crate::{
backends::plonky2::{ backends::plonky2::{
emptypod::cache_get_standard_empty_pod_verifier_circuit_data,
mainpod::cache_get_rec_main_pod_verifier_circuit_data, mainpod::cache_get_rec_main_pod_verifier_circuit_data,
primitives::merkletree::MerkleClaimAndProof, primitives::merkletree::MerkleClaimAndProof,
}, },
@ -51,14 +56,12 @@ use crate::{
pub static DEFAULT_VD_LIST: LazyLock<Vec<VerifierOnlyCircuitData>> = LazyLock::new(|| { pub static DEFAULT_VD_LIST: LazyLock<Vec<VerifierOnlyCircuitData>> = LazyLock::new(|| {
let params = Params::default(); let params = Params::default();
vec![ // NOTE: We only include the recursive MainPod with default parameters here. We don't need to
cache_get_rec_main_pod_verifier_circuit_data(&params) // 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(&params)
.verifier_only .verifier_only
.clone(), .clone()]
cache_get_standard_empty_pod_verifier_circuit_data()
.verifier_only
.clone(),
]
}); });
pub static DEFAULT_VD_SET: LazyLock<VDSet> = LazyLock::new(|| { pub static DEFAULT_VD_SET: LazyLock<VDSet> = LazyLock::new(|| {
@ -144,23 +147,16 @@ impl VDSet {
self.root self.root
} }
/// returns the vector of merkle proofs corresponding to the given verifier_datas /// returns the vector of merkle proofs corresponding to the given verifier_datas
pub fn get_vds_proofs( pub fn get_vds_proof(&self, vd: &VerifierOnlyCircuitData) -> Result<MerkleClaimAndProof> {
&self,
vds: &[VerifierOnlyCircuitData],
) -> Result<Vec<MerkleClaimAndProof>> {
let mut proofs: Vec<MerkleClaimAndProof> = vec![];
for vd in vds {
let verifier_data_hash = let verifier_data_hash =
crate::backends::plonky2::recursion::circuit::hash_verifier_data(vd); crate::backends::plonky2::recursion::circuit::hash_verifier_data(vd);
let p = self Ok(self
.proofs_map .proofs_map
.get(&Hash(verifier_data_hash.elements)) .get(&Hash(verifier_data_hash.elements))
.ok_or(crate::middleware::Error::custom( .ok_or(crate::middleware::Error::custom(
"verifier_data not found in VDSet".to_string(), "verifier_data not found in VDSet".to_string(),
))?; ))?
proofs.push(p.clone()); .clone())
}
Ok(proofs)
} }
/// Returns true if the `verifier_data_hash` is in the set /// Returns true if the `verifier_data_hash` is in the set
pub fn contains(&self, verifier_data_hash: HashOut) -> bool { pub fn contains(&self, verifier_data_hash: HashOut) -> bool {

View file

@ -111,10 +111,10 @@ impl StatementArgTarget {
pub fn anchored_key( pub fn anchored_key(
_builder: &mut CircuitBuilder, _builder: &mut CircuitBuilder,
pod_id: &ValueTarget, dict: &ValueTarget,
key: &ValueTarget, key: &ValueTarget,
) -> Self { ) -> Self {
Self::new(*pod_id, *key) Self::new(*dict, *key)
} }
pub fn wildcard_literal(builder: &mut CircuitBuilder, value: &ValueTarget) -> Self { 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( pub fn set_targets(
&self, &self,
pw: &mut PartialWitness<F>, pw: &mut PartialWitness<F>,

File diff suppressed because it is too large Load diff

View file

@ -3,5 +3,4 @@ pub mod hash;
pub mod mainpod; pub mod mainpod;
pub mod metrics; pub mod metrics;
pub mod mux_table; pub mod mux_table;
pub mod signedpod;
pub mod utils; pub mod utils;

View file

@ -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<F, D>,
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<MerkleProofExistenceTarget>,
pub(crate) signature: SignatureVerifyTarget,
}
impl SignedPodVerifyTarget {
pub fn new_virtual(params: &Params, builder: &mut CircuitBuilder<F, D>) -> 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<F, D>,
self_id: bool,
) -> Vec<StatementTarget> {
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<F>, 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(&params);
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<dyn Any>).downcast::<SignedPod>().unwrap();
// use the pod in the circuit
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let mut pw = PartialWitness::<F>::new();
// build the circuit logic
let signed_pod_verify = SignedPodVerifyTarget::new_virtual(&params, &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::<C>();
let proof = data.prove(pw)?;
data.verify(proof)?;
Ok(())
}
}

View file

@ -3,7 +3,6 @@ use plonky2::{
hash::hash_types::HashOutTarget, hash::hash_types::HashOutTarget,
iop::witness::{PartialWitness, WitnessWrite}, iop::witness::{PartialWitness, WitnessWrite},
plonk::{ plonk::{
circuit_builder::CircuitBuilder,
circuit_data::{self, CircuitConfig}, circuit_data::{self, CircuitConfig},
proof::ProofWithPublicInputs, proof::ProofWithPublicInputs,
}, },
@ -12,16 +11,16 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
backends::plonky2::{ backends::plonky2::{
basetypes::{Proof, C, D}, basetypes::{CircuitBuilder, Proof, C, D},
cache_get_standard_rec_main_pod_common_circuit_data, cache_get_standard_rec_main_pod_common_circuit_data,
circuits::{ circuits::{
common::{Flattenable, StatementTarget}, common::{Flattenable, StatementTarget},
mainpod::{calculate_id_circuit, PI_OFFSET_ID}, mainpod::{calculate_statements_hash_circuit, PI_OFFSET_STATEMENTS_HASH},
}, },
deserialize_proof, deserialize_verifier_only, deserialize_proof, deserialize_verifier_only,
error::{Error, Result}, error::{Error, Result},
hash_common_data, hash_common_data,
mainpod::{self, calculate_id}, mainpod::{self, calculate_statements_hash},
recursion::pad_circuit, recursion::pad_circuit,
serialization::{ serialization::{
CircuitDataSerializer, VerifierCircuitDataSerializer, VerifierOnlyCircuitDataSerializer, CircuitDataSerializer, VerifierCircuitDataSerializer, VerifierOnlyCircuitDataSerializer,
@ -30,52 +29,57 @@ use crate::{
}, },
cache::{self, CacheEntry}, cache::{self, CacheEntry},
middleware::{ middleware::{
self, AnchoredKey, Hash, Params, Pod, PodId, PodType, RecursivePod, Statement, ToFields, self, Hash, IntroPredicateRef, Params, Pod, PodType, Statement, ToFields, VDSet,
VDSet, Value, VerifierOnlyCircuitData, F, HASH_SIZE, KEY_TYPE, SELF, VerifierOnlyCircuitData, EMPTY_HASH, F, HASH_SIZE,
}, },
timed, timed,
}; };
struct EmptyPodVerifyCircuit { fn empty_statement() -> Statement {
params: Params, Statement::Intro(
} IntroPredicateRef {
name: "empty".to_string(),
fn type_statement() -> Statement { args_len: 0,
Statement::equal( verifier_data_hash: EMPTY_HASH,
AnchoredKey::from((SELF, KEY_TYPE)), },
Value::from(PodType::Empty), vec![],
) )
} }
impl EmptyPodVerifyCircuit {
fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<EmptyPodVerifyTarget> {
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)] #[derive(Clone, Serialize, Deserialize)]
pub struct EmptyPodVerifyTarget { pub struct EmptyPodVerifyTarget {
vds_root: HashOutTarget, vds_root: HashOutTarget,
} }
impl EmptyPodVerifyTarget { 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<F>, vds_root: Hash) -> Result<()> { pub fn set_targets(&self, pw: &mut PartialWitness<F>, vds_root: Hash) -> Result<()> {
Ok(pw.set_target_arr(&self.vds_root.elements, &vds_root.0)?) 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)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct EmptyPod { pub struct EmptyPod {
params: Params, params: Params,
id: PodId, sts_hash: Hash,
verifier_only: VerifierOnlyCircuitDataSerializer, verifier_only: VerifierOnlyCircuitDataSerializer,
common_hash: String, common_hash: String,
vd_set: VDSet, vd_set: VDSet,
@ -110,17 +114,15 @@ fn build() -> Result<(EmptyPodVerifyTarget, CircuitData)> {
#[cfg(feature = "zk")] #[cfg(feature = "zk")]
let config = CircuitConfig::standard_recursion_zk_config(); let config = CircuitConfig::standard_recursion_zk_config();
let mut builder = CircuitBuilder::<F, D>::new(config); let mut builder = CircuitBuilder::new(config);
let empty_pod_verify_target = EmptyPodVerifyCircuit { let empty_pod = EmptyPodVerifyTarget::new_virtual(&mut builder);
params: params.clone(), verify_empty_pod_circuit(&params, &mut builder, &empty_pod);
}
.eval(&mut builder)?;
let common_circuit_data = &*cache_get_standard_rec_main_pod_common_circuit_data(); let common_circuit_data = &*cache_get_standard_rec_main_pod_common_circuit_data();
pad_circuit(&mut builder, common_circuit_data); pad_circuit(&mut builder, common_circuit_data);
let data = timed!("EmptyPod build", builder.build::<C>()); let data = timed!("EmptyPod build", builder.build::<C>());
assert_eq!(common_circuit_data.0, data.common); assert_eq!(common_circuit_data.0, data.common);
Ok((empty_pod_verify_target, data)) Ok((empty_pod, data))
} }
impl EmptyPod { impl EmptyPod {
@ -130,19 +132,22 @@ impl EmptyPod {
let mut pw = PartialWitness::<F>::new(); let mut pw = PartialWitness::<F>::new();
empty_pod_verify_target.set_targets(&mut pw, vd_set.root())?; empty_pod_verify_target.set_targets(&mut pw, vd_set.root())?;
let proof = timed!("EmptyPod prove", data.prove(pw)?); let proof = timed!("EmptyPod prove", data.prove(pw)?);
let id = &proof.public_inputs[PI_OFFSET_ID..PI_OFFSET_ID + HASH_SIZE]; let sts_hash = {
let id = PodId(Hash([id[0], id[1], id[2], id[3]])); 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"); let common_hash = hash_common_data(&data.common).expect("hash ok");
Ok(EmptyPod { Ok(EmptyPod {
params: params.clone(), params: params.clone(),
verifier_only: VerifierOnlyCircuitDataSerializer(data.verifier_only.clone()), verifier_only: VerifierOnlyCircuitDataSerializer(data.verifier_only.clone()),
common_hash, common_hash,
id, sts_hash,
vd_set, vd_set,
proof: proof.proof, proof: proof.proof,
}) })
} }
pub fn new_boxed(params: &Params, vd_set: VDSet) -> Box<dyn RecursivePod> { pub fn new_boxed(params: &Params, vd_set: VDSet) -> Box<dyn Pod> {
let default_params = Params::default(); let default_params = Params::default();
assert_eq!(default_params.id_params(), params.id_params()); assert_eq!(default_params.id_params(), params.id_params());
@ -173,12 +178,12 @@ impl Pod for EmptyPod {
.into_iter() .into_iter()
.map(mainpod::Statement::from) .map(mainpod::Statement::from)
.collect_vec(); .collect_vec();
let id = PodId(calculate_id(&statements, &self.params)); let sts_hash = calculate_statements_hash(&statements, &self.params);
if id != self.id { if sts_hash != self.sts_hash {
return Err(Error::id_not_equal(self.id, id)); 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) .to_fields(&self.params)
.iter() .iter()
.chain(self.vd_set.root().0.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)) .map_err(|e| Error::plonky2_proof_fail("EmptyPod", e))
} }
fn id(&self) -> PodId { fn statements_hash(&self) -> Hash {
self.id self.sts_hash
} }
fn pod_type(&self) -> (usize, &'static str) { fn pod_type(&self) -> (usize, &'static str) {
(PodType::Empty as usize, "Empty") (PodType::Empty as usize, "Empty")
} }
fn pub_self_statements(&self) -> Vec<middleware::Statement> { fn pub_self_statements(&self) -> Vec<middleware::Statement> {
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 { fn verifier_data(&self) -> VerifierOnlyCircuitData {
self.verifier_only.0.clone() self.verifier_only.0.clone()
} }
@ -228,24 +222,33 @@ impl RecursivePod for EmptyPod {
fn vd_set(&self) -> &VDSet { fn vd_set(&self) -> &VDSet {
&self.vd_set &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( fn deserialize_data(
params: Params, params: Params,
data: serde_json::Value, data: serde_json::Value,
vd_set: VDSet, vd_set: VDSet,
id: PodId, sts_hash: Hash,
) -> Result<Box<dyn RecursivePod>> { ) -> Result<Self> {
let data: Data = serde_json::from_value(data)?; let data: Data = serde_json::from_value(data)?;
let common_circuit_data = cache_get_standard_rec_main_pod_common_circuit_data(); let common_circuit_data = cache_get_standard_rec_main_pod_common_circuit_data();
let proof = deserialize_proof(&common_circuit_data, &data.proof)?; let proof = deserialize_proof(&common_circuit_data, &data.proof)?;
let verifier_only = deserialize_verifier_only(&data.verifier_only)?; let verifier_only = deserialize_verifier_only(&data.verifier_only)?;
Ok(Box::new(Self { Ok(Self {
params, params,
id, sts_hash,
verifier_only: VerifierOnlyCircuitDataSerializer(verifier_only), verifier_only: VerifierOnlyCircuitDataSerializer(verifier_only),
common_hash: data.common_hash, common_hash: data.common_hash,
vd_set, vd_set,
proof, proof,
})) })
} }
} }

View file

@ -1,21 +1,15 @@
use std::{backtrace::Backtrace, fmt::Debug}; use std::{backtrace::Backtrace, fmt::Debug};
use crate::middleware::{PodId, PodType, Value}; use crate::middleware::Hash;
pub type Result<T, E = Error> = core::result::Result<T, E>; pub type Result<T, E = Error> = core::result::Result<T, E>;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum InnerError { pub enum InnerError {
#[error("id does not match, expected {0}, found {1}")] #[error("Statements hash does not match, expected {0}, found {1}")]
IdNotEqual(PodId, PodId), StsHashNotEqual(Hash, Hash),
#[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),
// POD related // POD related
#[error("invalid POD ID")]
PodIdInvalid,
#[error("verification failed: POD does not have type statement")] #[error("verification failed: POD does not have type statement")]
NotTypeStatement, NotTypeStatement,
#[error("repeated ValueOf")] #[error("repeated ValueOf")]
@ -85,16 +79,7 @@ impl Error {
pub fn not_type_statement() -> Self { pub fn not_type_statement() -> Self {
new!(NotTypeStatement) new!(NotTypeStatement)
} }
pub fn pod_id_invalid() -> Self { pub fn statements_hash_not_equal(expected: Hash, found: Hash) -> Self {
new!(PodIdInvalid) new!(StsHashNotEqual(expected, found))
}
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))
} }
} }

View file

@ -1,8 +1,10 @@
pub mod operation; pub mod operation;
use crate::middleware::PodType;
pub mod statement; pub mod statement;
use std::{any::Any, iter, sync::Arc}; use std::{iter, sync::Arc};
use itertools::Itertools; use itertools::Itertools;
use num_bigint::BigUint;
pub use operation::*; pub use operation::*;
use plonky2::{hash::poseidon::PoseidonHash, plonk::config::Hasher}; use plonky2::{hash::poseidon::PoseidonHash, plonk::config::Hasher};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -10,7 +12,7 @@ pub use statement::*;
use crate::{ use crate::{
backends::plonky2::{ backends::plonky2::{
basetypes::{CircuitData, Proof, ProofWithPublicInputs, VerifierOnlyCircuitData}, basetypes::{CircuitData, Proof, ProofWithPublicInputs, VerifierOnlyCircuitData, F},
cache::{self, CacheEntry}, cache::{self, CacheEntry},
cache_get_standard_rec_main_pod_common_circuit_data, cache_get_standard_rec_main_pod_common_circuit_data,
circuits::mainpod::{CustomPredicateVerification, MainPodVerifyInput, MainPodVerifyTarget}, circuits::mainpod::{CustomPredicateVerification, MainPodVerifyInput, MainPodVerifyTarget},
@ -20,7 +22,10 @@ use crate::{
hash_common_data, hash_common_data,
mock::emptypod::MockEmptyPod, mock::emptypod::MockEmptyPod,
primitives::{ primitives::{
ec::schnorr::SecretKey, ec::{
curve::Point as PublicKey,
schnorr::{SecretKey, Signature},
},
merkletree::{MerkleClaimAndProof, MerkleTreeStateTransitionProof}, merkletree::{MerkleClaimAndProof, MerkleTreeStateTransitionProof},
}, },
recursion::{ recursion::{
@ -30,32 +35,31 @@ use crate::{
CircuitDataSerializer, CommonCircuitDataSerializer, VerifierCircuitDataSerializer, CircuitDataSerializer, CommonCircuitDataSerializer, VerifierCircuitDataSerializer,
}, },
serialize_proof, serialize_verifier_only, serialize_proof, serialize_verifier_only,
signedpod::SignedPod,
}, },
middleware::{ middleware::{
self, resolve_wildcard_values, value_from_op, AnchoredKey, CustomPredicateBatch, self, resolve_wildcard_values, value_from_op, CustomPredicateBatch,
Error as MiddlewareError, Hash, MainPodInputs, NativeOperation, OperationType, Params, Pod, Error as MiddlewareError, Hash, MainPodInputs, MainPodProver, NativeOperation,
PodId, PodProver, PodType, RecursivePod, StatementArg, ToFields, VDSet, KEY_TYPE, SELF, OperationType, Params, Pod, RawValue, StatementArg, ToFields, VDSet,
}, },
timed, timed,
}; };
/// Hash a list of public statements to derive the PodId. To make circuits with different number /// Hash a list of public statements to derive the Statements hash. To make circuits with
/// of `max_public_statements compatible we pad the statements up to `num_public_statements_id`. /// different number of `max_public_statements compatible we pad the statements up to
/// As an optimization we front pad with none-statements so that circuits with a small /// `num_public_statements_id`. As an optimization we front pad with none-statements so that
/// `max_public_statements` only pay for `max_public_statements` by starting the poseidon state /// circuits with a small `max_public_statements` only pay for `max_public_statements` by starting
/// with a precomputed constant corresponding to the front-padding part: /// the poseidon state with a precomputed constant corresponding to the front-padding part: `id =
/// `id = hash(serialize(reverse(statements || none-statements)))` /// hash(serialize(reverse(statements || none-statements)))`
pub fn calculate_id(statements: &[Statement], params: &Params) -> middleware::Hash { pub fn calculate_statements_hash(statements: &[Statement], params: &Params) -> middleware::Hash {
assert!(statements.len() <= params.num_public_statements_id); assert!(statements.len() <= params.num_public_statements_hash);
assert!(params.max_public_statements <= params.num_public_statements_id); assert!(params.max_public_statements <= params.num_public_statements_hash);
let mut none_st: Statement = middleware::Statement::None.into(); let mut none_st: Statement = middleware::Statement::None.into();
pad_statement(params, &mut none_st); pad_statement(params, &mut none_st);
let statements_back_padded = statements let statements_back_padded = statements
.iter() .iter()
.chain(iter::repeat(&none_st)) .chain(iter::repeat(&none_st))
.take(params.num_public_statements_id) .take(params.num_public_statements_hash)
.collect_vec(); .collect_vec();
let field_elems = statements_back_padded let field_elems = statements_back_padded
.iter() .iter()
@ -236,6 +240,60 @@ pub(crate) fn extract_public_key_of(
Ok(table) 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<Vec<SignedBy>> {
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. /// 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<OperationArg> { fn find_op_arg(statements: &[Statement], op_arg: &middleware::Statement) -> Result<OperationArg> {
// NOTE: The `None` `Statement` always exists as a constant at index 0 // NOTE: The `None` `Statement` always exists as a constant at index 0
@ -283,37 +341,17 @@ pub(crate) fn layout_statements(
// predicate statements // predicate statements
statements.push(middleware::Statement::None.into()); statements.push(middleware::Statement::None.into());
// Input signed pods region // Input pods region
let dummy_signed_pod_box: Box<dyn Pod> = Box::new(SignedPod::dummy()); let empty_pod_box: Box<dyn Pod> = if mock || inputs.pods.len() == params.max_input_pods {
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<dyn RecursivePod> =
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 // We mocking or we don't need padding so we skip creating an EmptyPod
MockEmptyPod::new_boxed(params, inputs.vd_set.clone()) MockEmptyPod::new_boxed(params, inputs.vd_set.clone())
} else { } else {
EmptyPod::new_boxed(params, inputs.vd_set.clone()) EmptyPod::new_boxed(params, inputs.vd_set.clone())
}; };
let empty_pod = empty_pod_box.as_ref(); let empty_pod = empty_pod_box.as_ref();
assert!(inputs.recursive_pods.len() <= params.max_input_recursive_pods); assert!(inputs.pods.len() <= params.max_input_pods);
for i in 0..params.max_input_recursive_pods { for i in 0..params.max_input_pods {
let pod = inputs.recursive_pods.get(i).copied().unwrap_or(empty_pod); let pod = inputs.pods.get(i).copied().unwrap_or(empty_pod);
let sts = pod.pub_statements(); let sts = pod.pub_statements();
assert!(sts.len() <= params.max_public_statements); assert!(sts.len() <= params.max_public_statements);
for j in 0..params.max_input_pods_public_statements { for j in 0..params.max_input_pods_public_statements {
@ -347,20 +385,7 @@ pub(crate) fn layout_statements(
// Public statements // Public statements
assert!(inputs.public_statements.len() < params.max_public_statements); assert!(inputs.public_statements.len() < params.max_public_statements);
let pod_type = if mock { for i in 0..params.max_public_statements {
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) {
let mut st = inputs let mut st = inputs
.public_statements .public_statements
.get(i) .get(i)
@ -373,7 +398,7 @@ pub(crate) fn layout_statements(
let offset_public_statements = statements.len() - params.max_public_statements; let offset_public_statements = statements.len() - params.max_public_statements;
let public_statements = 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(); .to_vec();
Ok((statements, public_statements)) Ok((statements, public_statements))
} }
@ -413,13 +438,7 @@ pub(crate) fn process_public_statements_operations(
mut operations: Vec<Operation>, mut operations: Vec<Operation>,
) -> Result<Vec<Operation>> { ) -> Result<Vec<Operation>> {
let offset_public_statements = statements.len() - params.max_public_statements; let offset_public_statements = statements.len() - params.max_public_statements;
operations.push(Operation( for st in statements.iter().skip(offset_public_statements) {
OperationType::Native(NativeOperation::NewEntry),
vec![],
OperationAux::None,
));
for i in 0..(params.max_public_statements - 1) {
let st = &statements[offset_public_statements + i + 1];
let mut op = if st.is_none() { let mut op = if st.is_none() {
Operation( Operation(
OperationType::Native(NativeOperation::None), OperationType::Native(NativeOperation::None),
@ -442,44 +461,28 @@ pub(crate) fn process_public_statements_operations(
pub struct Prover {} pub struct Prover {}
impl PodProver for Prover { impl MainPodProver for Prover {
fn prove( fn prove(&self, params: &Params, inputs: MainPodInputs) -> Result<Box<dyn Pod>> {
&self,
params: &Params,
vd_set: &VDSet,
inputs: MainPodInputs,
) -> Result<Box<dyn RecursivePod>> {
let signed_pods_input: Vec<SignedPod> = inputs
.signed_pods
.iter()
.map(|p| {
let p = (*p as &dyn Any)
.downcast_ref::<SignedPod>()
.expect("type SignedPod");
p.clone()
})
.collect_vec();
// Pad input recursive pods with empty pods if necessary // 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 // We don't need padding so we skip creating an EmptyPod
MockEmptyPod::new_boxed(params, inputs.vd_set.clone()) MockEmptyPod::new_boxed(params, inputs.vd_set.clone())
} else { } else {
EmptyPod::new_boxed(params, inputs.vd_set.clone()) EmptyPod::new_boxed(params, inputs.vd_set.clone())
}; };
let inputs = MainPodInputs { let inputs = MainPodInputs {
recursive_pods: &inputs pods: &inputs
.recursive_pods .pods
.iter() .iter()
.copied() .copied()
.chain(iter::repeat(&*empty_pod)) .chain(iter::repeat(&*empty_pod))
.take(params.max_input_recursive_pods) .take(params.max_input_pods)
.collect_vec(), .collect_vec(),
..inputs ..inputs
}; };
let recursive_pods_pub_self_statements = inputs let input_pods_pub_self_statements = inputs
.recursive_pods .pods
.iter() .iter()
.map(|pod| { .map(|pod| {
assert_eq!(params.id_params(), pod.params().id_params()); assert_eq!(params.id_params(), pod.params().id_params());
@ -500,6 +503,8 @@ impl PodProver for Prover {
)?; )?;
let public_key_of_sks = let public_key_of_sks =
extract_public_key_of(params, &mut aux_list, inputs.operations, inputs.statements)?; 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 = let merkle_tree_state_transition_proofs =
extract_merkle_tree_state_transition_proofs(params, &mut aux_list, inputs.operations)?; 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)?; let operations = process_public_statements_operations(params, &statements, operations)?;
// get the id out of the public statements // 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 common_hash: String = cache_get_rec_main_pod_common_hash(params).clone();
let proofs = inputs let proofs = inputs
.recursive_pods .pods
.iter() .iter()
.map(|pod| { .map(|pod| {
assert_eq!(pod.common_hash(), common_hash); assert_eq!(pod.common_hash(), common_hash);
assert_eq!(inputs.vd_set.root(), pod.vd_set().root()); assert_eq!(inputs.vd_set.root(), pod.vd_set().root());
ProofWithPublicInputs { ProofWithPublicInputs {
proof: pod.proof(), 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(); .collect_vec();
let verifier_datas = inputs let verifier_datas = inputs
.recursive_pods .pods
.iter() .iter()
.map(|pod| pod.verifier_data()) .map(|pod| pod.verifier_data())
.collect_vec(); .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 { let input = MainPodVerifyInput {
vds_set: inputs.vd_set.clone(), vds_set: inputs.vd_set.clone(),
vd_mt_proofs, vd_mt_proofs,
signed_pods: signed_pods_input, input_pods_pub_self_statements,
recursive_pods_pub_self_statements,
statements: statements[statements.len() - params.max_statements..].to_vec(), statements: statements[statements.len() - params.max_statements..].to_vec(),
operations, operations,
merkle_proofs, merkle_proofs,
public_key_of_sks, public_key_of_sks,
signed_bys,
merkle_tree_state_transition_proofs, merkle_tree_state_transition_proofs,
custom_predicate_batches, custom_predicate_batches,
custom_predicate_verifications, custom_predicate_verifications,
@ -567,7 +588,7 @@ impl PodProver for Prover {
params: params.clone(), params: params.clone(),
verifier_only: circuit_data.verifier_only.clone(), verifier_only: circuit_data.verifier_only.clone(),
common_hash, common_hash,
id, sts_hash,
vd_set: inputs.vd_set, vd_set: inputs.vd_set,
public_statements, public_statements,
proof: proof_with_pis.proof, proof: proof_with_pis.proof,
@ -578,7 +599,7 @@ impl PodProver for Prover {
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct MainPod { pub struct MainPod {
params: Params, params: Params,
id: PodId, sts_hash: Hash,
verifier_only: VerifierOnlyCircuitData, verifier_only: VerifierOnlyCircuitData,
common_hash: String, common_hash: String,
/// vds_root is the merkle-root of the `VDSet`, which contains the /// 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!( timed!(
"recursive MainPod circuit_data padded", "recursive MainPod circuit_data padded",
RecursiveCircuit::<MainPodVerifyTarget>::target_and_circuit_data_padded( RecursiveCircuit::<MainPodVerifyTarget>::target_and_circuit_data_padded(
params.max_input_recursive_pods, params.max_input_pods,
&rec_common_circuit_data, &rec_common_circuit_data,
params, params,
) )
@ -676,6 +697,9 @@ impl Pod for MainPod {
fn params(&self) -> &Params { fn params(&self) -> &Params {
&self.params &self.params
} }
fn is_main(&self) -> bool {
true
}
fn verify(&self) -> Result<()> { fn verify(&self) -> Result<()> {
// 0. Assert that the CommonCircuitData of the pod is the current one // 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); 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 // 2. get the id out of the public statements
let id = PodId(calculate_id(&self.public_statements, &self.params)); let sts_hash = calculate_statements_hash(&self.public_statements, &self.params);
if id != self.id { if sts_hash != self.sts_hash {
return Err(Error::id_not_equal(self.id, id)); return Err(Error::statements_hash_not_equal(self.sts_hash, sts_hash));
} }
// 7. verifier_data_hash is in the VDSet // 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 // 1, 3, 4, 5 verification via the zkSNARK proof
let rec_main_pod_verifier_circuit_data = let rec_main_pod_verifier_circuit_data =
&*cache_get_rec_main_pod_verifier_circuit_data(&self.params); &*cache_get_rec_main_pod_verifier_circuit_data(&self.params);
let public_inputs = id let public_inputs = sts_hash
.to_fields(&self.params) .to_fields(&self.params)
.iter() .iter()
.chain(self.vd_set.root().0.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)) .map_err(|e| Error::plonky2_proof_fail("MainPod", e))
} }
fn id(&self) -> PodId { fn statements_hash(&self) -> Hash {
self.id self.sts_hash
} }
fn pod_type(&self) -> (usize, &'static str) { fn pod_type(&self) -> (usize, &'static str) {
(PodType::Main as usize, "Main") (PodType::Main as usize, "Main")
@ -734,18 +758,6 @@ impl Pod for MainPod {
.collect() .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 { fn verifier_data(&self) -> VerifierOnlyCircuitData {
self.verifier_only.clone() self.verifier_only.clone()
} }
@ -758,30 +770,42 @@ impl RecursivePod for MainPod {
fn vd_set(&self) -> &VDSet { fn vd_set(&self) -> &VDSet {
&self.vd_set &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( fn deserialize_data(
params: Params, params: Params,
data: serde_json::Value, data: serde_json::Value,
vd_set: VDSet, vd_set: VDSet,
id: PodId, id: Hash,
) -> Result<Box<dyn RecursivePod>> { ) -> Result<Self> {
let data: Data = serde_json::from_value(data)?; let data: Data = serde_json::from_value(data)?;
let common = cache_get_rec_main_pod_common_circuit_data(&params); let common = cache_get_rec_main_pod_common_circuit_data(&params);
let proof = deserialize_proof(&common, &data.proof)?; let proof = deserialize_proof(&common, &data.proof)?;
let verifier_only = deserialize_verifier_only(&data.verifier_only)?; let verifier_only = deserialize_verifier_only(&data.verifier_only)?;
Ok(Box::new(Self { Ok(Self {
params, params,
id, sts_hash: id,
verifier_only, verifier_only,
common_hash: data.common_hash, common_hash: data.common_hash,
vd_set, vd_set,
proof, proof,
public_statements: data.public_statements, public_statements: data.public_statements,
})) })
} }
} }
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use std::{any::Any, collections::HashSet};
use num::{BigUint, One}; use num::{BigUint, One};
use super::*; use super::*;
@ -789,18 +813,19 @@ pub mod tests {
backends::plonky2::{ backends::plonky2::{
mock::mainpod::{MockMainPod, MockProver}, mock::mainpod::{MockMainPod, MockProver},
primitives::ec::schnorr::SecretKey, primitives::ec::schnorr::SecretKey,
signedpod::Signer, signer::Signer,
}, },
dict,
examples::{ examples::{
attest_eth_friend, tickets_pod_full_flow, zu_kyc_pod_builder, zu_kyc_sign_pod_builders, attest_eth_friend, tickets_pod_full_flow, zu_kyc_pod_builder,
EthDosHelper, zu_kyc_sign_dict_builders, EthDosHelper,
}, },
frontend::{ frontend::{
self, literal, CustomPredicateBatchBuilder, MainPodBuilder, StatementTmplBuilder as STB, self, literal, CustomPredicateBatchBuilder, MainPodBuilder, StatementTmplBuilder as STB,
}, },
middleware::{ middleware::{
self, containers::Set, CustomPredicateRef, NativePredicate as NP, DEFAULT_VD_LIST, self, containers::Set, CustomPredicateRef, NativePredicate as NP, Signer as _,
DEFAULT_VD_SET, DEFAULT_VD_LIST, DEFAULT_VD_SET,
}, },
}; };
@ -809,7 +834,7 @@ pub mod tests {
let params = middleware::Params { let params = middleware::Params {
// Currently the circuit uses random access that only supports vectors of length 64. // 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. // 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_batches: 0,
max_custom_predicate_verifications: 0, max_custom_predicate_verifications: 0,
..Default::default() ..Default::default()
@ -819,7 +844,7 @@ pub mod tests {
vds.push(rec_main_pod_circuit_data(&params).1.verifier_only.clone()); vds.push(rec_main_pod_circuit_data(&params).1.verifier_only.clone());
let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap(); let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap();
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(&params); let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_dict_builders(&params);
let signer = Signer(SecretKey(BigUint::one())); let signer = Signer(SecretKey(BigUint::one()));
let gov_id_pod = gov_id_builder.sign(&signer)?; let gov_id_pod = gov_id_builder.sign(&signer)?;
let signer = Signer(SecretKey(2u64.into())); let signer = Signer(SecretKey(2u64.into()));
@ -850,9 +875,8 @@ pub mod tests {
#[test] #[test]
fn test_mini_0() { fn test_mini_0() {
let params = middleware::Params { let params = middleware::Params {
max_input_signed_pods: 1, max_signed_by: 1,
max_input_recursive_pods: 1, max_input_pods: 1,
max_signed_pod_values: 6,
max_statements: 8, max_statements: 8,
max_public_statements: 4, max_public_statements: 4,
max_input_pods_public_statements: 10, max_input_pods_public_statements: 10,
@ -862,7 +886,7 @@ pub mod tests {
vds.push(rec_main_pod_circuit_data(&params).1.verifier_only.clone()); vds.push(rec_main_pod_circuit_data(&params).1.verifier_only.clone());
let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap(); let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap();
let mut gov_id_builder = frontend::SignedPodBuilder::new(&params); let mut gov_id_builder = frontend::SignedDictBuilder::new(&params);
gov_id_builder.insert("idNumber", "4242424242"); gov_id_builder.insert("idNumber", "4242424242");
gov_id_builder.insert("dateOfBirth", 1169909384); gov_id_builder.insert("dateOfBirth", 1169909384);
gov_id_builder.insert("socialSecurityNumber", "G2121210"); gov_id_builder.insert("socialSecurityNumber", "G2121210");
@ -870,7 +894,10 @@ pub mod tests {
let gov_id = gov_id_builder.sign(&signer).unwrap(); let gov_id = gov_id_builder.sign(&signer).unwrap();
let now_minus_18y: i64 = 1169909388; let now_minus_18y: i64 = 1169909388;
let mut kyc_builder = frontend::MainPodBuilder::new(&params, &vd_set); let mut kyc_builder = frontend::MainPodBuilder::new(&params, &vd_set);
kyc_builder.add_signed_pod(&gov_id);
kyc_builder
.priv_op(frontend::Operation::dict_signed_by(&gov_id))
.unwrap();
kyc_builder kyc_builder
.pub_op(frontend::Operation::lt( .pub_op(frontend::Operation::lt(
(&gov_id, "dateOfBirth"), (&gov_id, "dateOfBirth"),
@ -902,9 +929,8 @@ pub mod tests {
#[test] #[test]
fn test_mini_1() { fn test_mini_1() {
let params = middleware::Params { let params = middleware::Params {
max_input_signed_pods: 0, max_signed_by: 0,
max_input_recursive_pods: 0, max_input_pods: 0,
max_signed_pod_values: 0,
max_statements: 2, max_statements: 2,
max_public_statements: 1, max_public_statements: 1,
max_input_pods_public_statements: 0, max_input_pods_public_statements: 0,
@ -939,13 +965,12 @@ pub mod tests {
#[test] #[test]
fn test_mainpod_small_empty() { fn test_mainpod_small_empty() {
let params = middleware::Params { let params = middleware::Params {
max_input_signed_pods: 0, max_signed_by: 0,
max_input_recursive_pods: 0, max_input_pods: 0,
max_input_pods_public_statements: 2, max_input_pods_public_statements: 2,
max_statements: 5, max_statements: 5,
max_signed_pod_values: 2,
max_public_statements: 2, max_public_statements: 2,
num_public_statements_id: 4, num_public_statements_hash: 4,
max_statement_args: 4, max_statement_args: 4,
max_operation_args: 4, max_operation_args: 4,
max_custom_predicate_batches: 2, max_custom_predicate_batches: 2,
@ -996,7 +1021,7 @@ pub mod tests {
let alice_attestation = attest_eth_friend(&params, &alice, bob.public_key()); let alice_attestation = attest_eth_friend(&params, &alice, bob.public_key());
let bob_attestation = attest_eth_friend(&params, &bob, charlie.public_key()); let bob_attestation = attest_eth_friend(&params, &bob, charlie.public_key());
let helper = EthDosHelper::new(&params, vd_set, false, alice.public_key())?; let helper = EthDosHelper::new(&params, vd_set, alice.public_key())?;
let prover = Prover {}; let prover = Prover {};
let dist_1 = helper.dist_1(&alice_attestation)?.prove(&prover)?; let dist_1 = helper.dist_1(&alice_attestation)?.prove(&prover)?;
crate::measure_gates_print!(); crate::measure_gates_print!();
@ -1010,8 +1035,8 @@ pub mod tests {
#[test] #[test]
fn test_main_mini_custom_1() -> frontend::Result<()> { fn test_main_mini_custom_1() -> frontend::Result<()> {
let params = Params { let params = Params {
max_input_signed_pods: 0, max_signed_by: 0,
max_input_recursive_pods: 0, max_input_pods: 0,
max_statements: 9, max_statements: 9,
max_public_statements: 4, max_public_statements: 4,
max_statement_args: 4, max_statement_args: 4,
@ -1020,7 +1045,7 @@ pub mod tests {
max_custom_batch_size: 3, max_custom_batch_size: 3,
max_custom_predicate_wildcards: 4, max_custom_predicate_wildcards: 4,
max_custom_predicate_verifications: 2, max_custom_predicate_verifications: 2,
max_merkle_proofs_containers: 0, max_merkle_proofs_containers: 3,
max_merkle_tree_state_transition_proofs_containers: 0, max_merkle_tree_state_transition_proofs_containers: 0,
..Default::default() ..Default::default()
}; };
@ -1030,17 +1055,20 @@ pub mod tests {
let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap(); let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap();
let mut cpb_builder = CustomPredicateBatchBuilder::new(params.clone(), "cpb".into()); 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) let stb1 = STB::new(NP::Equal)
.arg(("secret_id", "key")) .arg(("secret_dict", "key"))
.arg(("id", "score")); .arg(("dict", "score"));
let _ = cpb_builder.predicate_and( let _ = cpb_builder.predicate_and(
"pred_and", "pred_and",
&["id"], &["dict"],
&["secret_id"], &["secret_dict"],
&[stb0.clone(), stb1.clone()], &[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 = cpb_builder.finish();
let cpb_and = CustomPredicateRef::new(cpb.clone(), 0); let cpb_and = CustomPredicateRef::new(cpb.clone(), 0);
@ -1048,9 +1076,17 @@ pub mod tests {
let mut pod_builder = MainPodBuilder::new(&params, &vd_set); let mut pod_builder = MainPodBuilder::new(&params, &vd_set);
let st0 = pod_builder.priv_op(frontend::Operation::new_entry("score", 42))?; let dict = dict!(32, {"score" => 42})?;
let st1 = pod_builder.priv_op(frontend::Operation::new_entry("key", 42))?; let secret_dict = dict!(32, {"key" => 42})?;
let st2 = pod_builder.priv_op(frontend::Operation::eq(st1.clone(), st0.clone()))?; 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]))?; 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<()> { fn test_set_contains() -> frontend::Result<()> {
let params = Params::default(); let params = Params::default();
let mut builder = MainPodBuilder::new(&params, &DEFAULT_VD_SET); let mut builder = MainPodBuilder::new(&params, &DEFAULT_VD_SET);
let set = [1, 2, 3].into_iter().map(|n| n.into()).collect(); let set: HashSet<_> = [1, 2, 3].into_iter().map(|n| n.into()).collect();
let st = builder let set = Set::new(params.max_depth_mt_containers, set).unwrap();
.pub_op(frontend::Operation::new_entry( builder.pub_op(frontend::Operation::set_contains(set, 1))?;
"entry",
Set::new(params.max_depth_mt_containers, set).unwrap(),
))
.unwrap();
builder.pub_op(frontend::Operation::set_contains(st, 1))?;
let prover = Prover {}; let prover = Prover {};
let proof = builder.prove(&prover).unwrap(); let proof = builder.prove(&prover).unwrap();

View file

@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
backends::plonky2::{ backends::plonky2::{
error::{Error, Result}, error::{Error, Result},
mainpod::Statement, mainpod::{SignedBy, Statement},
primitives::merkletree::{MerkleClaimAndProof, MerkleTreeStateTransitionProof}, primitives::merkletree::{MerkleClaimAndProof, MerkleTreeStateTransitionProof},
}, },
middleware::{self, OperationType, Params}, middleware::{self, OperationType, Params},
@ -35,6 +35,7 @@ pub enum OperationAux {
None, None,
MerkleProofIndex(usize), MerkleProofIndex(usize),
PublicKeyOfIndex(usize), PublicKeyOfIndex(usize),
SignedByIndex(usize),
MerkleTreeStateTransitionProofIndex(usize), MerkleTreeStateTransitionProofIndex(usize),
CustomPredVerifyIndex(usize), CustomPredVerifyIndex(usize),
} }
@ -47,9 +48,12 @@ impl OperationAux {
fn table_offset_public_key_of(params: &Params) -> usize { fn table_offset_public_key_of(params: &Params) -> usize {
Self::table_offset_merkle_proof(params) + params.max_merkle_proofs_containers 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 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 { fn table_offset_custom_pred_verify(params: &Params) -> usize {
Self::table_offset_merkle_tree_state_transition_proof(params) Self::table_offset_merkle_tree_state_transition_proof(params)
+ params.max_merkle_tree_state_transition_proofs_containers + params.max_merkle_tree_state_transition_proofs_containers
@ -57,6 +61,7 @@ impl OperationAux {
pub(crate) fn table_size(params: &Params) -> usize { pub(crate) fn table_size(params: &Params) -> usize {
1 + params.max_merkle_proofs_containers 1 + params.max_merkle_proofs_containers
+ params.max_public_key_of + params.max_public_key_of
+ params.max_signed_by
+ params.max_merkle_tree_state_transition_proofs_containers + params.max_merkle_tree_state_transition_proofs_containers
+ params.max_custom_predicate_verifications + params.max_custom_predicate_verifications
} }
@ -65,6 +70,7 @@ impl OperationAux {
Self::None => 0, Self::None => 0,
Self::MerkleProofIndex(i) => Self::table_offset_merkle_proof(params) + *i, Self::MerkleProofIndex(i) => Self::table_offset_merkle_proof(params) + *i,
Self::PublicKeyOfIndex(i) => Self::table_offset_public_key_of(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::MerkleTreeStateTransitionProofIndex(i) => {
Self::table_offset_merkle_tree_state_transition_proof(params) + *i Self::table_offset_merkle_tree_state_transition_proof(params) + *i
} }
@ -89,6 +95,7 @@ impl Operation {
pub fn deref( pub fn deref(
&self, &self,
statements: &[Statement], statements: &[Statement],
signatures: &[SignedBy],
merkle_proofs: &[MerkleClaimAndProof], merkle_proofs: &[MerkleClaimAndProof],
merkle_tree_state_transition_proofs: &[MerkleTreeStateTransitionProof], merkle_tree_state_transition_proofs: &[MerkleTreeStateTransitionProof],
) -> Result<crate::middleware::Operation> { ) -> Result<crate::middleware::Operation> {
@ -125,6 +132,13 @@ impl Operation {
.clone(), .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, OperationAux::PublicKeyOfIndex(_) => crate::middleware::OperationAux::None,
}; };
Ok(middleware::Operation::op( Ok(middleware::Operation::op(
@ -154,6 +168,7 @@ impl fmt::Display for Operation {
OperationAux::MerkleProofIndex(i) => write!(f, " merkle_proof_{:02}", i)?, OperationAux::MerkleProofIndex(i) => write!(f, " merkle_proof_{:02}", i)?,
OperationAux::CustomPredVerifyIndex(i) => write!(f, " custom_pred_verify_{:02}", i)?, OperationAux::CustomPredVerifyIndex(i) => write!(f, " custom_pred_verify_{:02}", i)?,
OperationAux::PublicKeyOfIndex(i) => write!(f, " public_key_of_{:02}", i)?, OperationAux::PublicKeyOfIndex(i) => write!(f, " public_key_of_{:02}", i)?,
OperationAux::SignedByIndex(i) => write!(f, " signed_by_{:02}", i)?,
OperationAux::MerkleTreeStateTransitionProofIndex(i) => { OperationAux::MerkleTreeStateTransitionProofIndex(i) => {
write!(f, " merkle_tree_state_transition_proof_{:02}", i)? write!(f, " merkle_tree_state_transition_proof_{:02}", i)?
} }

View file

@ -74,6 +74,7 @@ impl TryFrom<Statement> for middleware::Statement {
S::HashOf(a1.try_into()?, a2.try_into()?, a3.try_into()?) S::HashOf(a1.try_into()?, a2.try_into()?, a3.try_into()?)
} }
(NP::PublicKeyOf, &[a1, a2]) => S::PublicKeyOf(a1.try_into()?, a2.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( (NP::ContainerInsert, &[a1, a2, a3, a4]) => S::ContainerInsert(
a1.try_into()?, a1.try_into()?,
a2.try_into()?, a2.try_into()?,
@ -105,6 +106,17 @@ impl TryFrom<Statement> for middleware::Statement {
.collect(); .collect();
S::Custom(cpr, vs) S::Custom(cpr, vs)
} }
Predicate::Intro(ir) => {
let vs: Vec<Value> = 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(_) => { Predicate::BatchSelf(_) => {
unreachable!() unreachable!()
} }
@ -123,6 +135,10 @@ impl From<middleware::Statement> for Statement {
middleware::Predicate::Custom(cpr), middleware::Predicate::Custom(cpr),
s.args().into_iter().collect(), s.args().into_iter().collect(),
), ),
middleware::Predicate::Intro(ir) => Statement(
middleware::Predicate::Intro(ir),
s.args().into_iter().collect(),
),
middleware::Predicate::BatchSelf(_) => unreachable!(), middleware::Predicate::BatchSelf(_) => unreachable!(),
} }
} }

View file

@ -4,35 +4,36 @@ use crate::{
backends::plonky2::{ backends::plonky2::{
basetypes::{Proof, VerifierOnlyCircuitData}, basetypes::{Proof, VerifierOnlyCircuitData},
error::{Error, Result}, error::{Error, Result},
mainpod::{self, calculate_id}, mainpod::{self, calculate_statements_hash},
},
middleware::{
AnchoredKey, Params, Pod, PodId, PodType, RecursivePod, Statement, VDSet, Value, KEY_TYPE,
SELF,
}, },
middleware::{Hash, IntroPredicateRef, Params, Pod, PodType, Statement, VDSet, EMPTY_HASH},
}; };
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct MockEmptyPod { pub struct MockEmptyPod {
params: Params, params: Params,
id: PodId, sts_hash: Hash,
vd_set: VDSet, vd_set: VDSet,
} }
fn type_statement() -> Statement { fn empty_statement() -> Statement {
Statement::equal( Statement::Intro(
AnchoredKey::from((SELF, KEY_TYPE)), IntroPredicateRef {
Value::from(PodType::Empty), name: "mock_empty".to_string(),
args_len: 0,
verifier_data_hash: EMPTY_HASH,
},
vec![],
) )
} }
impl MockEmptyPod { impl MockEmptyPod {
pub fn new_boxed(params: &Params, vd_set: VDSet) -> Box<dyn RecursivePod> { pub fn new_boxed(params: &Params, vd_set: VDSet) -> Box<dyn Pod> {
let statements = [mainpod::Statement::from(type_statement())]; let statements = [mainpod::Statement::from(empty_statement())];
let id = PodId(calculate_id(&statements, params)); let sts_hash = calculate_statements_hash(&statements, params);
Box::new(Self { Box::new(Self {
params: params.clone(), params: params.clone(),
id, sts_hash,
vd_set, vd_set,
}) })
} }
@ -48,28 +49,25 @@ impl Pod for MockEmptyPod {
.into_iter() .into_iter()
.map(mainpod::Statement::from) .map(mainpod::Statement::from)
.collect_vec(); .collect_vec();
let id = PodId(calculate_id(&statements, &self.params)); let sts_hash = calculate_statements_hash(&statements, &self.params);
if id != self.id { if sts_hash != self.sts_hash {
return Err(Error::id_not_equal(self.id, id)); return Err(Error::statements_hash_not_equal(self.sts_hash, sts_hash));
} }
Ok(()) Ok(())
} }
fn id(&self) -> PodId { fn statements_hash(&self) -> Hash {
self.id self.sts_hash
} }
fn pod_type(&self) -> (usize, &'static str) { fn pod_type(&self) -> (usize, &'static str) {
(PodType::MockEmpty as usize, "MockEmpty") (PodType::MockEmpty as usize, "MockEmpty")
} }
fn pub_self_statements(&self) -> Vec<Statement> { fn pub_self_statements(&self) -> Vec<Statement> {
vec![type_statement()] vec![empty_statement()]
} }
fn serialize_data(&self) -> serde_json::Value { fn verifier_data_hash(&self) -> Hash {
serde_json::Value::Null EMPTY_HASH
} }
}
impl RecursivePod for MockEmptyPod {
fn verifier_data(&self) -> VerifierOnlyCircuitData { fn verifier_data(&self) -> VerifierOnlyCircuitData {
panic!("MockEmptyPod can't be verified in a recursive MainPod circuit"); panic!("MockEmptyPod can't be verified in a recursive MainPod circuit");
} }
@ -82,13 +80,20 @@ impl RecursivePod for MockEmptyPod {
fn vd_set(&self) -> &VDSet { fn vd_set(&self) -> &VDSet {
&self.vd_set &self.vd_set
} }
fn serialize_data(&self) -> serde_json::Value {
serde_json::Value::Null
}
fn deserialize_data( fn deserialize_data(
params: Params, params: Params,
_data: serde_json::Value, _data: serde_json::Value,
vd_set: VDSet, vd_set: VDSet,
id: PodId, id: Hash,
) -> Result<Box<dyn RecursivePod>> { ) -> Result<Self> {
Ok(Box::new(Self { params, id, vd_set })) Ok(Self {
params,
sts_hash: id,
vd_set,
})
} }
} }

View file

@ -4,7 +4,6 @@
use std::{fmt, iter}; use std::{fmt, iter};
use itertools::Itertools;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
@ -12,31 +11,25 @@ use crate::{
basetypes::{Proof, VerifierOnlyCircuitData}, basetypes::{Proof, VerifierOnlyCircuitData},
error::{Error, Result}, error::{Error, Result},
mainpod::{ mainpod::{
calculate_id, extract_merkle_proofs, extract_merkle_tree_state_transition_proofs, calculate_statements_hash, extract_merkle_proofs,
layout_statements, process_private_statements_operations, extract_merkle_tree_state_transition_proofs, extract_signatures, layout_statements,
process_public_statements_operations, Operation, OperationAux, Statement, process_private_statements_operations, process_public_statements_operations, Operation,
OperationAux, SignedBy, Statement,
}, },
mock::emptypod::MockEmptyPod, mock::emptypod::MockEmptyPod,
primitives::merkletree::{MerkleClaimAndProof, MerkleTreeStateTransitionProof}, primitives::merkletree::{MerkleClaimAndProof, MerkleTreeStateTransitionProof},
recursion::hash_verifier_data, recursion::hash_verifier_data,
signedpod::SignedPod,
}, },
middleware::{ middleware::{
self, deserialize_pod, deserialize_signed_pod, hash_str, AnchoredKey, Hash, MainPodInputs, self, deserialize_pod, Hash, MainPodInputs, MainPodProver, Params, Pod, PodType, VDSet,
NativeOperation, NativePredicate, OperationType, Params, Pod, PodId, PodProver, PodType, EMPTY_HASH,
Predicate, RecursivePod, StatementArg, VDSet, Value, KEY_TYPE, SELF,
}, },
}; };
pub struct MockProver {} pub struct MockProver {}
impl PodProver for MockProver { impl MainPodProver for MockProver {
fn prove( fn prove(&self, params: &Params, inputs: MainPodInputs) -> Result<Box<dyn Pod>> {
&self,
params: &Params,
_vd_set: &VDSet,
inputs: MainPodInputs,
) -> Result<Box<dyn RecursivePod>> {
Ok(Box::new(MockMainPod::new(params, inputs)?)) Ok(Box::new(MockMainPod::new(params, inputs)?))
} }
} }
@ -44,10 +37,9 @@ impl PodProver for MockProver {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct MockMainPod { pub struct MockMainPod {
params: Params, params: Params,
id: PodId, sts_hash: Hash,
vd_set: VDSet, vd_set: VDSet,
input_signed_pods: Vec<Box<dyn Pod>>, input_pods: Vec<Box<dyn Pod>>,
input_recursive_pods: Vec<Box<dyn RecursivePod>>,
// All statements (inherited + newly introduced by this pod) // All statements (inherited + newly introduced by this pod)
statements: Vec<Statement>, statements: Vec<Statement>,
operations: Vec<Operation>, operations: Vec<Operation>,
@ -57,42 +49,28 @@ pub struct MockMainPod {
merkle_proofs_containers: Vec<MerkleClaimAndProof>, merkle_proofs_containers: Vec<MerkleClaimAndProof>,
// All Merkle tree state transition proofs // All Merkle tree state transition proofs
merkle_tree_state_transition_proofs_containers: Vec<MerkleTreeStateTransitionProof>, merkle_tree_state_transition_proofs_containers: Vec<MerkleTreeStateTransitionProof>,
// All verified signatures
signatures: Vec<SignedBy>,
} }
impl Eq for MockMainPod {} impl Eq for MockMainPod {}
impl fmt::Display for MockMainPod { impl fmt::Display for MockMainPod {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "MockMainPod ({}):", self.id)?; writeln!(f, "MockMainPod ({}):", self.sts_hash)?;
let offset_input_signed_pods = Self::offset_input_signed_pods(); let offset_input_pods = self.offset_input_pods();
let offset_input_recursive_pods = self.offset_input_recursive_pods();
let offset_input_statements = self.offset_input_statements(); let offset_input_statements = self.offset_input_statements();
let offset_public_statements = self.offset_public_statements(); let offset_public_statements = self.offset_public_statements();
for (i, st) in self.statements.iter().enumerate() { for (i, st) in self.statements.iter().enumerate() {
if self.params.max_input_signed_pods > 0 if self.params.max_input_pods > 0
&& (i >= offset_input_signed_pods && i < offset_input_recursive_pods) && (i >= offset_input_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)
&& (i < offset_input_statements) && (i < offset_input_statements)
&& (i - offset_input_recursive_pods) && (i - offset_input_pods)
.is_multiple_of(self.params.max_input_pods_public_statements) .is_multiple_of(self.params.max_input_pods_public_statements)
{ {
let index = (i - offset_input_recursive_pods) let index = (i - offset_input_pods) / self.params.max_input_pods_public_statements;
/ self.params.max_input_pods_public_statements; let pod = &self.input_pods[index];
let pod = &self.input_recursive_pods[index]; let id = pod.statements_hash();
let id = pod.id();
let pod_type = pod.pod_type(); let pod_type = pod.pod_type();
writeln!( writeln!(
f, f,
@ -148,26 +126,21 @@ struct Data {
statements: Vec<Statement>, statements: Vec<Statement>,
merkle_proofs: Vec<MerkleClaimAndProof>, merkle_proofs: Vec<MerkleClaimAndProof>,
merkle_tree_state_transition_proofs: Vec<MerkleTreeStateTransitionProof>, merkle_tree_state_transition_proofs: Vec<MerkleTreeStateTransitionProof>,
input_signed_pods: Vec<(usize, PodId, serde_json::Value)>, signatures: Vec<SignedBy>,
input_recursive_pods: Vec<(usize, Params, PodId, VDSet, serde_json::Value)>, input_pods: Vec<(usize, Params, Hash, VDSet, serde_json::Value)>,
} }
/// Inputs are sorted as: /// Inputs are sorted as:
/// - SignedPods /// - Pods
/// - MainPods
/// - private Statements /// - private Statements
/// - public Statements /// - public Statements
impl MockMainPod { impl MockMainPod {
fn offset_input_signed_pods() -> usize { fn offset_input_pods(&self) -> usize {
1 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 { fn offset_input_statements(&self) -> usize {
self.offset_input_recursive_pods() self.offset_input_pods()
+ self.params.max_input_recursive_pods * self.params.max_input_pods_public_statements + self.params.max_input_pods * self.params.max_input_pods_public_statements
} }
fn offset_public_statements(&self) -> usize { fn offset_public_statements(&self) -> usize {
self.offset_input_statements() + self.params.max_priv_statements() self.offset_input_statements() + self.params.max_priv_statements()
@ -175,6 +148,7 @@ impl MockMainPod {
pub fn new(params: &Params, inputs: MainPodInputs) -> Result<Self> { pub fn new(params: &Params, inputs: MainPodInputs) -> Result<Self> {
let (statements, public_statements) = layout_statements(params, true, &inputs)?; let (statements, public_statements) = layout_statements(params, true, &inputs)?;
dbg!(public_statements.len());
let mut aux_list = vec![OperationAux::None; params.max_priv_statements()]; let mut aux_list = vec![OperationAux::None; params.max_priv_statements()];
// Extract Merkle proofs and pad. // Extract Merkle proofs and pad.
let merkle_proofs = let merkle_proofs =
@ -182,6 +156,8 @@ impl MockMainPod {
// Similarly for Merkle state transition proofs. // Similarly for Merkle state transition proofs.
let merkle_tree_state_transition_proofs = let merkle_tree_state_transition_proofs =
extract_merkle_tree_state_transition_proofs(params, &mut aux_list, inputs.operations)?; 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( let operations = process_private_statements_operations(
params, params,
@ -192,35 +168,27 @@ impl MockMainPod {
let operations = process_public_statements_operations(params, &statements, operations)?; let operations = process_public_statements_operations(params, &statements, operations)?;
// get the id out of the public statements // 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<dyn Pod> = Box::new(SignedPod::dummy());
let input_signed_pods: Vec<Box<dyn Pod>> = 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 pad_pod = MockEmptyPod::new_boxed(params, inputs.vd_set.clone());
let input_recursive_pods: Vec<Box<dyn RecursivePod>> = inputs let input_pods: Vec<Box<dyn Pod>> = inputs
.recursive_pods .pods
.iter() .iter()
.map(|p| dyn_clone::clone_box(*p)) .map(|p| dyn_clone::clone_box(*p))
.chain(iter::repeat_with(|| pad_pod.clone())) .chain(iter::repeat_with(|| pad_pod.clone()))
.take(params.max_input_recursive_pods) .take(params.max_input_pods)
.collect(); .collect();
Ok(Self { Ok(Self {
params: params.clone(), params: params.clone(),
id, sts_hash,
vd_set: inputs.vd_set, vd_set: inputs.vd_set,
input_signed_pods, input_pods,
input_recursive_pods,
public_statements, public_statements,
statements, statements,
operations, operations,
merkle_proofs_containers: merkle_proofs, merkle_proofs_containers: merkle_proofs,
merkle_tree_state_transition_proofs_containers: merkle_tree_state_transition_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 { fn params(&self) -> &Params {
&self.params &self.params
} }
fn is_mock(&self) -> bool {
true
}
fn is_main(&self) -> bool {
true
}
fn verify(&self) -> Result<()> { fn verify(&self) -> Result<()> {
// 1. Verify input pods for pod in &self.input_pods {
for pod in &self.input_signed_pods {
pod.verify()?;
}
for pod in &self.input_recursive_pods {
pod.verify()?; pod.verify()?;
if pod.vd_set().root() != self.vd_set.root() { if pod.vd_set().root() != self.vd_set.root() {
return Err(Error::custom(format!( return Err(Error::custom(format!(
@ -248,67 +218,38 @@ impl Pod for MockMainPod {
self.vd_set.root(), self.vd_set.root(),
))); )));
} }
let (pod_type, _) = pod.pod_type(); // If the pod is not mock and main (MainPod family) check that its verifier data is in
// If the pod is not mock, check that its verifier data is in the set // the set
if pod_type != PodType::MockMain as usize && pod_type != PodType::MockEmpty as usize { if !pod.is_mock() && pod.is_main() {
let verifier_data = pod.verifier_data(); let verifier_data = pod.verifier_data();
let verifier_data_hash = hash_verifier_data(&verifier_data); let verifier_data_hash = hash_verifier_data(&verifier_data);
if !self.vd_set.contains(verifier_data_hash) { if !self.vd_set.contains(verifier_data_hash) {
return Err(Error::custom(format!( 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), Hash(verifier_data_hash.elements),
self.vd_set.root(), 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(); let input_statement_offset = self.offset_input_statements();
// get the input_statements from the self.statements // get the input_statements from the self.statements
let input_statements = &self.statements[input_statement_offset..]; 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 // 5. verify that all `input_statements` are correctly generated
// by `self.operations` (where each operation can only access previous statements) // by `self.operations` (where each operation can only access previous statements)
@ -319,6 +260,7 @@ impl Pod for MockMainPod {
self.operations[i] self.operations[i]
.deref( .deref(
&self.statements[..input_statement_offset + i], &self.statements[..input_statement_offset + i],
&self.signatures,
&self.merkle_proofs_containers, &self.merkle_proofs_containers,
&self.merkle_tree_state_transition_proofs_containers, &self.merkle_tree_state_transition_proofs_containers,
)? )?
@ -332,8 +274,8 @@ impl Pod for MockMainPod {
Ok(()) Ok(())
} }
fn id(&self) -> PodId { fn statements_hash(&self) -> Hash {
self.id self.sts_hash
} }
fn pod_type(&self) -> (usize, &'static str) { fn pod_type(&self) -> (usize, &'static str) {
(PodType::MockMain as usize, "MockMain") (PodType::MockMain as usize, "MockMain")
@ -346,20 +288,31 @@ impl Pod for MockMainPod {
.collect() .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 { fn serialize_data(&self) -> serde_json::Value {
let input_signed_pods = self let input_pods = self
.input_signed_pods .input_pods
.iter()
.map(|p| (p.pod_type().0, p.id(), p.serialize_data()))
.collect();
let input_recursive_pods = self
.input_recursive_pods
.iter() .iter()
.map(|p| { .map(|p| {
( (
p.pod_type().0, p.pod_type().0,
p.params().clone(), p.params().clone(),
p.id(), p.statements_hash(),
p.vd_set().clone(), p.vd_set().clone(),
p.serialize_data(), p.serialize_data(),
) )
@ -373,26 +326,11 @@ impl Pod for MockMainPod {
merkle_tree_state_transition_proofs: self merkle_tree_state_transition_proofs: self
.merkle_tree_state_transition_proofs_containers .merkle_tree_state_transition_proofs_containers
.clone(), .clone(),
input_signed_pods, signatures: self.signatures.clone(),
input_recursive_pods, input_pods,
}) })
.expect("serialization to json") .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 // MockMainPods include some internal private state which is necessary
// for verification. In non-mock Pods, this state will not be necessary, // for verification. In non-mock Pods, this state will not be necessary,
// as the public statements can be verified using a ZK proof. // as the public statements can be verified using a ZK proof.
@ -400,39 +338,35 @@ impl RecursivePod for MockMainPod {
params: Params, params: Params,
data: serde_json::Value, data: serde_json::Value,
vd_set: VDSet, vd_set: VDSet,
id: PodId, id: Hash,
) -> Result<Box<dyn RecursivePod>> { ) -> Result<Self> {
let Data { let Data {
public_statements, public_statements,
operations, operations,
statements, statements,
merkle_proofs, merkle_proofs,
merkle_tree_state_transition_proofs, merkle_tree_state_transition_proofs,
input_signed_pods, signatures,
input_recursive_pods, input_pods,
} = serde_json::from_value(data)?; } = serde_json::from_value(data)?;
let input_signed_pods = input_signed_pods let input_pods = input_pods
.into_iter()
.map(|(pod_type, id, data)| deserialize_signed_pod(pod_type, id, data))
.collect::<Result<Vec<_>>>()?;
let input_recursive_pods = input_recursive_pods
.into_iter() .into_iter()
.map(|(pod_type, params, id, vd_set, data)| { .map(|(pod_type, params, id, vd_set, data)| {
deserialize_pod(pod_type, params, id, vd_set, data) deserialize_pod(pod_type, params, id, vd_set, data)
}) })
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
Ok(Box::new(Self { Ok(Self {
params, params,
id, sts_hash: id,
vd_set, vd_set,
input_signed_pods, input_pods,
input_recursive_pods,
public_statements, public_statements,
operations, operations,
statements, statements,
merkle_proofs_containers: merkle_proofs, merkle_proofs_containers: merkle_proofs,
merkle_tree_state_transition_proofs_containers: merkle_tree_state_transition_proofs, merkle_tree_state_transition_proofs_containers: merkle_tree_state_transition_proofs,
})) signatures,
})
} }
} }
@ -442,23 +376,24 @@ pub mod tests {
use super::*; use super::*;
use crate::{ use crate::{
backends::plonky2::{primitives::ec::schnorr::SecretKey, signedpod::Signer}, backends::plonky2::{primitives::ec::schnorr::SecretKey, signer::Signer},
examples::{ examples::{
great_boy_pod_full_flow, tickets_pod_full_flow, zu_kyc_pod_builder, zu_kyc_pod_request, 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, frontend, middleware,
middleware::{Signer as _, Value},
}; };
#[test] #[test]
fn test_mock_main_zu_kyc() -> frontend::Result<()> { fn test_mock_main_zu_kyc() -> frontend::Result<()> {
let params = middleware::Params::default(); let params = middleware::Params::default();
let vd_set = &*MOCK_VD_SET; let vd_set = &*MOCK_VD_SET;
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(&params); let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_dict_builders(&params);
let signer = Signer(SecretKey(1u32.into())); let gov_id_signer = Signer(SecretKey(1u32.into()));
let gov_id_pod = gov_id_builder.sign(&signer)?; let gov_id_pod = gov_id_builder.sign(&gov_id_signer)?;
let signer = Signer(SecretKey(2u32.into())); let pay_stub_signer = Signer(SecretKey(2u32.into()));
let pay_stub_pod = pay_stub_builder.sign(&signer)?; let pay_stub_pod = pay_stub_builder.sign(&pay_stub_signer)?;
let kyc_builder = zu_kyc_pod_builder(&params, vd_set, &gov_id_pod, &pay_stub_pod)?; let kyc_builder = zu_kyc_pod_builder(&params, vd_set, &gov_id_pod, &pay_stub_pod)?;
let prover = MockProver {}; let prover = MockProver {};
@ -472,8 +407,8 @@ pub mod tests {
pod.verify()?; pod.verify()?;
let request = zu_kyc_pod_request( let request = zu_kyc_pod_request(
gov_id_pod.get("_signer").unwrap(), &Value::from(gov_id_signer.public_key()),
pay_stub_pod.get("_signer").unwrap(), &Value::from(pay_stub_signer.public_key()),
)?; )?;
assert!(request.exact_match_pod(&*pod).is_ok()); assert!(request.exact_match_pod(&*pod).is_ok());

View file

@ -7,7 +7,7 @@ pub mod mock;
pub mod primitives; pub mod primitives;
pub mod recursion; pub mod recursion;
pub mod serialization; pub mod serialization;
pub mod signedpod; pub mod signer;
use std::iter; use std::iter;
@ -49,7 +49,7 @@ pub fn cache_get_standard_rec_main_pod_common_circuit_data(
let circuit_data = timed!( let circuit_data = timed!(
"recursive MainPod circuit_data", "recursive MainPod circuit_data",
RecursiveCircuit::<MainPodVerifyTarget>::target_and_circuit_data( RecursiveCircuit::<MainPodVerifyTarget>::target_and_circuit_data(
params.max_input_recursive_pods, params.max_input_pods,
NUM_PUBLIC_INPUTS, NUM_PUBLIC_INPUTS,
params params
) )

View file

@ -24,6 +24,7 @@ use plonky2::{
util::serialization::{Read, Write}, util::serialization::{Read, Write},
}; };
use rand::rngs::OsRng; use rand::rngs::OsRng;
use schemars::JsonSchema;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::backends::plonky2::{ use crate::backends::plonky2::{
@ -102,6 +103,16 @@ pub struct Point {
pub u: ECField, 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 {
<String>::json_schema(gen)
}
}
impl fmt::Display for Point { impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[allow(clippy::collapsible_else_if)] #[allow(clippy::collapsible_else_if)]

View file

@ -20,6 +20,7 @@ use plonky2::{
plonk::circuit_builder::CircuitBuilder, plonk::circuit_builder::CircuitBuilder,
}; };
use rand::rngs::OsRng; use rand::rngs::OsRng;
use schemars::JsonSchema;
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::curve::Point; use super::curve::Point;
@ -43,6 +44,16 @@ pub struct Signature {
pub e: BigUint, 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 {
<String>::json_schema(gen)
}
}
impl Signature { impl Signature {
pub fn verify(&self, public_key: Point, msg: RawValue) -> bool { pub fn verify(&self, public_key: Point, msg: RawValue) -> bool {
let r = &self.s * Point::generator() + &self.e * public_key; let r = &self.s * Point::generator() + &self.e * public_key;

View file

@ -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<Key, Value>,
) -> Result<SignedPod> {
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<Key, Value>) -> Result<SignedPod> {
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<Key, Value>) -> Result<Box<dyn Pod>> {
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<SignedPod> = 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<Box<dyn Pod>> {
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::<HashMap<RawValue, RawValue>>(),
)?;
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<Statement> {
// By convention we put the KEY_TYPE first and KEY_SIGNER second
let mut kvs: HashMap<Key, Value> = 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(&params);
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<dyn Any>).downcast::<SignedPod>().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::<HashMap<Key, Value>>();
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::<HashMap<Key, Value>>();
bad_pod.dict = Dictionary::new(params.max_depth_mt_containers, bad_kvs).unwrap();
assert!(bad_pod.verify().is_err());
Ok(())
}
}

View file

@ -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()
}
}

View file

@ -5,28 +5,15 @@ use hex::ToHex;
use crate::{ use crate::{
frontend::{PodRequest, Result}, frontend::{PodRequest, Result},
lang::parse, 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 /// Instantiates an ETHDos batch
pub fn eth_dos_batch(params: &Params) -> Result<Arc<CustomPredicateBatch>> { pub fn eth_dos_batch(params: &Params) -> Result<Arc<CustomPredicateBatch>> {
let input = render!( let input = r#"
r#" eth_friend(src, dst, private: attestation) = AND(
eth_friend(src, dst, private: attestation_pod) = AND( SignedBy(?attestation, ?src)
Equal(?attestation_pod[{KEY_TYPE}], {pod_type}) Contains(?attestation, "attestation", ?dst)
Equal(?attestation_pod[{KEY_SIGNER}], ?src)
Equal(?attestation_pod["attestation"], ?dst)
) )
eth_dos_base(src, dst, distance) = AND( eth_dos_base(src, dst, distance) = AND(
@ -44,10 +31,8 @@ pub fn eth_dos_batch(params: &Params) -> Result<Arc<CustomPredicateBatch>> {
eth_dos_base(?src, ?dst, ?distance) eth_dos_base(?src, ?dst, ?distance)
eth_dos_ind(?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.0. {}", batch.predicates[0]);
println!("a.1. {}", batch.predicates[1]); println!("a.1. {}", batch.predicates[1]);
println!("a.2. {}", batch.predicates[2]); println!("a.2. {}", batch.predicates[2]);

View file

@ -8,26 +8,26 @@ use num::BigUint;
pub static MOCK_VD_SET: LazyLock<VDSet> = LazyLock::new(|| VDSet::new(6, &[]).unwrap()); pub static MOCK_VD_SET: LazyLock<VDSet> = LazyLock::new(|| VDSet::new(6, &[]).unwrap());
use crate::{ use crate::{
backends::plonky2::{primitives::ec::schnorr::SecretKey, signedpod::Signer}, backends::plonky2::{primitives::ec::schnorr::SecretKey, signer::Signer},
frontend::{ frontend::{
MainPod, MainPodBuilder, Operation, PodRequest, Result, SignedPod, SignedPodBuilder, MainPod, MainPodBuilder, Operation, PodRequest, Result, SignedDict, SignedDictBuilder,
}, },
lang::parse, lang::parse,
middleware::{ middleware::{
containers::Set, hash_values, CustomPredicateRef, Params, PodSigner, PodType, Predicate, self, containers::Set, hash_values, CustomPredicateRef, Params, Predicate, PublicKey,
Statement, StatementArg, TypedValue, VDSet, Value, KEY_SIGNER, KEY_TYPE, Signer as _, Statement, StatementArg, TypedValue, VDSet, Value,
}, },
}; };
// ZuKYC // ZuKYC
pub fn zu_kyc_sign_pod_builders(params: &Params) -> (SignedPodBuilder, SignedPodBuilder) { pub fn zu_kyc_sign_dict_builders(params: &Params) -> (SignedDictBuilder, SignedDictBuilder) {
let mut gov_id = SignedPodBuilder::new(params); let mut gov_id = SignedDictBuilder::new(params);
gov_id.insert("idNumber", "4242424242"); gov_id.insert("idNumber", "4242424242");
gov_id.insert("dateOfBirth", 1169909384); gov_id.insert("dateOfBirth", 1169909384);
gov_id.insert("socialSecurityNumber", "G2121210"); 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("socialSecurityNumber", "G2121210");
pay_stub.insert("startDate", 1706367566); pay_stub.insert("startDate", 1706367566);
@ -41,8 +41,8 @@ pub const ZU_KYC_SANCTION_LIST: &[&str] = &["A343434340"];
pub fn zu_kyc_pod_builder( pub fn zu_kyc_pod_builder(
params: &Params, params: &Params,
vd_set: &VDSet, vd_set: &VDSet,
gov_id: &SignedPod, gov_id: &SignedDict,
pay_stub: &SignedPod, pay_stub: &SignedDict,
) -> Result<MainPodBuilder> { ) -> Result<MainPodBuilder> {
let now_minus_18y = ZU_KYC_NOW_MINUS_18Y; let now_minus_18y = ZU_KYC_NOW_MINUS_18Y;
let now_minus_1y = ZU_KYC_NOW_MINUS_1Y; 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()); Value::from(Set::new(params.max_depth_mt_containers, sanctions_values).unwrap());
let mut kyc = MainPodBuilder::new(params, vd_set); let mut kyc = MainPodBuilder::new(params, vd_set);
kyc.add_signed_pod(gov_id); kyc.pub_op(Operation::dict_signed_by(gov_id))?;
kyc.add_signed_pod(pay_stub); kyc.pub_op(Operation::dict_signed_by(pay_stub))?;
kyc.pub_op(Operation::set_not_contains( kyc.pub_op(Operation::set_not_contains(
sanction_set, sanction_set,
(gov_id, "idNumber"), (gov_id, "idNumber"),
@ -66,14 +67,6 @@ pub fn zu_kyc_pod_builder(
(pay_stub, "socialSecurityNumber"), (pay_stub, "socialSecurityNumber"),
))?; ))?;
kyc.pub_op(Operation::eq((pay_stub, "startDate"), now_minus_1y))?; 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) Ok(kyc)
} }
@ -93,8 +86,8 @@ pub fn zu_kyc_pod_request(gov_signer: &Value, pay_signer: &Value) -> Result<PodR
Lt(?gov["dateOfBirth"], {ZU_KYC_NOW_MINUS_18Y}) Lt(?gov["dateOfBirth"], {ZU_KYC_NOW_MINUS_18Y})
Equal(?pay["startDate"], {ZU_KYC_NOW_MINUS_1Y}) Equal(?pay["startDate"], {ZU_KYC_NOW_MINUS_1Y})
Equal(?gov["socialSecurityNumber"], ?pay["socialSecurityNumber"]) Equal(?gov["socialSecurityNumber"], ?pay["socialSecurityNumber"])
Equal(?gov["_signer"], {gov_signer}) SignedBy(?gov, {gov_signer})
Equal(?pay["_signer"], {pay_signer}) SignedBy(?pay, {pay_signer})
// TODO: Ownership check and watermarking // TODO: Ownership check and watermarking
// Depends partly on https://github.com/0xPARC/pod2/issues/351 // Depends partly on https://github.com/0xPARC/pod2/issues/351
) )
@ -106,8 +99,12 @@ pub fn zu_kyc_pod_request(gov_signer: &Value, pay_signer: &Value) -> Result<PodR
// ETHDoS // ETHDoS
pub fn attest_eth_friend(params: &Params, src: &impl PodSigner, dst: Value) -> SignedPod { pub fn attest_eth_friend(
let mut attestation = SignedPodBuilder::new(params); params: &Params,
src: &impl middleware::Signer,
dst: PublicKey,
) -> SignedDict {
let mut attestation = SignedDictBuilder::new(params);
attestation.insert("attestation", dst); attestation.insert("attestation", dst);
attestation.sign(src).unwrap() attestation.sign(src).unwrap()
} }
@ -115,16 +112,15 @@ pub fn attest_eth_friend(params: &Params, src: &impl PodSigner, dst: Value) -> S
pub struct EthDosHelper { pub struct EthDosHelper {
params: Params, params: Params,
vd_set: VDSet, vd_set: VDSet,
mock: bool,
eth_friend: CustomPredicateRef, eth_friend: CustomPredicateRef,
eth_dos_base: CustomPredicateRef, eth_dos_base: CustomPredicateRef,
eth_dos_ind: CustomPredicateRef, eth_dos_ind: CustomPredicateRef,
eth_dos: CustomPredicateRef, eth_dos: CustomPredicateRef,
src: Value, src: PublicKey,
} }
impl EthDosHelper { impl EthDosHelper {
pub fn new(params: &Params, vd_set: &VDSet, mock: bool, src: Value) -> Result<Self> { pub fn new(params: &Params, vd_set: &VDSet, src: PublicKey) -> Result<Self> {
let eth_dos_batch = eth_dos_batch(params)?; let eth_dos_batch = eth_dos_batch(params)?;
let eth_friend = eth_dos_batch.predicate_ref_by_name("eth_friend").unwrap(); 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(); let eth_dos_base = eth_dos_batch.predicate_ref_by_name("eth_dos_base").unwrap();
@ -133,7 +129,6 @@ impl EthDosHelper {
Ok(Self { Ok(Self {
params: params.clone(), params: params.clone(),
vd_set: vd_set.clone(), vd_set: vd_set.clone(),
mock,
eth_friend, eth_friend,
eth_dos_base, eth_dos_base,
eth_dos_ind, eth_dos_ind,
@ -142,16 +137,13 @@ impl EthDosHelper {
}) })
} }
pub fn dist_1(&self, src_attestation: &SignedPod) -> Result<MainPodBuilder> { pub fn dist_1(&self, src_attestation: &SignedDict) -> Result<MainPodBuilder> {
assert_eq!( assert_eq!(self.src, src_attestation.public_key);
&self.src,
src_attestation.get(KEY_SIGNER).expect("get KEY_SIGNER")
);
let mut pod = MainPodBuilder::new(&self.params, &self.vd_set); 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 distance_eq_zero = pod.priv_op(Operation::eq(0, 0))?;
let eth_dos_src_to_src_base = pod.priv_op(Operation::custom( let eth_dos_src_to_src_base = pod.priv_op(Operation::custom(
self.eth_dos_base.clone(), self.eth_dos_base.clone(),
@ -171,20 +163,10 @@ impl EthDosHelper {
pub fn dist_n_plus_1( pub fn dist_n_plus_1(
&self, &self,
eth_dos_src_to_int_pod: &MainPod, eth_dos_src_to_int_pod: &MainPod,
int_attestation: &SignedPod, // int signs dst int_attestation: &SignedDict, // int signs dst
) -> Result<MainPodBuilder> { ) -> Result<MainPodBuilder> {
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); let mut pod = MainPodBuilder::new(&self.params, &self.vd_set);
pod.add_signed_pod(int_attestation); pod.add_pod(eth_dos_src_to_int_pod.clone());
pod.add_recursive_pod(eth_dos_src_to_int_pod.clone());
let eth_dos_int_to_dst = eth_dos_src_to_int_pod let eth_dos_int_to_dst = eth_dos_src_to_int_pod
.pod .pod
@ -200,10 +182,7 @@ impl EthDosHelper {
_ => panic!("expected StatementArg::Literal"), _ => panic!("expected StatementArg::Literal"),
}) })
}; };
assert_eq!( assert_eq!(int, Value::from(int_attestation.public_key));
&int,
int_attestation.get(KEY_SIGNER).expect("get KEY_SIGNER")
);
let n_i64 = if let TypedValue::Int(x) = n.typed() { let n_i64 = if let TypedValue::Int(x) = n.typed() {
*x *x
@ -221,25 +200,15 @@ impl EthDosHelper {
&self, &self,
pod: &mut MainPodBuilder, pod: &mut MainPodBuilder,
eth_dos_int_to_dst: Statement, eth_dos_int_to_dst: Statement,
int_attestation: &SignedPod, int_attestation: &SignedDict,
n: i64, n: i64,
) -> Result<()> { ) -> Result<()> {
assert_eq!(
&Value::from(PodType::Signed),
int_attestation.get(KEY_TYPE).expect("get KEY_TYPE")
);
// eth_friend statement // eth_friend statement
let attestation_is_signed_pod = int_attestation.get_statement(KEY_TYPE).unwrap(); let attestation_signed_by_int = pod.priv_op(Operation::dict_signed_by(int_attestation))?;
let attestation_signed_by_int = int_attestation.get_statement(KEY_SIGNER).unwrap();
let int_attests_to_dst = int_attestation.get_statement("attestation").unwrap(); let int_attests_to_dst = int_attestation.get_statement("attestation").unwrap();
let ethfriends_int_dst = pod.priv_op(Operation::custom( let ethfriends_int_dst = pod.priv_op(Operation::custom(
self.eth_friend.clone(), self.eth_friend.clone(),
[ [attestation_signed_by_int, int_attests_to_dst],
attestation_is_signed_pod,
attestation_signed_by_int,
int_attests_to_dst,
],
))?; ))?;
// distance = n + 1 // distance = n + 1
@ -259,17 +228,17 @@ impl EthDosHelper {
// GreatBoy // GreatBoy
pub fn good_boy_sign_pod_builder(params: &Params, user: &Value, age: i64) -> SignedPodBuilder { pub fn good_boy_sign_pod_builder(params: &Params, user: &PublicKey, age: i64) -> SignedDictBuilder {
let mut good_boy = SignedPodBuilder::new(params); let mut good_boy = SignedDictBuilder::new(params);
good_boy.insert("user", user.clone()); good_boy.insert("user", *user);
good_boy.insert("age", age); good_boy.insert("age", age);
good_boy good_boy
} }
pub fn friend_sign_pod_builder(params: &Params, friend: &Value) -> SignedPodBuilder { pub fn friend_sign_pod_builder(params: &Params, friend: &PublicKey) -> SignedDictBuilder {
let mut friend_pod = SignedPodBuilder::new(params); let mut friend_pod = SignedDictBuilder::new(params);
friend_pod.insert("friend", friend.clone()); friend_pod.insert("friend", *friend);
friend_pod friend_pod
} }
@ -277,10 +246,10 @@ pub fn friend_sign_pod_builder(params: &Params, friend: &Value) -> SignedPodBuil
pub fn great_boy_pod_builder( pub fn great_boy_pod_builder(
params: &Params, params: &Params,
vd_set: &VDSet, vd_set: &VDSet,
good_boy_pods: [&SignedPod; 4], good_boy_signed_dicts: [&SignedDict; 4],
friend_pods: [&SignedPod; 2], friend_signed_dicts: [&SignedDict; 2],
good_boy_issuers: &Value, good_boy_issuers: &Value,
receiver: &Value, receiver: &PublicKey,
) -> Result<MainPodBuilder> { ) -> Result<MainPodBuilder> {
// Attestment chain (issuer -> good boy -> great boy): // Attestment chain (issuer -> good boy -> great boy):
// issuer 0 -> good_boy_pods[0] => good boy 0 // 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 // good boy 1 -> friend_pods[1] => receiver
let mut great_boy = MainPodBuilder::new(params, vd_set); let mut great_boy = MainPodBuilder::new(params, vd_set);
for good_boy_pod in good_boy_pods { for good_boy_signed_dict in good_boy_signed_dicts {
great_boy.add_signed_pod(good_boy_pod); great_boy.pub_op(Operation::dict_signed_by(good_boy_signed_dict))?;
} }
for friend_pod in friend_pods { for friend_signed_dict in friend_signed_dicts {
great_boy.add_signed_pod(friend_pod); great_boy.pub_op(Operation::dict_signed_by(friend_signed_dict))?;
} }
for good_boy_idx in 0..2 { 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 { 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 // Each good boy POD comes from a valid issuer
great_boy.pub_op(Operation::set_contains( great_boy.pub_op(Operation::set_contains(
good_boy_issuers, 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 // Each good boy has 2 good boy pods
great_boy.pub_op(Operation::eq( great_boy.pub_op(Operation::eq(
(good_boy_pods[good_boy_idx * 2 + issuer_idx], "user"), (good_boy_signed_dicts[good_boy_idx * 2 + issuer_idx], "user"),
(friend_pods[good_boy_idx], KEY_SIGNER), friend_signed_dicts[good_boy_idx].public_key,
))?; ))?;
} }
// The good boy PODs from each good boy have different issuers // The good boy PODs from each good boy have different issuers
great_boy.pub_op(Operation::ne( great_boy.pub_op(Operation::ne(
(good_boy_pods[good_boy_idx * 2], KEY_SIGNER), good_boy_signed_dicts[good_boy_idx * 2].public_key,
(good_boy_pods[good_boy_idx * 2 + 1], KEY_SIGNER), good_boy_signed_dicts[good_boy_idx * 2 + 1].public_key,
))?; ))?;
// Each good boy is receivers' friend // Each good boy is receivers' friend
great_boy.pub_op(Operation::eq( great_boy.pub_op(Operation::eq(
(friend_pods[good_boy_idx], "friend"), (friend_signed_dicts[good_boy_idx], "friend"),
receiver.clone(), *receiver,
))?; ))?;
} }
// The two good boys are different // The two good boys are different
great_boy.pub_op(Operation::ne( great_boy.pub_op(Operation::ne(
(friend_pods[0], KEY_SIGNER), friend_signed_dicts[0].public_key,
(friend_pods[1], KEY_SIGNER), friend_signed_dicts[1].public_key,
))?; ))?;
Ok(great_boy) Ok(great_boy)
@ -343,11 +302,11 @@ pub fn great_boy_pod_builder(
pub fn great_boy_pod_full_flow() -> Result<MainPodBuilder> { pub fn great_boy_pod_full_flow() -> Result<MainPodBuilder> {
let params = Params { let params = Params {
max_input_signed_pods: 6, max_signed_by: 6,
max_input_recursive_pods: 0, max_input_pods: 0,
max_statements: 100, max_statements: 100,
max_public_statements: 50, max_public_statements: 50,
num_public_statements_id: 50, num_public_statements_hash: 50,
..Default::default() ..Default::default()
}; };
let vd_set = &*MOCK_VD_SET; let vd_set = &*MOCK_VD_SET;
@ -413,13 +372,13 @@ pub fn great_boy_pod_full_flow() -> Result<MainPodBuilder> {
pub const TICKET_OWNER_SECRET_KEY: SecretKey = SecretKey(BigUint::ZERO); 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) // 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("eventId", 123);
builder.insert("productId", 456); builder.insert("productId", 456);
// Removed temporarily to make the example fit in 8 entries. // 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("attendeeEmail", "john.doe@example.com");
builder.insert("attendeePublicKey", TICKET_OWNER_SECRET_KEY.public_key()); builder.insert("attendeePublicKey", TICKET_OWNER_SECRET_KEY.public_key());
builder.insert("isConsumed", true); builder.insert("isConsumed", true);
@ -430,7 +389,7 @@ pub fn tickets_sign_pod_builder(params: &Params) -> SignedPodBuilder {
pub fn tickets_pod_builder( pub fn tickets_pod_builder(
params: &Params, params: &Params,
vd_set: &VDSet, vd_set: &VDSet,
signed_pod: &SignedPod, signed_dict: &SignedDict,
expected_event_id: i64, expected_event_id: i64,
expect_consumed: bool, expect_consumed: bool,
blacklisted_emails: &Set, blacklisted_emails: &Set,
@ -438,28 +397,28 @@ pub fn tickets_pod_builder(
let blacklisted_email_set_value = Value::from(TypedValue::Set(blacklisted_emails.clone())); let blacklisted_email_set_value = Value::from(TypedValue::Set(blacklisted_emails.clone()));
// Create a main pod referencing this signed pod with some statements // Create a main pod referencing this signed pod with some statements
let mut builder = MainPodBuilder::new(params, vd_set); let mut builder = MainPodBuilder::new(params, vd_set);
builder.add_signed_pod(signed_pod); builder.pub_op(Operation::dict_signed_by(signed_dict))?;
builder.pub_op(Operation::eq((signed_pod, "eventId"), expected_event_id))?; builder.pub_op(Operation::eq((signed_dict, "eventId"), expected_event_id))?;
builder.pub_op(Operation::eq((signed_pod, "isConsumed"), expect_consumed))?; builder.pub_op(Operation::eq((signed_dict, "isConsumed"), expect_consumed))?;
builder.pub_op(Operation::eq((signed_pod, "isRevoked"), false))?; builder.pub_op(Operation::eq((signed_dict, "isRevoked"), false))?;
builder.pub_op(Operation::dict_not_contains( builder.pub_op(Operation::dict_not_contains(
blacklisted_email_set_value, blacklisted_email_set_value,
(signed_pod, "attendeeEmail"), (signed_dict, "attendeeEmail"),
))?; ))?;
// This isn't the most fool-proof way to prove ownership (it requires // 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 // verifier to check pod ID on an anchored key to confirm statement wasn't
// copied), but it's the simplest. // 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( builder.pub_op(Operation::public_key_of(
(signed_pod, "attendeePublicKey"), (signed_dict, "attendeePublicKey"),
st_sk.clone(), sk.clone(),
))?; ))?;
// Nullifier calculation is public, but based on the private sk. // Nullifier calculation is public, but based on the private sk.
let external_nullifier = "external nullifier"; let external_nullifier = "external nullifier";
let nullifier = hash_values(&[TICKET_OWNER_SECRET_KEY.into(), external_nullifier.into()]); 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) Ok(builder)
} }
@ -467,11 +426,11 @@ pub fn tickets_pod_builder(
pub fn tickets_pod_full_flow(params: &Params, vd_set: &VDSet) -> Result<MainPodBuilder> { pub fn tickets_pod_full_flow(params: &Params, vd_set: &VDSet) -> Result<MainPodBuilder> {
let builder = tickets_sign_pod_builder(params); 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( tickets_pod_builder(
params, params,
vd_set, vd_set,
&signed_pod, &signed_dict,
123, 123,
true, true,
&Set::new(params.max_depth_mt_containers, HashSet::new())?, &Set::new(params.max_depth_mt_containers, HashSet::new())?,

View file

@ -6,7 +6,7 @@ use schemars::JsonSchema;
use crate::{ use crate::{
frontend::{AnchoredKey, Error, Result, Statement, StatementArg}, frontend::{AnchoredKey, Error, Result, Statement, StatementArg},
middleware::{ 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, Predicate, StatementTmpl, StatementTmplArg, ToFields, Value, Wildcard,
}, },
}; };
@ -181,8 +181,8 @@ impl CustomPredicateBatchBuilder {
.map(|a| { .map(|a| {
Ok::<_, Error>(match a { Ok::<_, Error>(match a {
BuilderArg::Literal(v) => StatementTmplArg::Literal(v.clone()), BuilderArg::Literal(v) => StatementTmplArg::Literal(v.clone()),
BuilderArg::Key(pod_id_wc, key_str) => StatementTmplArg::AnchoredKey( BuilderArg::Key(root_wc, key_str) => StatementTmplArg::AnchoredKey(
resolve_wildcard(args, priv_args, pod_id_wc)?, resolve_wildcard(args, priv_args, root_wc)?,
Key::from(key_str), Key::from(key_str),
), ),
BuilderArg::WildcardLiteral(v) => { BuilderArg::WildcardLiteral(v) => {
@ -223,7 +223,7 @@ fn resolve_wildcard(args: &[&str], priv_args: &[&str], s: &str) -> Result<Wildca
.enumerate() .enumerate()
.find_map(|(i, name)| (s == *name).then_some(Wildcard::new(s.to_string(), i))) .find_map(|(i, name)| (s == *name).then_some(Wildcard::new(s.to_string(), i)))
.ok_or(Error::custom(format!( .ok_or(Error::custom(format!(
"Wildcard {} not amongst args {:?}", "Wildcard \"{}\" not amongst args {:?}",
s, s,
[args.to_vec(), priv_args.to_vec()].concat() [args.to_vec(), priv_args.to_vec()].concat()
))) )))
@ -274,15 +274,10 @@ mod tests {
let mut builder = CustomPredicateBatchBuilder::new(params.clone(), "gt_custom_pred".into()); let mut builder = CustomPredicateBatchBuilder::new(params.clone(), "gt_custom_pred".into());
let gt_stb = StatementTmplBuilder::new(NativePredicate::Gt) let gt_stb = StatementTmplBuilder::new(NativePredicate::Gt)
.arg(("s1_origin", "s1_key")) .arg("s1")
.arg(("s2_origin", "s2_key")); .arg("s2");
builder.predicate_and( builder.predicate_and("gt_custom_pred", &["s1", "s2"], &[], &[gt_stb])?;
"gt_custom_pred",
&["s1_origin", "s2_origin"],
&[],
&[gt_stb],
)?;
let batch = builder.finish(); let batch = builder.finish();
let batch_clone = batch.clone(); let batch_clone = batch.clone();
let gt_custom_pred = CustomPredicateRef::new(batch, 0); let gt_custom_pred = CustomPredicateRef::new(batch, 0);
@ -290,11 +285,8 @@ mod tests {
let mut mp_builder = MainPodBuilder::new(&params, vd_set); let mut mp_builder = MainPodBuilder::new(&params, vd_set);
// 2 > 1 // 2 > 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 // 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!( assert_eq!(
desugared_gt.predicate(), desugared_gt.predicate(),
Predicate::Native(NativePredicate::Lt) Predicate::Native(NativePredicate::Lt)
@ -324,12 +316,12 @@ mod tests {
CustomPredicateBatchBuilder::new(params.clone(), "set_contains_custom_pred".into()); CustomPredicateBatchBuilder::new(params.clone(), "set_contains_custom_pred".into());
let set_contains_stb = StatementTmplBuilder::new(NativePredicate::SetContains) let set_contains_stb = StatementTmplBuilder::new(NativePredicate::SetContains)
.arg(("s1_origin", "s1_key")) .arg("s1")
.arg(("s2_origin", "s2_key")); .arg("s2");
builder.predicate_and( builder.predicate_and(
"set_contains_custom_pred", "set_contains_custom_pred",
&["s1_origin", "s2_origin"], &["s1", "s2"],
&[], &[],
&[set_contains_stb], &[set_contains_stb],
)?; )?;
@ -339,11 +331,8 @@ mod tests {
let mut mp_builder = MainPodBuilder::new(&params, vd_set); let mut mp_builder = MainPodBuilder::new(&params, vd_set);
let set_values: HashSet<Value> = [1, 2, 3].iter().map(|i| Value::from(*i)).collect(); let set_values: HashSet<Value> = [1, 2, 3].iter().map(|i| Value::from(*i)).collect();
let s1 = mp_builder.priv_op(Operation::new_entry( let s1 = Set::new(params.max_depth_mt_containers, set_values)?;
"s1_key", let s2 = 1;
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 set_contains = mp_builder.pub_op(Operation::set_contains(s1, s2))?; let set_contains = mp_builder.pub_op(Operation::set_contains(s1, s2))?;
assert_eq!( assert_eq!(

View file

@ -4,13 +4,15 @@
use std::{collections::HashMap, convert::From, fmt}; use std::{collections::HashMap, convert::From, fmt};
use itertools::Itertools; use itertools::Itertools;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub use serialization::{SerializedMainPod, SerializedSignedPod}; pub use serialization::SerializedMainPod;
use crate::middleware::{ use crate::middleware::{
self, check_custom_pred, check_st_tmpl, hash_op, hash_str, max_op, prod_op, sum_op, self, check_custom_pred, check_st_tmpl, containers::Dictionary, hash_op, max_op, prod_op,
AnchoredKey, Key, MainPodInputs, NativeOperation, OperationAux, OperationType, Params, PodId, sum_op, AnchoredKey, Hash, Key, MainPodInputs, MainPodProver, NativeOperation, OperationAux,
PodProver, PodSigner, Statement, StatementArg, VDSet, Value, ValueRef, KEY_TYPE, SELF, OperationType, Params, PublicKey, RawValue, Signature, Signer, Statement, StatementArg, VDSet,
Value, ValueRef,
}; };
mod custom; mod custom;
@ -24,14 +26,14 @@ pub use operation::*;
pub use pod_request::*; pub use pod_request::*;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SignedPodBuilder { pub struct SignedDictBuilder {
pub params: Params, pub params: Params,
pub kvs: HashMap<Key, Value>, pub kvs: HashMap<Key, Value>,
} }
impl fmt::Display for SignedPodBuilder { impl fmt::Display for SignedDictBuilder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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()) { for (k, v) in self.kvs.iter().sorted_by_key(|kv| kv.0.hash()) {
writeln!(f, " - {}: {}", k, v)?; writeln!(f, " - {}: {}", k, v)?;
} }
@ -39,7 +41,7 @@ impl fmt::Display for SignedPodBuilder {
} }
} }
impl SignedPodBuilder { impl SignedDictBuilder {
pub fn new(params: &Params) -> Self { pub fn new(params: &Params) -> Self {
Self { Self {
params: params.clone(), params: params.clone(),
@ -51,65 +53,66 @@ impl SignedPodBuilder {
self.kvs.insert(key.into(), value.into()); self.kvs.insert(key.into(), value.into());
} }
pub fn sign<S: PodSigner>(&self, signer: &S) -> Result<SignedPod> { pub fn sign<S: Signer>(&self, signer: &S) -> Result<SignedDict> {
// Sign POD with committed KV store. // Sign committed KV store.
let pod = signer.sign(&self.params, &self.kvs)?; 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 #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
/// string<-->hash relation of the keys. // #[serde(try_from = "SerializedSignedDict", into = "SerializedSignedDict")]
#[derive(Debug, Clone, Serialize, Deserialize)] pub struct SignedDict {
#[serde(try_from = "SerializedSignedPod", into = "SerializedSignedPod")] pub dict: Dictionary,
pub struct SignedPod { pub public_key: PublicKey,
pub pod: Box<dyn middleware::Pod>, pub signature: Signature,
// We store a copy of the key values for quick access
kvs: HashMap<Key, Value>,
} }
impl fmt::Display for SignedPod { impl fmt::Display for SignedDict {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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 // 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 // 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 // deterministic based on the keys values not on the order of the keys when added into the
// tree. // 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)?; writeln!(f, " - {} = {}", k, v)?;
} }
Ok(()) Ok(())
} }
} }
impl SignedPod { impl SignedDict {
pub fn new(pod: Box<dyn middleware::Pod>) -> 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()
}
pub fn verify(&self) -> Result<()> { 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<Key, Value> { pub fn kvs(&self) -> &HashMap<Key, Value> {
&self.kvs self.dict.kvs()
} }
pub fn get(&self, key: impl Into<Key>) -> Option<&Value> { pub fn get(&self, key: impl Into<Key>) -> 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<Key>) -> Option<Statement> { pub fn get_statement(&self, key: impl Into<Key>) -> Option<Statement> {
let key: Key = key.into(); let key: Key = key.into();
self.kvs() self.kvs().get(&key).map(|value| {
.get(&key) Statement::Contains(
.map(|value| Statement::equal(AnchoredKey::from((self.id(), key)), value.clone())) 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 struct MainPodBuilder {
pub params: Params, pub params: Params,
pub vd_set: VDSet, pub vd_set: VDSet,
pub input_signed_pods: Vec<SignedPod>, pub input_pods: Vec<MainPod>,
pub input_recursive_pods: Vec<MainPod>,
pub statements: Vec<Statement>, pub statements: Vec<Statement>,
pub operations: Vec<Operation>, pub operations: Vec<Operation>,
pub public_statements: Vec<Statement>, pub public_statements: Vec<Statement>,
// Internal state // Internal state
/// Counter for constants created from literals // TODO: track contains ops with literals added explicitly as well.
const_cnt: usize, dict_contains: Vec<(Value, Value)>, // (root, key)
/// Map from (public, Value) to Key of already created literals via Equal statements.
literals: HashMap<(bool, Value), Key>,
} }
impl fmt::Display for MainPodBuilder { impl fmt::Display for MainPodBuilder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "MainPod:")?; 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:")?; 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, " - {}", in_pod.id())?;
} }
writeln!(f, " statements:")?; writeln!(f, " statements:")?;
@ -157,20 +153,15 @@ impl MainPodBuilder {
Self { Self {
params: params.clone(), params: params.clone(),
vd_set: vd_set.clone(), vd_set: vd_set.clone(),
input_signed_pods: Vec::new(), input_pods: Vec::new(),
input_recursive_pods: Vec::new(),
statements: Vec::new(), statements: Vec::new(),
operations: Vec::new(), operations: Vec::new(),
public_statements: Vec::new(), public_statements: Vec::new(),
const_cnt: 0, dict_contains: Vec::new(),
literals: HashMap::new(),
} }
} }
pub fn add_signed_pod(&mut self, pod: &SignedPod) { pub fn add_pod(&mut self, pod: MainPod) {
self.input_signed_pods.push(pod.clone()); self.input_pods.push(pod);
}
pub fn add_recursive_pod(&mut self, pod: MainPod) {
self.input_recursive_pods.push(pod);
} }
pub fn insert(&mut self, public: bool, st_op: (Statement, Operation)) -> Result<()> { pub fn insert(&mut self, public: bool, st_op: (Statement, Operation)) -> Result<()> {
// TODO: Do error handling instead of panic // TODO: Do error handling instead of panic
@ -384,12 +375,9 @@ impl MainPodBuilder {
let st = match op.0 { let st = match op.0 {
OperationType::Native(o) => { OperationType::Native(o) => {
let native_arg_error = move || Error::op_invalid_args(format!("{o:?}")); let native_arg_error = move || Error::op_invalid_args(format!("{o:?}"));
match (o, &op.1.as_slice()) { match (o, &op.1.as_slice(), &op.2) {
(None, &[]) => Statement::None, (None, &[], _) => Statement::None,
(NewEntry, &[OperationArg::Entry(k, v)]) => { (EqualFromEntries, &[a1, a2], _) => {
Statement::equal(AnchoredKey::from((SELF, k.as_str())), v.clone())
}
(EqualFromEntries, &[a1, a2]) => {
let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?; 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 (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?;
if v1 == v2 { if v1 == v2 {
@ -398,7 +386,7 @@ impl MainPodBuilder {
return Err(native_arg_error()); 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 (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 (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?;
if v1 != v2 { if v1 != v2 {
@ -407,7 +395,7 @@ impl MainPodBuilder {
return Err(native_arg_error()); 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 (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)?; let (r2, v2) = a2.int_value_and_ref().ok_or_else(native_arg_error)?;
if v1 < v2 { if v1 < v2 {
@ -416,7 +404,7 @@ impl MainPodBuilder {
return Err(native_arg_error()); 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 (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)?; let (r2, v2) = a2.int_value_and_ref().ok_or_else(native_arg_error)?;
if v1 <= v2 { if v1 <= v2 {
@ -425,10 +413,11 @@ impl MainPodBuilder {
return Err(native_arg_error()); return Err(native_arg_error());
} }
} }
(CopyStatement, &[OperationArg::Statement(s)]) => s.clone(), (CopyStatement, &[OperationArg::Statement(s)], _) => s.clone(),
( (
TransitiveEqualFromStatements, TransitiveEqualFromStatements,
&[OperationArg::Statement(Statement::Equal(r1, r2)), OperationArg::Statement(Statement::Equal(r3, r4))], &[OperationArg::Statement(Statement::Equal(r1, r2)), OperationArg::Statement(Statement::Equal(r3, r4))],
_,
) => { ) => {
if r2 == r3 { if r2 == r3 {
Statement::Equal(r1.clone(), r4.clone()) Statement::Equal(r1.clone(), r4.clone())
@ -436,10 +425,10 @@ impl MainPodBuilder {
return Err(native_arg_error()); 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()) 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 (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 (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)?; let (r3, v3) = a3.value_and_ref().ok_or_else(native_arg_error)?;
@ -449,7 +438,7 @@ impl MainPodBuilder {
return Err(native_arg_error()); 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 (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 (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)?; let (r3, v3) = a3.value_and_ref().ok_or_else(native_arg_error)?;
@ -459,7 +448,7 @@ impl MainPodBuilder {
return Err(native_arg_error()); 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 (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 (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)?; let (r3, v3) = a3.value_and_ref().ok_or_else(native_arg_error)?;
@ -469,7 +458,7 @@ impl MainPodBuilder {
return Err(native_arg_error()); 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 (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 (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)?; let (r3, v3) = a3.value_and_ref().ok_or_else(native_arg_error)?;
@ -479,20 +468,20 @@ impl MainPodBuilder {
return Err(native_arg_error()); 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 (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 (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)?; let (r3, _v3) = a3.value_and_ref().ok_or_else(native_arg_error)?;
// TODO: validate proof // TODO: validate proof
Statement::Contains(r1, r2, r3) 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 (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 (r2, _v2) = a2.value_and_ref().ok_or_else(native_arg_error)?;
// TODO: validate proof // TODO: validate proof
Statement::NotContains(r1, r2) Statement::NotContains(r1, r2)
} }
(PublicKeyOf, &[a1, a2]) => { (PublicKeyOf, &[a1, a2], _) => {
let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?; 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 (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?;
if middleware::Operation::check_public_key(v1, v2)? { if middleware::Operation::check_public_key(v1, v2)? {
@ -501,7 +490,16 @@ impl MainPodBuilder {
return Err(native_arg_error()); 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 (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 (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)?; let (r3, _v3) = a3.value_and_ref().ok_or_else(native_arg_error)?;
@ -509,7 +507,7 @@ impl MainPodBuilder {
// TODO: validate proof // TODO: validate proof
Statement::ContainerInsert(r1, r2, r3, r4) 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 (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 (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)?; let (r3, _v3) = a3.value_and_ref().ok_or_else(native_arg_error)?;
@ -517,14 +515,14 @@ impl MainPodBuilder {
// TODO: validate proof // TODO: validate proof
Statement::ContainerUpdate(r1, r2, r3, r4) 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 (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 (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)?; let (r3, _v3) = a3.value_and_ref().ok_or_else(native_arg_error)?;
// TODO: validate proof // TODO: validate proof
Statement::ContainerDelete(r1, r2, r3) Statement::ContainerDelete(r1, r2, r3)
} }
(t, _) => { (t, _, _) => {
if t.is_syntactic_sugar() { if t.is_syntactic_sugar() {
return Err(Error::custom(format!( return Err(Error::custom(format!(
"Unexpected syntactic sugar: {:?}", "Unexpected syntactic sugar: {:?}",
@ -583,7 +581,28 @@ impl MainPodBuilder {
Ok(st) 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<Statement> { fn op(&mut self, public: bool, op: Operation) -> Result<Statement> {
self.add_entries_contains(&op)?;
let op = Self::fill_in_aux(Self::lower_op(op)?)?; let op = Self::fill_in_aux(Self::lower_op(op)?)?;
let st = self.op_statement(op.clone())?; let st = self.op_statement(op.clone())?;
self.insert(public, (st, op))?; self.insert(public, (st, op))?;
@ -591,48 +610,13 @@ impl MainPodBuilder {
Ok(self.statements[self.statements.len() - 1].clone()) Ok(self.statements[self.statements.len() - 1].clone())
} }
/// Convenience method for introducing public constants.
pub fn pub_literal(&mut self, v: impl Into<Value>) -> Result<Statement> {
self.literal(true, v.into())
}
/// Convenience method for introducing private constants.
pub fn priv_literal(&mut self, v: impl Into<Value>) -> Result<Statement> {
self.literal(false, v.into())
}
fn literal(&mut self, public: bool, value: Value) -> Result<Statement> {
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) { pub fn reveal(&mut self, st: &Statement) {
self.public_statements.push(st.clone()); self.public_statements.push(st.clone());
} }
pub fn prove(&self, prover: &dyn PodProver) -> Result<MainPod> { pub fn prove(&self, prover: &dyn MainPodProver) -> Result<MainPod> {
let compiler = MainPodCompiler::new(&self.params); let compiler = MainPodCompiler::new(&self.params);
let inputs = MainPodCompilerInputs { let inputs = MainPodCompilerInputs {
// signed_pods: &self.input_signed_pods,
// main_pods: &self.input_main_pods,
statements: &self.statements, statements: &self.statements,
operations: &self.operations, operations: &self.operations,
public_statements: &self.public_statements, public_statements: &self.public_statements,
@ -641,67 +625,18 @@ impl MainPodBuilder {
let (statements, operations, public_statements) = compiler.compile(inputs, &self.params)?; let (statements, operations, public_statements) = compiler.compile(inputs, &self.params)?;
let inputs = MainPodInputs { let inputs = MainPodInputs {
signed_pods: &self pods: &self.input_pods.iter().map(|p| p.pod.as_ref()).collect_vec(),
.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(),
statements: &statements, statements: &statements,
operations: &operations, operations: &operations,
public_statements: &public_statements, public_statements: &public_statements,
vd_set: self.vd_set.clone(), vd_set: self.vd_set.clone(),
}; };
let pod = prover.prove(&self.params, &self.vd_set, inputs)?; let pod = prover.prove(&self.params, 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();
Ok(MainPod { Ok(MainPod {
pod, pod,
params: self.params.clone(), params: self.params.clone(),
public_statements, public_statements: self.public_statements.clone(),
}) })
} }
} }
@ -709,30 +644,26 @@ impl MainPodBuilder {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(try_from = "SerializedMainPod", into = "SerializedMainPod")] #[serde(try_from = "SerializedMainPod", into = "SerializedMainPod")]
pub struct MainPod { pub struct MainPod {
pub pod: Box<dyn middleware::RecursivePod>, pub pod: Box<dyn middleware::Pod>,
pub public_statements: Vec<Statement>, pub public_statements: Vec<Statement>,
pub params: Params, pub params: Params,
} }
impl fmt::Display for MainPod { impl fmt::Display for MainPod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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, " valid? {}", self.pod.verify().is_ok())?;
writeln!(f, " statements:")?; writeln!(f, " statements:")?;
for st in &self.pod.pub_statements() { for st in &self.pod.pub_statements() {
writeln!(f, " - {}", st)?; writeln!(f, " - {}", st)?;
} }
writeln!(f, " kvs:")?;
for (k, v) in &self.pod.kvs() {
writeln!(f, " - {}: {}", k, v)?;
}
Ok(()) Ok(())
} }
} }
impl MainPod { impl MainPod {
pub fn id(&self) -> PodId { pub fn id(&self) -> Hash {
self.pod.id() self.pod.statements_hash()
} }
/// Returns the value of a Equal statement with self id that defines key if it exists. /// Returns the value of a Equal statement with self id that defines key if it exists.
@ -742,7 +673,7 @@ impl MainPod {
.iter() .iter()
.find_map(|st| match st { .find_map(|st| match st {
Statement::Equal(ValueRef::Key(ak), ValueRef::Literal(value)) 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) Some(value)
} }
@ -753,8 +684,6 @@ impl MainPod {
} }
struct MainPodCompilerInputs<'a> { struct MainPodCompilerInputs<'a> {
// pub signed_pods: &'a [Box<dyn middleware::SignedPod>],
// pub main_pods: &'a [Box<dyn middleware::MainPod>],
pub statements: &'a [Statement], pub statements: &'a [Statement],
pub operations: &'a [Operation], pub operations: &'a [Operation],
pub public_statements: &'a [Statement], pub public_statements: &'a [Statement],
@ -832,8 +761,6 @@ impl MainPodCompiler {
Vec<Statement>, // public statements Vec<Statement>, // public statements
)> { )> {
let MainPodCompilerInputs { let MainPodCompilerInputs {
// signed_pods: _,
// main_pods: _,
statements, statements,
operations, operations,
public_statements, public_statements,
@ -853,16 +780,17 @@ pub mod tests {
use super::*; use super::*;
use crate::{ use crate::{
backends::plonky2::{ backends::plonky2::{
mock::mainpod::MockProver, primitives::ec::schnorr::SecretKey, signedpod::Signer, mock::mainpod::MockProver, primitives::ec::schnorr::SecretKey, signer::Signer,
}, },
dict,
examples::{ examples::{
attest_eth_friend, custom::eth_dos_request, great_boy_pod_full_flow, attest_eth_friend, custom::eth_dos_request, great_boy_pod_full_flow,
tickets_pod_full_flow, zu_kyc_pod_builder, zu_kyc_pod_request, 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::{ middleware::{
containers::{Array, Dictionary, Set}, containers::{Array, Set},
Value, Signer as _, Value,
}, },
}; };
@ -874,44 +802,21 @@ pub mod tests {
Ok(()) 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::<HashMap<_, _>>();
let embedded_kvs = pod
.pod
.kvs()
.into_iter()
.map(|(middleware::AnchoredKey { key, .. }, v)| (key, v))
.collect::<HashMap<_, _>>();
if kvs == embedded_kvs {
Ok(())
} else {
Err(Error::custom(format!(
"KVs {:?} do not agree with those embedded in the POD: {:?}",
kvs, embedded_kvs
)))
}
}
#[test] #[test]
fn test_front_zu_kyc() -> Result<()> { fn test_front_zu_kyc() -> Result<()> {
let params = Params::default(); let params = Params::default();
let vd_set = &*MOCK_VD_SET; let vd_set = &*MOCK_VD_SET;
let (gov_id, pay_stub) = zu_kyc_sign_pod_builders(&params); let (gov_id, pay_stub) = zu_kyc_sign_dict_builders(&params);
println!("{}", gov_id); println!("{}", gov_id);
println!("{}", pay_stub); println!("{}", pay_stub);
let signer = Signer(SecretKey(1u32.into())); let gov_id_signer = Signer(SecretKey(1u32.into()));
let gov_id = gov_id.sign(&signer)?; let gov_id = gov_id.sign(&gov_id_signer)?;
check_kvs(&gov_id)?;
println!("{}", gov_id); println!("{}", gov_id);
let signer = Signer(SecretKey(2u32.into())); let pay_stub_signer = Signer(SecretKey(2u32.into()));
let pay_stub = pay_stub.sign(&signer)?; let pay_stub = pay_stub.sign(&pay_stub_signer)?;
check_kvs(&pay_stub)?;
println!("{}", pay_stub); println!("{}", pay_stub);
let kyc_builder = zu_kyc_pod_builder(&params, vd_set, &gov_id, &pay_stub)?; let kyc_builder = zu_kyc_pod_builder(&params, vd_set, &gov_id, &pay_stub)?;
@ -923,14 +828,22 @@ pub mod tests {
println!("{}", kyc); println!("{}", kyc);
kyc.pod.verify()?;
let request = zu_kyc_pod_request( let request = zu_kyc_pod_request(
gov_id.get("_signer").unwrap(), &Value::from(gov_id_signer.public_key()),
pay_stub.get("_signer").unwrap(), &Value::from(pay_stub_signer.public_key()),
)?; )?;
// Check the bindings of the "gov" and "pay" wildcards from the PodRequest // Check the bindings of the "gov" and "pay" wildcards from the PodRequest
let bindings = request.exact_match_pod(&*kyc.pod).unwrap(); let bindings = request.exact_match_pod(&*kyc.pod).unwrap();
assert_eq!(*bindings.get("gov").unwrap(), gov_id.id().into()); assert_eq!(
assert_eq!(*bindings.get("pay").unwrap(), pay_stub.id().into()); *bindings.get("gov").unwrap(),
gov_id.dict.commitment().into()
);
assert_eq!(
*bindings.get("pay").unwrap(),
pay_stub.dict.commitment().into()
);
check_public_statements(&kyc) check_public_statements(&kyc)
} }
@ -950,7 +863,7 @@ pub mod tests {
let charlie = Signer(SecretKey(3u32.into())); let charlie = Signer(SecretKey(3u32.into()));
let david = Signer(SecretKey(4u32.into())); let david = Signer(SecretKey(4u32.into()));
let helper = EthDosHelper::new(&params, vd_set, true, alice.public_key())?; let helper = EthDosHelper::new(&params, vd_set, alice.public_key())?;
let prover = MockProver {}; let prover = MockProver {};
@ -960,8 +873,8 @@ pub mod tests {
let request = eth_dos_request()?; let request = eth_dos_request()?;
assert!(request.exact_match_pod(&*dist_1.pod).is_ok()); assert!(request.exact_match_pod(&*dist_1.pod).is_ok());
let bindings = request.exact_match_pod(&*dist_1.pod).unwrap(); let bindings = request.exact_match_pod(&*dist_1.pod).unwrap();
assert_eq!(*bindings.get("src").unwrap(), alice.public_key()); assert_eq!(*bindings.get("src").unwrap(), alice.public_key().into());
assert_eq!(*bindings.get("dst").unwrap(), bob.public_key()); assert_eq!(*bindings.get("dst").unwrap(), bob.public_key().into());
assert_eq!(*bindings.get("distance").unwrap(), 1.into()); assert_eq!(*bindings.get("distance").unwrap(), 1.into());
let bob_attestation = attest_eth_friend(&params, &bob, charlie.public_key()); let bob_attestation = attest_eth_friend(&params, &bob, charlie.public_key());
@ -971,8 +884,8 @@ pub mod tests {
dist_2.pod.verify()?; dist_2.pod.verify()?;
assert!(request.exact_match_pod(&*dist_2.pod).is_ok()); assert!(request.exact_match_pod(&*dist_2.pod).is_ok());
let bindings = request.exact_match_pod(&*dist_2.pod).unwrap(); let bindings = request.exact_match_pod(&*dist_2.pod).unwrap();
assert_eq!(*bindings.get("src").unwrap(), alice.public_key()); assert_eq!(*bindings.get("src").unwrap(), alice.public_key().into());
assert_eq!(*bindings.get("dst").unwrap(), charlie.public_key()); assert_eq!(*bindings.get("dst").unwrap(), charlie.public_key().into());
assert_eq!(*bindings.get("distance").unwrap(), 2.into()); assert_eq!(*bindings.get("distance").unwrap(), 2.into());
let charlie_attestation = attest_eth_friend(&params, &charlie, david.public_key()); let charlie_attestation = attest_eth_friend(&params, &charlie, david.public_key());
@ -982,8 +895,8 @@ pub mod tests {
dist_3.pod.verify()?; dist_3.pod.verify()?;
assert!(request.exact_match_pod(&*dist_3.pod).is_ok()); assert!(request.exact_match_pod(&*dist_3.pod).is_ok());
let bindings = request.exact_match_pod(&*dist_3.pod).unwrap(); let bindings = request.exact_match_pod(&*dist_3.pod).unwrap();
assert_eq!(*bindings.get("src").unwrap(), alice.public_key()); assert_eq!(*bindings.get("src").unwrap(), alice.public_key().into());
assert_eq!(*bindings.get("dst").unwrap(), david.public_key()); assert_eq!(*bindings.get("dst").unwrap(), david.public_key().into());
assert_eq!(*bindings.get("distance").unwrap(), 3.into()); assert_eq!(*bindings.get("distance").unwrap(), 3.into());
Ok(()) Ok(())
@ -1014,25 +927,22 @@ pub mod tests {
let params = Params::default(); let params = Params::default();
let vd_set = &*MOCK_VD_SET; let vd_set = &*MOCK_VD_SET;
let mut signed_builder = SignedPodBuilder::new(&params); let mut signed_builder = SignedDictBuilder::new(&params);
signed_builder.insert("a", 1); signed_builder.insert("a", 1);
signed_builder.insert("b", 1); signed_builder.insert("b", 1);
let signer = Signer(SecretKey(1u32.into())); 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(&params, vd_set); let mut builder = MainPodBuilder::new(&params, vd_set);
builder.add_signed_pod(&signed_pod); builder
.pub_op(Operation::dict_signed_by(&signed_dict))
//let op_val1 = Operation{ .unwrap();
// OperationType::Native(NativeOperation::CopyStatement),
// signed_pod.
//}
let op_eq1 = Operation( let op_eq1 = Operation(
OperationType::Native(NativeOperation::EqualFromEntries), OperationType::Native(NativeOperation::EqualFromEntries),
vec![ vec![
OperationArg::from((&signed_pod, "a")), OperationArg::from((&signed_dict, "a")),
OperationArg::from((&signed_pod, "b")), OperationArg::from((&signed_dict, "b")),
], ],
OperationAux::None, OperationAux::None,
); );
@ -1040,8 +950,8 @@ pub mod tests {
let op_eq2 = Operation( let op_eq2 = Operation(
OperationType::Native(NativeOperation::EqualFromEntries), OperationType::Native(NativeOperation::EqualFromEntries),
vec![ vec![
OperationArg::from((&signed_pod, "b")), OperationArg::from((&signed_dict, "b")),
OperationArg::from((&signed_pod, "a")), OperationArg::from((&signed_dict, "a")),
], ],
OperationAux::None, OperationAux::None,
); );
@ -1065,17 +975,21 @@ pub mod tests {
fn test_false_st() { fn test_false_st() {
let params = Params::default(); let params = Params::default();
let vd_set = &*MOCK_VD_SET; let vd_set = &*MOCK_VD_SET;
let mut builder = SignedPodBuilder::new(&params); let mut builder = SignedDictBuilder::new(&params);
builder.insert("num", 2); builder.insert("num", 2);
let signer = Signer(SecretKey(1u32.into())); 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(&params, vd_set); let mut builder = MainPodBuilder::new(&params, vd_set);
builder.add_signed_pod(&pod); builder
builder.pub_op(Operation::gt((&pod, "num"), 5)).unwrap(); .pub_op(Operation::dict_signed_by(&signed_dict))
.unwrap();
builder
.pub_op(Operation::gt((&signed_dict, "num"), 5))
.unwrap();
let prover = MockProver {}; let prover = MockProver {};
let false_pod = builder.prove(&prover).unwrap(); let false_pod = builder.prove(&prover).unwrap();
@ -1088,26 +1002,28 @@ pub mod tests {
fn test_dictionaries() -> Result<()> { fn test_dictionaries() -> Result<()> {
let params = Params::default(); let params = Params::default();
let vd_set = &*MOCK_VD_SET; let vd_set = &*MOCK_VD_SET;
let mut builder = SignedPodBuilder::new(&params); let mut builder = SignedDictBuilder::new(&params);
let mut my_dict_kvs: HashMap<Key, Value> = HashMap::new(); let dict = dict!(params.max_depth_mt_containers, {
my_dict_kvs.insert(Key::from("a"), Value::from(1)); "a" => 1,
my_dict_kvs.insert(Key::from("b"), Value::from(2)); "b" => 2,
my_dict_kvs.insert(Key::from("c"), Value::from(3)); "c" => 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_root = Value::from(dict.clone()); let dict_root = Value::from(dict.clone());
builder.insert("dict", dict_root); builder.insert("dict", dict_root);
let signer = Signer(SecretKey(1u32.into())); let signer = Signer(SecretKey(1u32.into()));
let pod = builder.sign(&signer).unwrap(); let signed_dict = builder.sign(&signer).unwrap();
let mut builder = MainPodBuilder::new(&params, vd_set); let mut builder = MainPodBuilder::new(&params, vd_set);
builder.add_signed_pod(&pod); builder
let st0 = pod.get_statement("dict").unwrap(); .pub_op(Operation::dict_signed_by(&signed_dict))
let st1 = builder.op(true, Operation::new_entry("key", "a")).unwrap(); .unwrap();
let st2 = builder.literal(false, Value::from(1)).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( builder.pub_op(Operation(
// OperationType // OperationType
@ -1116,7 +1032,7 @@ pub mod tests {
vec![ vec![
OperationArg::Statement(st0.clone()), OperationArg::Statement(st0.clone()),
OperationArg::Statement(st1), OperationArg::Statement(st1),
OperationArg::Statement(st2), OperationArg::Literal(Value::from(1)),
], ],
OperationAux::MerkleProof(dict.prove(&Key::from("a")).unwrap().1), OperationAux::MerkleProof(dict.prove(&Key::from("a")).unwrap().1),
))?; ))?;
@ -1259,25 +1175,35 @@ pub mod tests {
let sk = SecretKey::new_rand(); let sk = SecretKey::new_rand();
let pk = sk.public_key(); let pk = sk.public_key();
// Signed POD contains public key as owner // Signed Dict contains public key as owner
let mut builder = SignedPodBuilder::new(&params); let mut builder = SignedDictBuilder::new(&params);
builder.insert("owner", Value::from(pk)); builder.insert("owner", Value::from(pk));
builder.insert("other_data", Value::from(123)); builder.insert("other_data", Value::from(123));
let signer = Signer(SecretKey(1u32.into())); 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. // Main POD proves ownership of the owner's secret key.
let mut builder = MainPodBuilder::new(&params, vd_set); let mut builder = MainPodBuilder::new(&params, vd_set);
builder.add_signed_pod(&signed_pod); builder.pub_op(Operation::signed_by(
let st0 = signed_pod.get_statement("owner").unwrap(); Value::from(signed_dict.dict.clone()),
let st1 = builder Value::from(signed_dict.public_key),
.priv_op(Operation::new_entry("known_secret", Value::from(sk))) signed_dict.signature.clone(),
.unwrap(); ))?;
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 builder
.pub_op(Operation( .pub_op(Operation(
// OperationType
OperationType::Native(NativeOperation::PublicKeyOf), OperationType::Native(NativeOperation::PublicKeyOf),
// Vec<OperationArg>
vec![OperationArg::Statement(st0), OperationArg::Statement(st1)], vec![OperationArg::Statement(st0), OperationArg::Statement(st1)],
OperationAux::None, OperationAux::None,
)) ))
@ -1301,22 +1227,25 @@ pub mod tests {
let pk = sk.public_key(); let pk = sk.public_key();
// Signed POD contains public key as owner // Signed POD contains public key as owner
let mut builder = SignedPodBuilder::new(&params); let mut builder = SignedDictBuilder::new(&params);
builder.insert("owner", Value::from(pk)); builder.insert("owner", Value::from(pk));
builder.insert("other_data", Value::from(123)); builder.insert("other_data", Value::from(123));
let signer = Signer(SecretKey(1u32.into())); 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 // Try to build with the wrong secret key. The pre-proving checks
// will catch this. // will catch this.
let mut builder = MainPodBuilder::new(&params, vd_set); let mut builder = MainPodBuilder::new(&params, vd_set);
builder.add_signed_pod(&signed_pod); builder
let st0 = signed_pod.get_statement("owner").unwrap(); .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 let st1 = builder
.priv_op(Operation::new_entry( .op(
"known_secret", true,
Value::from(SecretKey(BigUint::from(123u32))), Operation::dict_contains(local, "known_secret", SecretKey(BigUint::from(123u32))),
)) )
.unwrap(); .unwrap();
assert!(builder assert!(builder
.pub_op(Operation( .pub_op(Operation(
@ -1341,33 +1270,30 @@ pub mod tests {
// Try to build with wrong type in 1st arg // Try to build with wrong type in 1st arg
let mut builder = MainPodBuilder::new(&params, vd_set); let mut builder = MainPodBuilder::new(&params, vd_set);
let st_pk = builder.literal(false, Value::from(pk)).unwrap(); let int2 = Value::from(123);
let st_int1 = builder.literal(false, Value::from(123)).unwrap(); let sk = Value::from(sk);
assert!(builder assert!(builder
.pub_op(Operation( .pub_op(Operation(
// OperationType // OperationType
OperationType::Native(NativeOperation::PublicKeyOf), OperationType::Native(NativeOperation::PublicKeyOf),
// Vec<OperationArg> // Vec<OperationArg>
vec![ vec![OperationArg::Literal(int2), OperationArg::Literal(sk),],
OperationArg::Statement(st_pk),
OperationArg::Statement(st_int1),
],
OperationAux::None, OperationAux::None,
)) ))
.is_err()); .is_err());
// Try to build with wrong type in 2nd arg // Try to build with wrong type in 2nd arg
builder = MainPodBuilder::new(&params, vd_set); let mut builder = MainPodBuilder::new(&params, vd_set);
let st_sk = builder.literal(false, Value::from(pk)).unwrap(); let pk = Value::from(pk);
let st_int2 = builder.literal(false, Value::from(123)).unwrap(); let int1 = Value::from(123);
assert!(builder assert!(builder
.pub_op(Operation( .pub_op(Operation(
// OperationType // OperationType
OperationType::Native(NativeOperation::PublicKeyOf), OperationType::Native(NativeOperation::PublicKeyOf),
// Vec<OperationArg> // Vec<OperationArg>
vec![ vec![
OperationArg::Statement(st_int2), OperationArg::Literal(pk.clone()),
OperationArg::Statement(st_sk), OperationArg::Literal(int1),
], ],
OperationAux::None, OperationAux::None,
)) ))
@ -1376,33 +1302,6 @@ pub mod tests {
Ok(()) 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(&params, 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] #[should_panic]
#[test] #[test]
fn test_reject_unsound_statement() { fn test_reject_unsound_statement() {
@ -1411,23 +1310,25 @@ pub mod tests {
let params = Params::default(); let params = Params::default();
let vd_set = &*MOCK_VD_SET; let vd_set = &*MOCK_VD_SET;
let mut builder = MainPodBuilder::new(&params, vd_set); let mut builder = MainPodBuilder::new(&params, vd_set);
let self_a = AnchoredKey::from((SELF, "a")); let local = dict!(32, {"a" => 3, "b" => 27}).unwrap();
let self_b = AnchoredKey::from((SELF, "b")); let value_of_a = Statement::contains(local.clone(), "a", 3);
let value_of_a = Statement::equal(self_a.clone(), Value::from(3)); let value_of_b = Statement::contains(local.clone(), "b", 27);
let value_of_b = Statement::equal(self_b.clone(), Value::from(27));
let op_new_entry = Operation( let op_contains = Operation(
OperationType::Native(NativeOperation::NewEntry), OperationType::Native(NativeOperation::DictContainsFromEntries),
vec![], vec![],
OperationAux::None, OperationAux::None,
); );
builder builder
.insert(false, (value_of_a.clone(), op_new_entry.clone())) .insert(false, (value_of_a.clone(), op_contains.clone()))
.unwrap(); .unwrap();
builder builder
.insert(false, (value_of_b.clone(), op_new_entry)) .insert(false, (value_of_b.clone(), op_contains))
.unwrap(); .unwrap();
let st = Statement::equal(self_a, self_b); let st = Statement::equal(
AnchoredKey::from((&local, "a")),
AnchoredKey::from((&local, "b")),
);
let op = Operation( let op = Operation(
OperationType::Native(NativeOperation::EqualFromEntries), OperationType::Native(NativeOperation::EqualFromEntries),
vec![ vec![

View file

@ -1,10 +1,10 @@
use std::fmt; use std::fmt;
use crate::{ use crate::{
frontend::{MainPod, SignedPod}, frontend::SignedDict,
middleware::{ middleware::{
AnchoredKey, CustomPredicateRef, NativeOperation, OperationAux, OperationType, Statement, containers::Dictionary, root_key_to_ak, CustomPredicateRef, NativeOperation, OperationAux,
TypedValue, Value, ValueRef, OperationType, Signature, Statement, TypedValue, Value, ValueRef,
}, },
}; };
@ -16,12 +16,12 @@ pub enum OperationArg {
} }
impl OperationArg { impl OperationArg {
/// Extracts the value underlying literal and `ValueOf` statement /// Extracts the value underlying literal and `Contains` statement
/// operation args. /// operation args.
pub(crate) fn value(&self) -> Option<&Value> { pub(crate) fn value(&self) -> Option<&Value> {
match self { match self {
Self::Literal(v) => Some(v), Self::Literal(v) => Some(v),
Self::Statement(Statement::Equal(_, ValueRef::Literal(v))) => Some(v), Self::Statement(Statement::Contains(_, _, ValueRef::Literal(v))) => Some(v),
_ => None, _ => None,
} }
} }
@ -29,7 +29,11 @@ impl OperationArg {
pub(crate) fn value_and_ref(&self) -> Option<(ValueRef, &Value)> { pub(crate) fn value_and_ref(&self) -> Option<(ValueRef, &Value)> {
match self { match self {
Self::Literal(v) => Some((ValueRef::Literal(v.clone()), v)), 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, _ => None,
} }
} }
@ -64,27 +68,21 @@ impl From<&Value> for OperationArg {
} }
} }
impl From<(&SignedPod, &str)> for OperationArg { impl From<(&Dictionary, &str)> for OperationArg {
fn from((pod, key): (&SignedPod, &str)) -> Self { fn from((dict, key): (&Dictionary, &str)) -> Self {
// TODO: TryFrom. // TODO: Use TryFrom
let value = pod let value = dict.get(&key.into()).cloned().unwrap();
.kvs() Self::Statement(Statement::Contains(
.get(&key.into()) dict.clone().into(),
.cloned() key.into(),
.unwrap_or_else(|| panic!("Key {} is not present in POD: {}", key, pod));
Self::Statement(Statement::Equal(
AnchoredKey::from((pod.id(), key)).into(),
value.into(), value.into(),
)) ))
} }
} }
impl From<(&MainPod, &str)> for OperationArg {
fn from((pod, key): (&MainPod, &str)) -> Self { impl From<(&SignedDict, &str)> for OperationArg {
// TODO: TryFrom. fn from((signed_dict, key): (&SignedDict, &str)) -> Self {
let value = pod OperationArg::from((&signed_dict.dict, key))
.get(key)
.unwrap_or_else(|| panic!("Key {} is not present in POD: {}", key, pod));
Self::Statement(Statement::equal(AnchoredKey::from((pod.id(), key)), value))
} }
} }
@ -186,13 +184,6 @@ macro_rules! op_impl_st {
} }
impl Operation { impl Operation {
pub fn new_entry(a1: impl Into<String>, a2: impl Into<Value>) -> Self {
Self(
OperationType::Native(NativeOperation::NewEntry),
vec![OperationArg::Entry(a1.into(), a2.into())],
OperationAux::None,
)
}
op_impl_oa!(eq, EqualFromEntries, 2); op_impl_oa!(eq, EqualFromEntries, 2);
op_impl_oa!(ne, NotEqualFromEntries, 2); op_impl_oa!(ne, NotEqualFromEntries, 2);
op_impl_oa!(gt_eq, GtEqFromEntries, 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_insert, SetInsertFromEntries, 3);
op_impl_oa!(set_delete, SetDeleteFromEntries, 3); op_impl_oa!(set_delete, SetDeleteFromEntries, 3);
op_impl_oa!(array_update, ArrayUpdateFromEntries, 4); op_impl_oa!(array_update, ArrayUpdateFromEntries, 4);
pub fn signed_by(
msg: impl Into<OperationArg>,
pk: impl Into<OperationArg>,
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(),
)
}
} }

View file

@ -128,8 +128,8 @@ impl PodRequest {
} }
// Try to bind wildcard to the POD ID // Try to bind wildcard to the POD ID
let pod_id_value = Value::from(stmt_key.pod_id); let root_value = Value::from(stmt_key.root);
self.try_bind_wildcard(&wildcard.name, pod_id_value, current_bindings, new_bindings) self.try_bind_wildcard(&wildcard.name, root_value, current_bindings, new_bindings)
} }
// Other combinations don't match // Other combinations don't match
@ -176,12 +176,14 @@ impl Display for PodRequest {
mod tests { mod tests {
use crate::{ use crate::{
backends::plonky2::{ 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}, frontend::{MainPodBuilder, Operation},
lang::parse, lang::parse,
middleware::Params, middleware::{Params, Value},
}; };
#[test] #[test]
@ -189,7 +191,7 @@ mod tests {
let params = Params::default(); let params = Params::default();
let vd_set = &*MOCK_VD_SET; let vd_set = &*MOCK_VD_SET;
let (gov_id, pay_stub) = zu_kyc_sign_pod_builders(&params); let (gov_id, pay_stub) = zu_kyc_sign_dict_builders(&params);
let gov_id = gov_id.sign(&Signer(SecretKey(1u32.into()))).unwrap(); let gov_id = gov_id.sign(&Signer(SecretKey(1u32.into()))).unwrap();
let pay_stub = pay_stub.sign(&Signer(SecretKey(2u32.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(); 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 // This request matches the POD
let request = zu_kyc_pod_request( let request = zu_kyc_pod_request(
gov_id.get("_signer").unwrap(), &Value::from(gov_id.public_key),
pay_stub.get("_signer").unwrap(), &Value::from(pay_stub.public_key),
) )
.unwrap(); .unwrap();
assert!(request.exact_match_pod(&*kyc.pod).is_ok()); assert!(request.exact_match_pod(&*kyc.pod).is_ok());

View file

@ -1,73 +1,30 @@
use std::collections::HashMap;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::Error; use super::Error;
use crate::{ use crate::{
frontend::{MainPod, SignedPod}, frontend::MainPod,
middleware::{ middleware::{deserialize_pod, Hash, Params, Statement, VDSet},
deserialize_pod, deserialize_signed_pod, Key, Params, PodId, Statement, VDSet, Value,
},
}; };
#[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<Key, Value>,
data: serde_json::Value,
}
impl SerializedSignedPod {
pub fn id(&self) -> PodId {
self.id
}
}
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
#[schemars(rename = "MainPod")] #[schemars(rename = "MainPod")]
pub struct SerializedMainPod { pub struct SerializedMainPod {
params: Params, params: Params,
pod_type: (usize, String), pod_type: (usize, String),
id: PodId, id: Hash,
vd_set: VDSet, vd_set: VDSet,
public_statements: Vec<Statement>, public_statements: Vec<Statement>,
data: serde_json::Value, data: serde_json::Value,
} }
impl SerializedMainPod { impl SerializedMainPod {
pub fn id(&self) -> PodId { pub fn id(&self) -> Hash {
self.id self.id
} }
} }
impl From<SignedPod> 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<SerializedSignedPod> for SignedPod {
type Error = Error;
fn try_from(serialized: SerializedSignedPod) -> Result<Self, Self::Error> {
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<MainPod> for SerializedMainPod { impl From<MainPod> for SerializedMainPod {
fn from(pod: MainPod) -> Self { fn from(pod: MainPod) -> Self {
let (pod_type, pod_type_name_str) = pod.pod.pod_type(); let (pod_type, pod_type_name_str) = pod.pod.pod_type();
@ -116,17 +73,17 @@ mod tests {
mainpod::{rec_main_pod_circuit_data, Prover}, mainpod::{rec_main_pod_circuit_data, Prover},
mock::mainpod::MockProver, mock::mainpod::MockProver,
primitives::ec::schnorr::SecretKey, primitives::ec::schnorr::SecretKey,
signedpod::Signer, signer::Signer,
}, },
examples::{ 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, MOCK_VD_SET,
}, },
frontend::{Result, SignedPodBuilder}, frontend::{Result, SignedDict, SignedDictBuilder},
middleware::{ middleware::{
self, self,
containers::{Array, Dictionary, Set}, 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 params = &Params::default();
let mut builder = SignedPodBuilder::new(params); let mut builder = SignedDictBuilder::new(params);
builder.insert("name", "test"); builder.insert("name", "test");
builder.insert("age", 30); builder.insert("age", 30);
builder.insert("very_large_int", 1152921504606846976); builder.insert("very_large_int", 1152921504606846976);
@ -228,46 +185,29 @@ mod tests {
} }
#[test] #[test]
fn test_signed_pod_serialization() { fn test_signed_dict_serialization() {
let builder = signed_pod_builder(); let builder = signed_dict_builder();
let signer = Signer(SecretKey(1u32.into())); 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); println!("serialized: {}", serialized);
let deserialized: SignedPod = serde_json::from_str(&serialized).unwrap(); let deserialized: SignedDict = serde_json::from_str(&serialized).unwrap();
println!( println!(
"deserialized: {}", "deserialized: {}",
serde_json::to_string_pretty(&deserialized).unwrap() serde_json::to_string_pretty(&deserialized).unwrap()
); );
assert_eq!(pod.kvs, deserialized.kvs); assert_eq!(signed_dict.dict.kvs(), deserialized.dict.kvs());
assert_eq!(pod.verify().is_ok(), deserialized.verify().is_ok()); assert_eq!(signed_dict.public_key, deserialized.public_key);
assert_eq!(pod.id(), deserialized.id()) assert_eq!(signed_dict.signature, deserialized.signature);
} assert_eq!(signed_dict.verify().is_ok(), deserialized.verify().is_ok());
#[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())
} }
fn build_mock_zukyc_pod() -> Result<MainPod> { fn build_mock_zukyc_pod() -> Result<MainPod> {
let params = middleware::Params::default(); let params = middleware::Params::default();
let vd_set = &*MOCK_VD_SET; let vd_set = &*MOCK_VD_SET;
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(&params); let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_dict_builders(&params);
let signer = Signer(SecretKey(1u32.into())); let signer = Signer(SecretKey(1u32.into()));
let gov_id_pod = gov_id_builder.sign(&signer).unwrap(); let gov_id_pod = gov_id_builder.sign(&signer).unwrap();
let signer = Signer(SecretKey(2u32.into())); let signer = Signer(SecretKey(2u32.into()));
@ -283,18 +223,19 @@ mod tests {
let params = middleware::Params { let params = middleware::Params {
// Currently the circuit uses random access that only supports vectors of length 64. // 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. // 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() ..Default::default()
}; };
let mut vds = DEFAULT_VD_LIST.clone(); let mut vds = DEFAULT_VD_LIST.clone();
vds.push(rec_main_pod_circuit_data(&params).1.verifier_only.clone()); vds.push(rec_main_pod_circuit_data(&params).1.verifier_only.clone());
let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap(); let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap();
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(&params); let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_dict_builders(&params);
let signer = Signer(SecretKey(1u32.into())); let signer = Signer(SecretKey(1u32.into()));
let gov_id_pod = gov_id_builder.sign(&signer)?; let gov_id_pod = gov_id_builder.sign(&signer)?;
let signer = Signer(SecretKey(2u32.into())); let signer = Signer(SecretKey(2u32.into()));
let pay_stub_pod = pay_stub_builder.sign(&signer)?; let pay_stub_pod = pay_stub_builder.sign(&signer)?;
let _signer = Signer(SecretKey(3u32.into()));
let kyc_builder = zu_kyc_pod_builder(&params, &vd_set, &gov_id_pod, &pay_stub_pod)?; let kyc_builder = zu_kyc_pod_builder(&params, &vd_set, &gov_id_pod, &pay_stub_pod)?;
let prover = Prover {}; let prover = Prover {};
@ -311,7 +252,10 @@ mod tests {
let deserialized: MainPod = serde_json::from_str(&serialized).unwrap(); let deserialized: MainPod = serde_json::from_str(&serialized).unwrap();
assert_eq!(kyc_pod.public_statements, deserialized.public_statements); 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()?); assert_eq!(kyc_pod.pod.verify()?, deserialized.pod.verify()?);
Ok(()) Ok(())
@ -324,7 +268,10 @@ mod tests {
let deserialized: MainPod = serde_json::from_str(&serialized).unwrap(); let deserialized: MainPod = serde_json::from_str(&serialized).unwrap();
assert_eq!(kyc_pod.public_statements, deserialized.public_statements); 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()?); assert_eq!(kyc_pod.pod.verify()?, deserialized.pod.verify()?);
Ok(()) Ok(())
@ -348,7 +295,7 @@ mod tests {
let alice_attestation = attest_eth_friend(&params, &alice, bob.public_key()); let alice_attestation = attest_eth_friend(&params, &alice, bob.public_key());
let bob_attestation = attest_eth_friend(&params, &bob, charlie.public_key()); let bob_attestation = attest_eth_friend(&params, &bob, charlie.public_key());
let helper = EthDosHelper::new(&params, vd_set, true, alice.public_key())?; let helper = EthDosHelper::new(&params, vd_set, alice.public_key())?;
let prover = MockProver {}; let prover = MockProver {};
let dist_1 = helper.dist_1(&alice_attestation)?.prove(&prover)?; let dist_1 = helper.dist_1(&alice_attestation)?.prove(&prover)?;
let dist_2 = helper let dist_2 = helper
@ -360,28 +307,28 @@ mod tests {
#[test] #[test]
// This tests that we can generate JSON Schemas for the MainPod and // 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 // against the schemas. Since both Mock and Plonky2 PODs have the same
// public interface, we can assume that the schema works for both. // public interface, we can assume that the schema works for both.
fn test_schema() { fn test_schema() {
let mainpod_schema = schema_for!(SerializedMainPod); 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 kyc_pod = build_mock_zukyc_pod().unwrap();
let signed_pod = signed_pod_builder() let signed_dict = signed_dict_builder()
.sign(&Signer(SecretKey(1u32.into()))) .sign(&Signer(SecretKey(1u32.into())))
.unwrap(); .unwrap();
let ethdos_pod = build_ethdos_pod().unwrap(); let ethdos_pod = build_ethdos_pod().unwrap();
let mainpod_schema_value = serde_json::to_value(&mainpod_schema).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 kyc_pod_value = serde_json::to_value(&kyc_pod).unwrap();
let mainpod_valid = jsonschema::validate(&mainpod_schema_value, &kyc_pod_value); let mainpod_valid = jsonschema::validate(&mainpod_schema_value, &kyc_pod_value);
assert!(mainpod_valid.is_ok(), "{:#?}", mainpod_valid); assert!(mainpod_valid.is_ok(), "{:#?}", mainpod_valid);
let signed_pod_value = serde_json::to_value(&signed_pod).unwrap(); let signed_dict_value = serde_json::to_value(&signed_dict).unwrap();
let signedpod_valid = jsonschema::validate(&signedpod_schema_value, &signed_pod_value); let signed_dict_valid = jsonschema::validate(&signed_dict_schema_value, &signed_dict_value);
assert!(signedpod_valid.is_ok(), "{:#?}", signedpod_valid); assert!(signed_dict_valid.is_ok(), "{:#?}", signed_dict_valid);
let ethdos_pod_value = serde_json::to_value(&ethdos_pod).unwrap(); let ethdos_pod_value = serde_json::to_value(&ethdos_pod).unwrap();
let ethdos_pod_valid = jsonschema::validate(&mainpod_schema_value, &ethdos_pod_value); let ethdos_pod_valid = jsonschema::validate(&mainpod_schema_value, &ethdos_pod_value);

View file

@ -15,8 +15,6 @@ identifier = @{ !("private") ~ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")*
private_kw = { "private:" } private_kw = { "private:" }
self_keyword = @{ "SELF" }
// Define wildcard names (start with '?') // Define wildcard names (start with '?')
wildcard = @{ "?" ~ identifier } wildcard = @{ "?" ~ identifier }
@ -66,10 +64,8 @@ literal_value = {
literal_array | literal_array |
literal_bool | literal_bool |
literal_raw | literal_raw |
literal_pod_id |
literal_string | literal_string |
literal_int | literal_int
self_keyword
} }
// Primitive literal types // Primitive literal types
@ -81,7 +77,6 @@ literal_bool = @{ "true" | "false" }
hash_hex = @{ "0x" ~ (ASCII_HEX_DIGIT ~ ASCII_HEX_DIGIT){32} } hash_hex = @{ "0x" ~ (ASCII_HEX_DIGIT ~ ASCII_HEX_DIGIT){32} }
literal_raw = { "Raw" ~ "(" ~ hash_hex ~ ")" } literal_raw = { "Raw" ~ "(" ~ hash_hex ~ ")" }
literal_pod_id = { hash_hex }
// String literal parsing based on https://pest.rs/book/examples/json.html // String literal parsing based on https://pest.rs/book/examples/json.html
literal_string = ${ "\"" ~ inner ~ "\"" } // Compound atomic string rule literal_string = ${ "\"" ~ inner ~ "\"" } // Compound atomic string rule
@ -114,7 +109,6 @@ test_wildcard = { SOI ~ wildcard ~ EOI }
test_literal_int = { SOI ~ literal_int ~ EOI } test_literal_int = { SOI ~ literal_int ~ EOI }
test_hash_hex = { SOI ~ hash_hex ~ EOI } test_hash_hex = { SOI ~ hash_hex ~ EOI }
test_literal_raw = { SOI ~ literal_raw ~ 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_literal_value = { SOI ~ literal_value ~ EOI }
test_statement = { SOI ~ statement ~ EOI } test_statement = { SOI ~ statement ~ EOI }
test_custom_predicate_def = { SOI ~ custom_predicate_def ~ EOI } test_custom_predicate_def = { SOI ~ custom_predicate_def ~ EOI }

View file

@ -32,9 +32,8 @@ mod tests {
backends::plonky2::primitives::ec::schnorr::SecretKey, backends::plonky2::primitives::ec::schnorr::SecretKey,
lang::error::ProcessorError, lang::error::ProcessorError,
middleware::{ middleware::{
hash_str, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, Key, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, Key, NativePredicate,
NativePredicate, Params, PodId, PodType, Predicate, RawValue, StatementTmpl, Params, Predicate, RawValue, StatementTmpl, StatementTmplArg, Value, Wildcard,
StatementTmplArg, Value, Wildcard, KEY_SIGNER, KEY_TYPE, SELF,
}, },
}; };
@ -107,7 +106,7 @@ mod tests {
fn test_e2e_simple_request() -> Result<(), LangError> { fn test_e2e_simple_request() -> Result<(), LangError> {
let input = r#" let input = r#"
REQUEST( REQUEST(
Equal(?ConstPod["my_val"], 0x0000000000000000000000000000000000000000000000000000000000000001) Equal(?ConstPod["my_val"], Raw(0x0000000000000000000000000000000000000000000000000000000000000001))
Lt(?GovPod["dob"], ?ConstPod["my_val"]) Lt(?GovPod["dob"], ?ConstPod["my_val"])
) )
"#; "#;
@ -482,10 +481,8 @@ mod tests {
#[test] #[test]
fn test_e2e_ethdos_predicates() -> Result<(), LangError> { fn test_e2e_ethdos_predicates() -> Result<(), LangError> {
let params = Params { let params = Params {
max_input_signed_pods: 3, max_input_pods: 3,
max_input_recursive_pods: 3,
max_statements: 31, max_statements: 31,
max_signed_pod_values: 8,
max_public_statements: 10, max_public_statements: 10,
max_statement_args: 6, max_statement_args: 6,
max_operation_args: 5, max_operation_args: 5,
@ -496,10 +493,9 @@ mod tests {
}; };
let input = r#" let input = r#"
eth_friend(src, dst, private: attestation_pod) = AND( eth_friend(src, dst, private: attestation_dict) = AND(
Equal(?attestation_pod["_type"], 1) SignedBy(?attestation_dict, ?src)
Equal(?attestation_pod["_signer"], ?src) Equal(?attestation_dict["attestation"], ?dst)
Equal(?attestation_pod["attestation"], ?dst)
) )
eth_dos_distance_base(src, dst, distance) = AND( eth_dos_distance_base(src, dst, distance) = AND(
@ -536,23 +532,13 @@ mod tests {
// eth_friend (Index 0) // eth_friend (Index 0)
let expected_friend_stmts = vec![ let expected_friend_stmts = vec![
StatementTmpl { StatementTmpl {
pred: Predicate::Native(NativePredicate::Equal), pred: Predicate::Native(NativePredicate::SignedBy),
args: vec![ args: vec![sta_wc_lit("attestation_dict", 2), sta_wc_lit("src", 0)],
sta_ak(("attestation_pod", 2), "_type"), // Pub(0-1), Priv(2)
sta_lit(PodType::Signed),
],
}, },
StatementTmpl { StatementTmpl {
pred: Predicate::Native(NativePredicate::Equal), pred: Predicate::Native(NativePredicate::Equal),
args: vec![ args: vec![
sta_ak(("attestation_pod", 2), "_signer"), sta_ak(("attestation_dict", 2), "attestation"),
sta_wc_lit("src", 0), // Pub arg 0
],
},
StatementTmpl {
pred: Predicate::Native(NativePredicate::Equal),
args: vec![
sta_ak(("attestation_pod", 2), "attestation"),
sta_wc_lit("dst", 1), // Pub arg 1 sta_wc_lit("dst", 1), // Pub arg 1
], ],
}, },
@ -563,7 +549,7 @@ mod tests {
true, // AND true, // AND
expected_friend_stmts, expected_friend_stmts,
2, // public_args_len: src, dst 2, // public_args_len: src, dst
names(&["src", "dst", "attestation_pod"]), names(&["src", "dst", "attestation_dict"]),
)?; )?;
// eth_dos_distance_base (Index 1) // eth_dos_distance_base (Index 1)
@ -853,7 +839,6 @@ mod tests {
#[test] #[test]
fn test_e2e_literals() -> Result<(), LangError> { fn test_e2e_literals() -> Result<(), LangError> {
let pk = crate::backends::plonky2::primitives::ec::curve::Point::generator(); let pk = crate::backends::plonky2::primitives::ec::curve::Point::generator();
let pod_id = PodId(hash_str("test"));
let raw = RawValue::from(1); let raw = RawValue::from(1);
let string = "hello"; let string = "hello";
let int = 123; let int = 123;
@ -864,17 +849,14 @@ mod tests {
r#" r#"
REQUEST( REQUEST(
Equal(?A["pk"], {}) Equal(?A["pk"], {})
Equal(?B["pod_id"], {}) Equal(?B["raw"], {})
Equal(?C["raw"], {}) Equal(?C["string"], {})
Equal(?D["string"], {}) Equal(?D["int"], {})
Equal(?E["int"], {}) Equal(?E["bool"], {})
Equal(?F["bool"], {}) Equal(?F["sk"], {})
Equal(?G["sk"], {})
Equal(?H["self"], SELF)
) )
"#, "#,
Value::from(pk).to_podlang_string(), Value::from(pk).to_podlang_string(),
Value::from(pod_id).to_podlang_string(),
Value::from(raw).to_podlang_string(), Value::from(raw).to_podlang_string(),
Value::from(string).to_podlang_string(), Value::from(string).to_podlang_string(),
Value::from(int).to_podlang_string(), Value::from(int).to_podlang_string(),
@ -884,7 +866,6 @@ mod tests {
/* /*
REQUEST( REQUEST(
Equal(?A["pk"], PublicKey(3t9fNuU194n7mSJPRdeaJRMqw6ZQCUddzvECWNe1k2b1rdBezXpJxF)) Equal(?A["pk"], PublicKey(3t9fNuU194n7mSJPRdeaJRMqw6ZQCUddzvECWNe1k2b1rdBezXpJxF))
Equal(?B["pod_id"], 0x735b31d3aad0f5b66002ffe1dc7d2eaa0ee9c59c09b641e8261530c5f3a02f29)
Equal(?C["raw"], Raw(0x0000000000000000000000000000000000000000000000000000000000000001)) Equal(?C["raw"], Raw(0x0000000000000000000000000000000000000000000000000000000000000001))
Equal(?D["string"], "hello") Equal(?D["string"], "hello")
Equal(?E["int"], 123) Equal(?E["int"], 123)
@ -905,31 +886,23 @@ mod tests {
}, },
StatementTmpl { StatementTmpl {
pred: Predicate::Native(NativePredicate::Equal), 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 { StatementTmpl {
pred: Predicate::Native(NativePredicate::Equal), 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 { StatementTmpl {
pred: Predicate::Native(NativePredicate::Equal), 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 { StatementTmpl {
pred: Predicate::Native(NativePredicate::Equal), 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 { StatementTmpl {
pred: Predicate::Native(NativePredicate::Equal), pred: Predicate::Native(NativePredicate::Equal),
args: vec![sta_ak(("F", 5), "bool"), sta_lit(Value::from(bool))], args: vec![sta_ak(("F", 5), "sk"), sta_lit(Value::from(sk))],
},
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))],
}, },
]; ];
@ -972,21 +945,13 @@ mod tests {
let params = Params::default(); let params = Params::default();
let available_batches = &[]; let available_batches = &[];
let input = format!( let input = r#"
r#" identity_verified(username, private: identity_dict) = AND(
identity_verified(username, private: identity_pod) = AND( Equal(?identity_dict["username"], ?username)
Equal(?identity_pod["{key_type}"], {signed_pod_type}) Equal(?identity_dict["user_public_key"], ?user_public_key)
Equal(?identity_pod["{key_signer}"], {identity_server_pk})
Equal(?identity_pod["username"], ?username)
Equal(?identity_pod["user_public_key"], ?user_public_key)
) )
"#, "#
key_type = KEY_TYPE, .to_string();
signed_pod_type = PodType::Signed as u32,
key_signer = KEY_SIGNER,
identity_server_pk =
"0x0000000000000000000000000000000000000000000000000000000000000000"
);
let result = parse(&input, &params, available_batches); let result = parse(&input, &params, available_batches);

View file

@ -117,7 +117,7 @@ mod tests {
// Use anchored rule for failure cases // Use anchored rule for failure cases
assert_fails( assert_fails(
Rule::test_literal_raw, Rule::test_literal_raw,
"0x0000000000000000000000000000000000000000000000000000000000000000)", "0x0000000000000000000000000000000000000000000000000000000000000000",
); // Missing Raw() wrapper ); // Missing Raw() wrapper
assert_fails(Rule::test_literal_raw, "Raw(0xabc)"); // Fails (string is too short) 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) 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)), &format!("Raw(0x{})", "a".repeat(66)),
); // Fails (string is too long) ); // 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 // String
assert_parses(Rule::literal_string, "\"hello\""); assert_parses(Rule::literal_string, "\"hello\"");
assert_parses(Rule::literal_string, "\"escaped \\\" quote\""); 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, "#[1, 2, 3]");
assert_parses( assert_parses(
Rule::literal_set, Rule::literal_set,
"#[ \"a\", 0x0000000000000000000000000000000000000000000000000000000000000000 ]", "#[ \"a\", Raw(0x0000000000000000000000000000000000000000000000000000000000000000) ]",
); );
// Dict // Dict
@ -172,7 +156,7 @@ mod tests {
assert_parses(Rule::literal_dict, "{ \"nested\": { \"key\": 1 } }"); assert_parses(Rule::literal_dict, "{ \"nested\": { \"key\": 1 } }");
assert_parses( assert_parses(
Rule::literal_dict, Rule::literal_dict,
"{ \"raw_val\": 0x0000000000000000000000000000000000000000000000000000000000000000 } ", "{ \"raw_val\": Raw(0x0000000000000000000000000000000000000000000000000000000000000000) } ",
); );
assert_fails(Rule::literal_dict, "{ name: \"Alice\" }"); // Key must be string literal with quotes assert_fails(Rule::literal_dict, "{ name: \"Alice\" }"); // Key must be string literal with quotes
} }

View file

@ -64,6 +64,9 @@ impl StatementTmpl {
Predicate::Custom(custom_ref) => { Predicate::Custom(custom_ref) => {
write!(w, "{}", custom_ref.predicate().name)?; write!(w, "{}", custom_ref.predicate().name)?;
} }
Predicate::Intro(intro_ref) => {
write!(w, "{}", intro_ref.name)?;
}
Predicate::BatchSelf(index) => { Predicate::BatchSelf(index) => {
if let Some(batch) = batch_context { if let Some(batch) = batch_context {
if let Some(predicate) = batch.predicates.get(*index) { if let Some(predicate) = batch.predicates.get(*index) {
@ -523,16 +526,6 @@ mod tests {
assert_round_trip(&input); 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] #[test]
fn test_pretty_print_demonstration() { fn test_pretty_print_demonstration() {
let input = r#" let input = r#"

View file

@ -43,6 +43,7 @@ pub fn native_predicate_from_string(s: &str) -> Option<NativePredicate> {
"MaxOf" => Some(NativePredicate::MaxOf), "MaxOf" => Some(NativePredicate::MaxOf),
"HashOf" => Some(NativePredicate::HashOf), "HashOf" => Some(NativePredicate::HashOf),
"PublicKeyOf" => Some(NativePredicate::PublicKeyOf), "PublicKeyOf" => Some(NativePredicate::PublicKeyOf),
"SignedBy" => Some(NativePredicate::SignedBy),
"DictContains" => Some(NativePredicate::DictContains), "DictContains" => Some(NativePredicate::DictContains),
"DictNotContains" => Some(NativePredicate::DictNotContains), "DictNotContains" => Some(NativePredicate::DictNotContains),
"ArrayContains" => Some(NativePredicate::ArrayContains), "ArrayContains" => Some(NativePredicate::ArrayContains),
@ -328,17 +329,17 @@ fn pest_pair_to_builder_arg(
} }
Rule::anchored_key => { Rule::anchored_key => {
let mut inner_ak_pairs = arg_content_pair.clone().into_inner(); let mut inner_ak_pairs = arg_content_pair.clone().into_inner();
let pod_id_pair = inner_ak_pairs.next().unwrap(); let root_pair = inner_ak_pairs.next().unwrap();
let pod_id_wc_str = pod_id_pair.as_str().strip_prefix("?").unwrap(); let root_wc_str = root_pair.as_str().strip_prefix("?").unwrap();
if let StatementContext::CustomPredicate { if let StatementContext::CustomPredicate {
argument_names, argument_names,
pred_name, pred_name,
} = context } = context
{ {
if !argument_names.contains(pod_id_wc_str) { if !argument_names.contains(root_wc_str) {
return Err(ProcessorError::UndefinedWildcard { return Err(ProcessorError::UndefinedWildcard {
name: pod_id_wc_str.to_string(), name: root_wc_str.to_string(),
pred_name: pred_name.to_string(), pred_name: pred_name.to_string(),
span: Some(get_span(arg_content_pair)), 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_part_pair = inner_ak_pairs.next().unwrap();
let key_str = parse_pest_string_literal(&key_part_pair)?; 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()), _ => 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( fn validate_and_build_statement_template(
stmt_name_str: &str, stmt_name_str: &str,
pred: &Predicate, pred: &Predicate,
@ -374,7 +407,8 @@ fn validate_and_build_statement_template(
| NativePredicate::DictNotContains | NativePredicate::DictNotContains
| NativePredicate::SetNotContains | NativePredicate::SetNotContains
| NativePredicate::NotContains | NativePredicate::NotContains
| NativePredicate::PublicKeyOf => 2, | NativePredicate::PublicKeyOf
| NativePredicate::SignedBy => 2,
NativePredicate::Contains NativePredicate::Contains
| NativePredicate::ArrayContains | NativePredicate::ArrayContains
| NativePredicate::DictContains | NativePredicate::DictContains
@ -405,28 +439,23 @@ fn validate_and_build_statement_template(
} }
Predicate::Custom(custom_ref) => { Predicate::Custom(custom_ref) => {
let expected_arity = custom_ref.predicate().args_len; let expected_arity = custom_ref.predicate().args_len;
if args.len() != expected_arity { validate_dyn_len_predicate(
return Err(ProcessorError::ArgumentCountMismatch { stmt_name_str,
predicate: stmt_name_str.to_string(), &args,
expected: expected_arity, expected_arity,
found: args.len(), stmt_span,
span: Some(stmt_name_span), 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),
});
}
} }
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(_) => { Predicate::BatchSelf(_) => {
let (_original_pred_idx, expected_arity_val) = processing_ctx let (_original_pred_idx, expected_arity_val) = processing_ctx
@ -650,8 +679,8 @@ fn process_statement_template(
for arg in &builder_args { for arg in &builder_args {
match arg { match arg {
BuilderArg::WildcardLiteral(name) => temp_stmt_wildcard_names.push(name.clone()), BuilderArg::WildcardLiteral(name) => temp_stmt_wildcard_names.push(name.clone()),
BuilderArg::Key(pod_id_wc_str, _key_str) => { BuilderArg::Key(root_wc_str, _key_str) => {
temp_stmt_wildcard_names.push(pod_id_wc_str.clone()); temp_stmt_wildcard_names.push(root_wc_str.clone());
} }
_ => {} _ => {}
} }
@ -742,14 +771,6 @@ fn process_literal_value(
}) })
.map(Value::from) .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 => { Rule::literal_public_key => {
let pk_str_pair = inner_lit.into_inner().next().unwrap(); let pk_str_pair = inner_lit.into_inner().next().unwrap();
let pk_b58 = pk_str_pair.as_str(); let pk_b58 = pk_str_pair.as_str();
@ -826,7 +847,6 @@ fn process_literal_value(
})?; })?;
Ok(Value::from(secret_key)) Ok(Value::from(secret_key))
} }
Rule::self_keyword => Ok(Value::from(middleware::SELF)),
_ => unreachable!("Unexpected rule: {:?}", inner_lit.as_rule()), _ => unreachable!("Unexpected rule: {:?}", inner_lit.as_rule()),
} }
} }
@ -912,11 +932,6 @@ fn parse_hex_str_to_raw_value(hex_str: &str) -> Result<middleware::RawValue, Pro
Ok(middleware::RawValue(v)) Ok(middleware::RawValue(v))
} }
fn parse_hex_str_to_pod_id(hex_str: &str) -> Result<middleware::PodId, ProcessorError> {
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 // 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). // based on an ordered list of names from the current scope (e.g., request or predicate def).
fn resolve_wildcard( fn resolve_wildcard(
@ -945,10 +960,10 @@ fn resolve_request_statement_builder(
for builder_arg in stb.args { for builder_arg in stb.args {
let mw_arg = match builder_arg { let mw_arg = match builder_arg {
BuilderArg::Literal(v) => StatementTmplArg::Literal(v), BuilderArg::Literal(v) => StatementTmplArg::Literal(v),
BuilderArg::Key(pod_id_wc_str, key_str) => { BuilderArg::Key(root_wc_str, key_str) => {
let pod_id_wc = resolve_wildcard(ordered_request_wildcard_names, &pod_id_wc_str)?; let root_wc = resolve_wildcard(ordered_request_wildcard_names, &root_wc_str)?;
let key = Key::from(key_str); let key = Key::from(key_str);
StatementTmplArg::AnchoredKey(pod_id_wc, key) StatementTmplArg::AnchoredKey(root_wc, key)
} }
BuilderArg::WildcardLiteral(wc_name) => { BuilderArg::WildcardLiteral(wc_name) => {
let wc = resolve_wildcard(ordered_request_wildcard_names, &wc_name)?; let wc = resolve_wildcard(ordered_request_wildcard_names, &wc_name)?;

View file

@ -27,6 +27,18 @@ pub struct Dictionary {
kvs: HashMap<Key, Value>, kvs: HashMap<Key, Value>,
} }
#[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 { impl Dictionary {
/// max_depth determines the depth of the underlying MerkleTree, allowing to /// max_depth determines the depth of the underlying MerkleTree, allowing to
/// store 2^max_depth elements in the Dictionary /// store 2^max_depth elements in the Dictionary

View file

@ -33,7 +33,7 @@ impl fmt::Display for Wildcard {
impl ToFields for Wildcard { impl ToFields for Wildcard {
fn to_fields(&self, _params: &Params) -> Vec<F> { fn to_fields(&self, _params: &Params) -> Vec<F> {
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 { match self {
Self::None => write!(f, "none"), Self::None => write!(f, "none"),
Self::Literal(v) => v.fmt(f), Self::Literal(v) => v.fmt(f),
Self::AnchoredKey(pod_id, key) => { Self::AnchoredKey(root, key) => {
pod_id.fmt(f)?; root.fmt(f)?;
write!(f, "[")?; write!(f, "[")?;
key.fmt(f)?; key.fmt(f)?;
write!(f, "]") write!(f, "]")
@ -451,10 +451,13 @@ impl CustomPredicateRef {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::middleware::{ use crate::{
dict,
middleware::{
AnchoredKey, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, Key, AnchoredKey, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, Key,
NativePredicate, Operation, Params, PodType, Predicate, Statement, StatementTmpl, NativePredicate, Operation, Params, Predicate, Statement, StatementTmpl,
StatementTmplArg, SELF, StatementTmplArg,
},
}; };
fn st(p: Predicate, args: Vec<StatementTmplArg>) -> StatementTmpl { fn st(p: Predicate, args: Vec<StatementTmplArg>) -> 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( let custom_statement = Statement::Custom(
CustomPredicateRef::new(cust_pred_batch.clone(), 0), CustomPredicateRef::new(cust_pred_batch.clone(), 0),
vec![Value::from(SELF)], vec![Value::from(d0.clone())],
); );
let custom_deduction = Operation::Custom( let custom_deduction = Operation::Custom(
CustomPredicateRef::new(cust_pred_batch, 0), CustomPredicateRef::new(cust_pred_batch, 0),
vec![ vec![
Statement::equal(AnchoredKey::from((SELF, "c")), 2), Statement::equal(AnchoredKey::from((&d1, "c")), 2),
Statement::product_of( Statement::product_of(
AnchoredKey::from((SELF, "a")), AnchoredKey::from((&d0, "a")),
AnchoredKey::from((SELF, "b")), AnchoredKey::from((&d1, "b")),
Value::from(3), Value::from(3),
), ),
], ],
@ -548,18 +558,8 @@ mod tests {
"eth_friend".into(), "eth_friend".into(),
vec![ vec![
st( st(
P::Native(NP::Equal), P::Native(NP::SignedBy),
vec![ vec![STA::Wildcard(wc(2)), STA::Wildcard(wc(0))],
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)),
],
), ),
st( st(
P::Native(NP::Equal), P::Native(NP::Equal),

View file

@ -3,7 +3,7 @@
use std::{backtrace::Backtrace, fmt::Debug}; use std::{backtrace::Backtrace, fmt::Debug};
use crate::middleware::{ use crate::middleware::{
CustomPredicate, Key, Operation, PodId, Predicate, Statement, StatementArg, StatementTmplArg, CustomPredicate, Hash, Key, Operation, Predicate, Statement, StatementArg, StatementTmplArg,
Value, Wildcard, Value, Wildcard,
}; };
@ -24,7 +24,7 @@ pub enum MiddlewareInnerError {
#[error("{0} should be assigned the value {1} but has previously been assigned {2}")] #[error("{0} should be assigned the value {1} but has previously been assigned {2}")]
InvalidWildcardAssignment(Wildcard, Value, Value), InvalidWildcardAssignment(Wildcard, Value, Value),
#[error("{0} matches POD ID {1}, yet the template key {2} does not match {3}")] #[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}")] #[error("{0} does not match against {1}")]
MismatchedStatementTmplArg(StatementTmplArg, StatementArg), MismatchedStatementTmplArg(StatementTmplArg, StatementArg),
#[error("Expected a statement of type {0}, got {1}")] #[error("Expected a statement of type {0}, got {1}")]
@ -90,14 +90,14 @@ impl Error {
new!(InvalidWildcardAssignment(wildcard, value, prev_value)) new!(InvalidWildcardAssignment(wildcard, value, prev_value))
} }
pub(crate) fn mismatched_anchored_key_in_statement_tmpl_arg( pub(crate) fn mismatched_anchored_key_in_statement_tmpl_arg(
pod_id_wildcard: Wildcard, root_wildcard: Wildcard,
pod_id: PodId, root: Hash,
key_tmpl: Key, key_tmpl: Key,
key: Key, key: Key,
) -> Self { ) -> Self {
new!(MismatchedAnchoredKeyInStatementTmplArg( new!(MismatchedAnchoredKeyInStatementTmplArg(
pod_id_wildcard, root_wildcard,
pod_id, root,
key_tmpl, key_tmpl,
key key
)) ))

View file

@ -6,6 +6,7 @@ use std::sync::Arc;
use hex::ToHex; use hex::ToHex;
use itertools::Itertools; use itertools::Itertools;
use strum_macros::FromRepr; use strum_macros::FromRepr;
mod basetypes; mod basetypes;
use std::{cmp::PartialEq, hash}; use std::{cmp::PartialEq, hash};
@ -19,7 +20,7 @@ mod operation;
mod pod_deserialization; mod pod_deserialization;
pub mod serialization; pub mod serialization;
mod statement; mod statement;
use std::{any::Any, collections::HashMap, fmt}; use std::{any::Any, fmt};
pub use basetypes::*; pub use basetypes::*;
pub use custom::*; pub use custom::*;
@ -30,13 +31,10 @@ pub use pod_deserialization::*;
use serialization::*; use serialization::*;
pub use statement::*; pub use statement::*;
use crate::backends::plonky2::primitives::{ use crate::backends::plonky2::primitives::merkletree::{
ec::{curve::Point as PublicKey, schnorr::SecretKey}, MerkleProof, MerkleTreeStateTransitionProof,
merkletree::{MerkleProof, MerkleTreeStateTransitionProof},
}; };
pub const SELF: PodId = PodId(SELF_ID_HASH);
// TODO: Move all value-related types to to `value.rs` // TODO: Move all value-related types to to `value.rs`
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
// TODO #[schemars(transform = serialization::transform_value_schema)] // TODO #[schemars(transform = serialization::transform_value_schema)]
@ -64,7 +62,6 @@ pub enum TypedValue {
PublicKey(PublicKey), PublicKey(PublicKey),
// Schnorr secret key variant (scalar) // Schnorr secret key variant (scalar)
SecretKey(SecretKey), SecretKey(SecretKey),
PodId(PodId),
// UNTAGGED TYPES: // UNTAGGED TYPES:
#[serde(untagged)] #[serde(untagged)]
Set(Set), Set(Set),
@ -120,12 +117,6 @@ impl From<SecretKey> for TypedValue {
} }
} }
impl From<PodId> for TypedValue {
fn from(id: PodId) -> Self {
TypedValue::PodId(id)
}
}
impl From<Set> for TypedValue { impl From<Set> for TypedValue {
fn from(s: Set) -> Self { fn from(s: Set) -> Self {
TypedValue::Set(s) TypedValue::Set(s)
@ -150,12 +141,6 @@ impl From<RawValue> for TypedValue {
} }
} }
impl From<PodType> for TypedValue {
fn from(t: PodType) -> Self {
TypedValue::from(t as i64)
}
}
impl TryFrom<&TypedValue> for i64 { impl TryFrom<&TypedValue> for i64 {
type Error = Error; type Error = Error;
fn try_from(v: &TypedValue) -> std::result::Result<Self, Self::Error> { fn try_from(v: &TypedValue) -> std::result::Result<Self, Self::Error> {
@ -167,30 +152,23 @@ impl TryFrom<&TypedValue> for i64 {
} }
} }
impl TryFrom<TypedValue> for Key { impl TryFrom<&TypedValue> for String {
type Error = Error; type Error = Error;
fn try_from(tv: TypedValue) -> Result<Self> { fn try_from(tv: &TypedValue) -> Result<Self> {
match tv { match tv {
TypedValue::String(s) => Ok(Key::new(s)), TypedValue::String(s) => Ok(s.clone()),
_ => Err(Error::custom(format!( _ => Err(Error::custom(format!(
"Value {} cannot be converted to a key.", "Value {} cannot be converted to a string.",
tv tv
))), ))),
} }
} }
} }
impl TryFrom<&TypedValue> for PodId { impl TryFrom<&TypedValue> for Key {
type Error = Error; type Error = Error;
fn try_from(v: &TypedValue) -> Result<Self> { fn try_from(tv: &TypedValue) -> Result<Self> {
match v { Ok(Key::new(String::try_from(tv)?))
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
))),
}
} }
} }
@ -262,13 +240,6 @@ impl fmt::Display for TypedValue {
} }
TypedValue::PublicKey(p) => write!(f, "PublicKey({})", p), TypedValue::PublicKey(p) => write!(f, "PublicKey({})", p),
TypedValue::SecretKey(p) => write!(f, "SecretKey({})", 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::<String>())
}
}
TypedValue::Raw(r) => { TypedValue::Raw(r) => {
write!(f, "Raw(0x{})", r.encode_hex::<String>()) write!(f, "Raw(0x{})", r.encode_hex::<String>())
} }
@ -288,7 +259,6 @@ impl From<&TypedValue> for RawValue {
TypedValue::Raw(v) => *v, TypedValue::Raw(v) => *v,
TypedValue::PublicKey(p) => RawValue::from(hash_fields(&p.as_fields())), TypedValue::PublicKey(p) => RawValue::from(hash_fields(&p.as_fields())),
TypedValue::SecretKey(sk) => RawValue::from(hash_fields(&sk.to_limbs())), 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() ..Default::default()
}; };
let pod_id_schema = schemars::schema::SchemaObject { let root_schema = schemars::schema::SchemaObject {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))), instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))),
object: Some(Box::new(schemars::schema::ObjectValidation { object: Some(Box::new(schemars::schema::ObjectValidation {
properties: [("PodId".to_string(), gen.subschema_for::<PodId>())] properties: [("Root".to_string(), gen.subschema_for::<Hash>())]
.into_iter() .into_iter()
.collect(), .collect(),
required: ["PodId".to_string()].into_iter().collect(), required: ["Root".to_string()].into_iter().collect(),
..Default::default() ..Default::default()
})), })),
..Default::default() ..Default::default()
@ -390,7 +360,7 @@ impl JsonSchema for TypedValue {
Schema::Object(SchemaObject { Schema::Object(SchemaObject {
subschemas: Some(Box::new(schemars::schema::SubschemaValidation { subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
any_of: Some(vec![ any_of: Some(vec![
Schema::Object(pod_id_schema), Schema::Object(root_schema),
Schema::Object(int_schema), Schema::Object(int_schema),
Schema::Object(raw_schema), Schema::Object(raw_schema),
Schema::Object(public_key_schema), Schema::Object(public_key_schema),
@ -498,7 +468,7 @@ impl Value {
key, self 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)?)), TypedValue::Set(s) => Ok((key, s.prove(key)?)),
_ => Err(Error::custom(format!( _ => Err(Error::custom(format!(
"Invalid container value {}", "Invalid container value {}",
@ -512,7 +482,7 @@ impl Value {
TypedValue::Array(_) => Err(Error::custom( TypedValue::Array(_) => Err(Error::custom(
"Arrays do not support `NotContains` operation.".to_string(), "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), TypedValue::Set(s) => s.prove_nonexistence(key),
_ => Err(Error::custom(format!( _ => Err(Error::custom(format!(
"Invalid container value {}", "Invalid container value {}",
@ -529,7 +499,7 @@ impl Value {
) -> Result<MerkleTreeStateTransitionProof> { ) -> Result<MerkleTreeStateTransitionProof> {
let container = self.typed().clone(); let container = self.typed().clone();
match container { 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), TypedValue::Set(mut s) => s.insert(value),
_ => Err(Error::custom(format!( _ => Err(Error::custom(format!(
"Invalid container value {}", "Invalid container value {}",
@ -553,7 +523,7 @@ impl Value {
key, self 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!( _ => Err(Error::custom(format!(
"Invalid container value {} for update op", "Invalid container value {} for update op",
self.typed() self.typed()
@ -565,7 +535,7 @@ impl Value {
pub(crate) fn prove_deletion(&self, key: &Value) -> Result<MerkleTreeStateTransitionProof> { pub(crate) fn prove_deletion(&self, key: &Value) -> Result<MerkleTreeStateTransitionProof> {
let container = self.typed().clone(); let container = self.typed().clone();
match container { 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), TypedValue::Set(mut s) => s.delete(key),
_ => Err(Error::custom(format!( _ => Err(Error::custom(format!(
"Invalid container value {}", "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)] #[derive(Clone, Debug, Eq)]
pub struct Key { pub struct Key {
name: String, name: String,
@ -708,32 +658,32 @@ impl JsonSchema for Key {
#[derive(Clone, Debug, Eq, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Debug, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct AnchoredKey { pub struct AnchoredKey {
pub pod_id: PodId, pub root: Hash,
pub key: Key, pub key: Key,
} }
impl AnchoredKey { impl AnchoredKey {
pub fn new(pod_id: PodId, key: Key) -> Self { pub fn new(root: Hash, key: Key) -> Self {
Self { pod_id, key } Self { root, key }
} }
} }
impl hash::Hash for AnchoredKey { impl hash::Hash for AnchoredKey {
fn hash<H: hash::Hasher>(&self, state: &mut H) { fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.pod_id.hash(state); self.root.hash(state);
self.key.hash.hash(state); self.key.hash.hash(state);
} }
} }
impl PartialEq for AnchoredKey { impl PartialEq for AnchoredKey {
fn eq(&self, other: &Self) -> bool { 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 { impl fmt::Display for AnchoredKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.pod_id.fmt(f)?; self.root.fmt(f)?;
write!(f, "[")?; write!(f, "[")?;
self.key.fmt(f)?; self.key.fmt(f)?;
write!(f, "]")?; write!(f, "]")?;
@ -741,31 +691,30 @@ impl fmt::Display for AnchoredKey {
} }
} }
impl<T> From<(PodId, T)> for AnchoredKey impl<T> From<(Hash, T)> for AnchoredKey
where where
T: Into<Key>, T: Into<Key>,
{ {
fn from((pod_id, t): (PodId, T)) -> Self { fn from((root, t): (Hash, T)) -> Self {
Self::new(pod_id, t.into()) Self::new(root, t.into())
} }
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize, JsonSchema)] impl<T> From<(&Dictionary, T)> for AnchoredKey
pub struct PodId(pub Hash); where
T: Into<Key>,
impl ToFields for PodId { {
fn to_fields(&self, params: &Params) -> Vec<F> { fn from((dict, t): (&Dictionary, T)) -> Self {
self.0.to_fields(params) Self::new(dict.commitment(), t.into())
} }
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, FromRepr, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Copy, Debug, PartialEq, Eq, FromRepr, Serialize, Deserialize, JsonSchema)]
pub enum PodType { pub enum PodType {
Signed = 1, Main = 1,
Main = 2, Empty = 2,
Empty = 3, MockMain = 101,
MockMain = 102, MockEmpty = 102,
MockEmpty = 103,
} }
impl fmt::Display for PodType { impl fmt::Display for PodType {
@ -773,7 +722,6 @@ impl fmt::Display for PodType {
match self { match self {
PodType::MockMain => write!(f, "MockMain"), PodType::MockMain => write!(f, "MockMain"),
PodType::MockEmpty => write!(f, "MockEmpty"), PodType::MockEmpty => write!(f, "MockEmpty"),
PodType::Signed => write!(f, "Signed"),
PodType::Main => write!(f, "Main"), PodType::Main => write!(f, "Main"),
PodType::Empty => write!(f, "Empty"), PodType::Empty => write!(f, "Empty"),
} }
@ -784,11 +732,9 @@ impl fmt::Display for PodType {
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Hash)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Params { pub struct Params {
pub max_input_signed_pods: usize, pub max_input_pods: usize,
pub max_input_recursive_pods: usize,
pub max_input_pods_public_statements: usize, pub max_input_pods_public_statements: usize,
pub max_statements: usize, pub max_statements: usize,
pub max_signed_pod_values: usize,
pub max_public_statements: usize, pub max_public_statements: usize,
pub max_operation_args: usize, pub max_operation_args: usize,
// max number of custom predicates batches that a MainPod can use // max number of custom predicates batches that a MainPod can use
@ -809,13 +755,15 @@ pub struct Params {
pub max_depth_mt_vds: usize, pub max_depth_mt_vds: usize,
// maximum number of public key derivations used for PublicKeyOf operation // maximum number of public key derivations used for PublicKeyOf operation
pub max_public_key_of: usize, 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 // 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. // different circuits to be compatible in their verification.
// //
// Number of public statements to hash to calculate the id. Must be equal or greater than // Number of public statements to hash to calculate the public inputs. Must be equal or
// `max_public_statements`. // greater than `max_public_statements`.
pub num_public_statements_id: usize, pub num_public_statements_hash: usize,
pub max_statement_args: usize, pub max_statement_args: usize,
// //
// The following parameters define how a custom predicate batch id is calculated. // The following parameters define how a custom predicate batch id is calculated.
@ -829,13 +777,11 @@ pub struct Params {
impl Default for Params { impl Default for Params {
fn default() -> Self { fn default() -> Self {
Self { Self {
max_input_signed_pods: 3, max_input_pods: 2,
max_input_recursive_pods: 2,
max_input_pods_public_statements: 10, max_input_pods_public_statements: 10,
max_statements: 20, max_statements: 40,
max_signed_pod_values: 8,
max_public_statements: 10, max_public_statements: 10,
num_public_statements_id: 16, num_public_statements_hash: 16,
max_statement_args: 5, max_statement_args: 5,
max_operation_args: 5, max_operation_args: 5,
max_custom_predicate_batches: 2, max_custom_predicate_batches: 2,
@ -843,11 +789,12 @@ impl Default for Params {
max_custom_predicate_arity: 5, max_custom_predicate_arity: 5,
max_custom_predicate_wildcards: 10, max_custom_predicate_wildcards: 10,
max_custom_batch_size: 5, // TODO: Move down to 4? 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_merkle_tree_state_transition_proofs_containers: 5,
max_depth_mt_containers: 32, max_depth_mt_containers: 32,
max_depth_mt_vds: 6, // up to 64 (2^6) different pod circuits max_depth_mt_vds: 6, // up to 64 (2^6) different pod circuits
max_public_key_of: 2, 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 /// Total size of the statement table including None, input statements from signed pods and
/// input recursive pods and new statements (public & private) /// input recursive pods and new statements (public & private)
pub fn statement_table_size(&self) -> usize { pub fn statement_table_size(&self) -> usize {
1 + self.max_input_signed_pods * self.max_signed_pod_values 1 + self.max_input_pods * self.max_input_pods_public_statements + self.max_statements
+ self.max_input_recursive_pods * self.max_input_pods_public_statements
+ self.max_statements
} }
/// Parameters that define how the id is calculated /// Parameters that define how the id is calculated
pub fn id_params(&self) -> Vec<usize> { pub fn id_params(&self) -> Vec<usize> {
vec![ vec![
self.num_public_statements_id, self.num_public_statements_hash,
self.max_statement_args, self.max_statement_args,
self.max_custom_predicate_arity, self.max_custom_predicate_arity,
self.max_custom_batch_size, self.max_custom_batch_size,
@ -920,23 +865,19 @@ impl Params {
} }
} }
/// Replace references to SELF by `self_id`. /// Replace EMPTY_HASH in IntroPredicateRef by verifier_data_hash
pub fn normalize_statement(statement: &Statement, self_id: PodId) -> Statement { pub fn normalize_statement(statement: &Statement, verifier_data_hash: Hash) -> Statement {
let predicate = statement.predicate(); match statement {
let args = statement Statement::Intro(ir, args) if ir.verifier_data_hash == EMPTY_HASH => Statement::Intro(
.args() IntroPredicateRef {
.iter() name: ir.name.clone(),
.map(|sa| match &sa { args_len: ir.args_len,
StatementArg::Key(AnchoredKey { pod_id, key }) if *pod_id == SELF => { verifier_data_hash,
StatementArg::Key(AnchoredKey::new(self_id, key.clone())) },
args.clone(),
),
s => s.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")
} }
pub trait EqualsAny { pub trait EqualsAny {
@ -953,10 +894,27 @@ impl<T: Any + Eq> 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 { pub trait Pod: fmt::Debug + DynClone + Sync + Send + Any + EqualsAny {
fn params(&self) -> &Params; fn params(&self) -> &Params;
fn verify(&self) -> Result<(), BackendError>; 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. /// Return a uuid of the pod type and its name. The name is only used as metadata.
fn pod_type(&self) -> (usize, &'static str); fn pod_type(&self) -> (usize, &'static str);
/// Statements as internally generated, where self-referencing arguments use SELF in the /// 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 /// Normalized statements, where self-referencing arguments use the pod id instead of SELF in
/// the anchored key. /// the anchored key.
fn pub_statements(&self) -> Vec<Statement> { fn pub_statements(&self) -> Vec<Statement> {
let verifier_data_hash = self.verifier_data_hash();
self.pub_self_statements() self.pub_self_statements()
.into_iter() .into_iter()
.map(|statement| normalize_statement(&statement, self.id())) .map(|statement| normalize_statement(&statement, verifier_data_hash))
.collect() .collect()
} }
/// Return this Pods data serialized into a json value. This serialization can skip `params, /// Return this Pods data serialized into a json value. This serialization can skip `params,
/// id, vds_root` /// id, vds_root`
fn serialize_data(&self) -> serde_json::Value; fn serialize_data(&self) -> serde_json::Value;
/// Extract key-values from ValueOf public statements /// Returns the deserialized Pod.
fn kvs(&self) -> HashMap<AnchoredKey, Value> { fn deserialize_data(
self.pub_statements() params: Params,
.into_iter() data: serde_json::Value,
.filter_map(|st| match st { vd_set: VDSet,
Statement::Equal(ValueRef::Key(ak), ValueRef::Literal(v)) => Some((ak, v)), id: Hash,
_ => None, ) -> Result<Self, BackendError>
}) where
.collect() Self: Sized;
}
fn equals(&self, other: &dyn Pod) -> bool { fn equals(&self, other: &dyn Pod) -> bool {
self.equals_any(other as &dyn Any) 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<dyn Pod> { impl PartialEq for Box<dyn Pod> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
@ -1000,66 +968,24 @@ impl Eq for Box<dyn Pod> {}
// impl Clone for Box<dyn Pod> // impl Clone for Box<dyn Pod>
dyn_clone::clone_trait_object!(Pod); dyn_clone::clone_trait_object!(Pod);
/// Trait for pods that are generated with a plonky2 circuit and that can be verified by a pub trait Signer {
/// recursive MainPod circuit. A Pod implementing this trait does not necesarilly come from fn sign(&self, msg: RawValue) -> Signature;
/// recursion: for example an introduction Pod in general is not recursive. fn public_key(&self) -> PublicKey;
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<Box<dyn RecursivePod>, BackendError>
where
Self: Sized;
}
impl PartialEq for Box<dyn RecursivePod> {
fn eq(&self, other: &Self) -> bool {
self.equals(&**other)
}
}
impl Eq for Box<dyn RecursivePod> {}
// impl Clone for Box<dyn RecursivePod>
dyn_clone::clone_trait_object!(RecursivePod);
pub trait PodSigner {
fn sign(
&self,
params: &Params,
kvs: &HashMap<Key, Value>,
) -> Result<Box<dyn Pod>, BackendError>;
} }
#[derive(Debug)] #[derive(Debug)]
pub struct MainPodInputs<'a> { pub struct MainPodInputs<'a> {
pub signed_pods: &'a [&'a dyn Pod], pub pods: &'a [&'a dyn Pod],
pub recursive_pods: &'a [&'a dyn RecursivePod],
pub statements: &'a [Statement], pub statements: &'a [Statement],
pub operations: &'a [Operation], pub operations: &'a [Operation],
/// Statements that need to be made public (they can come from input pods or input /// Statements that need to be made public (they can come from input pods or input
/// statements) /// statements)
pub public_statements: &'a [Statement], pub public_statements: &'a [Statement],
// TODO: REMOVE THIS
pub vd_set: VDSet, pub vd_set: VDSet,
} }
pub trait PodProver { pub trait MainPodProver {
fn prove( fn prove(&self, params: &Params, inputs: MainPodInputs) -> Result<Box<dyn Pod>, BackendError>;
&self,
params: &Params,
vd_set: &VDSet,
inputs: MainPodInputs,
) -> Result<Box<dyn RecursivePod>, BackendError>;
} }
pub trait ToFields { pub trait ToFields {

View file

@ -8,14 +8,14 @@ use crate::{
backends::plonky2::primitives::{ backends::plonky2::primitives::{
ec::{ ec::{
curve::{Point as PublicKey, GROUP_ORDER}, curve::{Point as PublicKey, GROUP_ORDER},
schnorr::SecretKey, schnorr::{SecretKey, Signature},
}, },
merkletree::{MerkleProof, MerkleTree, MerkleTreeOp, MerkleTreeStateTransitionProof}, merkletree::{MerkleProof, MerkleTree, MerkleTreeOp, MerkleTreeStateTransitionProof},
}, },
middleware::{ middleware::{
hash_values, AnchoredKey, CustomPredicate, CustomPredicateRef, Error, NativePredicate, hash_values, AnchoredKey, CustomPredicate, CustomPredicateRef, Error, Hash, Key,
Params, Predicate, Result, Statement, StatementArg, StatementTmpl, StatementTmplArg, NativePredicate, Params, Predicate, Result, Statement, StatementArg, StatementTmpl,
ToFields, TypedValue, Value, ValueRef, Wildcard, F, SELF, StatementTmplArg, ToFields, TypedValue, Value, ValueRef, Wildcard, F,
}, },
}; };
@ -30,6 +30,7 @@ pub enum OperationAux {
None, None,
MerkleProof(MerkleProof), MerkleProof(MerkleProof),
MerkleTreeStateTransitionProof(MerkleTreeStateTransitionProof), MerkleTreeStateTransitionProof(MerkleTreeStateTransitionProof),
Signature(Signature),
} }
impl fmt::Display for OperationAux { impl fmt::Display for OperationAux {
@ -41,6 +42,7 @@ impl fmt::Display for OperationAux {
Self::MerkleTreeStateTransitionProof(pf) => { Self::MerkleTreeStateTransitionProof(pf) => {
write!(f, "merkle_tree_state_transition_proof({:?})", pf)? write!(f, "merkle_tree_state_transition_proof({:?})", pf)?
} }
Self::Signature(sig) => write!(f, "signature({:?})", sig)?,
} }
Ok(()) 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 { pub enum NativeOperation {
None = 0, None = 0,
NewEntry = 1, CopyStatement = 1,
CopyStatement = 2, EqualFromEntries = 2,
EqualFromEntries = 3, NotEqualFromEntries = 3,
NotEqualFromEntries = 4, LtEqFromEntries = 4,
LtEqFromEntries = 5, LtFromEntries = 5,
LtFromEntries = 6, TransitiveEqualFromStatements = 6,
TransitiveEqualFromStatements = 7, LtToNotEqual = 7,
LtToNotEqual = 8, ContainsFromEntries = 8,
ContainsFromEntries = 9, NotContainsFromEntries = 9,
NotContainsFromEntries = 10, SumOf = 10,
SumOf = 11, ProductOf = 11,
ProductOf = 12, MaxOf = 12,
MaxOf = 13, HashOf = 13,
HashOf = 14, PublicKeyOf = 14,
PublicKeyOf = 15, SignedBy = 15,
ContainerInsertFromEntries = 16, ContainerInsertFromEntries = 16,
ContainerUpdateFromEntries = 17, ContainerUpdateFromEntries = 17,
ContainerDeleteFromEntries = 18, ContainerDeleteFromEntries = 18,
@ -127,7 +129,6 @@ impl OperationType {
match self { match self {
OperationType::Native(native_op) => match native_op { OperationType::Native(native_op) => match native_op {
NativeOperation::None => Some(Predicate::Native(NativePredicate::None)), NativeOperation::None => Some(Predicate::Native(NativePredicate::None)),
NativeOperation::NewEntry => Some(Predicate::Native(NativePredicate::Equal)),
NativeOperation::CopyStatement => None, NativeOperation::CopyStatement => None,
NativeOperation::EqualFromEntries => { NativeOperation::EqualFromEntries => {
Some(Predicate::Native(NativePredicate::Equal)) Some(Predicate::Native(NativePredicate::Equal))
@ -154,6 +155,7 @@ impl OperationType {
NativeOperation::PublicKeyOf => { NativeOperation::PublicKeyOf => {
Some(Predicate::Native(NativePredicate::PublicKeyOf)) Some(Predicate::Native(NativePredicate::PublicKeyOf))
} }
NativeOperation::SignedBy => Some(Predicate::Native(NativePredicate::SignedBy)),
NativeOperation::ContainerInsertFromEntries => { NativeOperation::ContainerInsertFromEntries => {
Some(Predicate::Native(NativePredicate::ContainerInsert)) Some(Predicate::Native(NativePredicate::ContainerInsert))
} }
@ -174,7 +176,6 @@ impl OperationType {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Operation { pub enum Operation {
None, None,
NewEntry,
CopyStatement(Statement), CopyStatement(Statement),
EqualFromEntries(Statement, Statement), EqualFromEntries(Statement, Statement),
NotEqualFromEntries(Statement, Statement), NotEqualFromEntries(Statement, Statement),
@ -198,6 +199,7 @@ pub enum Operation {
MaxOf(Statement, Statement, Statement), MaxOf(Statement, Statement, Statement),
HashOf(Statement, Statement, Statement), HashOf(Statement, Statement, Statement),
PublicKeyOf(Statement, Statement), PublicKeyOf(Statement, Statement),
SignedBy(Statement, Statement, Signature),
ContainerInsertFromEntries( ContainerInsertFromEntries(
/* new_root */ Statement, /* new_root */ Statement,
/* old_root */ Statement, /* old_root */ Statement,
@ -243,7 +245,6 @@ impl Operation {
use NativeOperation::*; use NativeOperation::*;
match self { match self {
Self::None => OT::Native(None), Self::None => OT::Native(None),
Self::NewEntry => OT::Native(NewEntry),
Self::CopyStatement(_) => OT::Native(CopyStatement), Self::CopyStatement(_) => OT::Native(CopyStatement),
Self::EqualFromEntries(_, _) => OT::Native(EqualFromEntries), Self::EqualFromEntries(_, _) => OT::Native(EqualFromEntries),
Self::NotEqualFromEntries(_, _) => OT::Native(NotEqualFromEntries), Self::NotEqualFromEntries(_, _) => OT::Native(NotEqualFromEntries),
@ -258,6 +259,7 @@ impl Operation {
Self::MaxOf(_, _, _) => OT::Native(MaxOf), Self::MaxOf(_, _, _) => OT::Native(MaxOf),
Self::HashOf(_, _, _) => OT::Native(HashOf), Self::HashOf(_, _, _) => OT::Native(HashOf),
Self::PublicKeyOf(_, _) => OT::Native(PublicKeyOf), Self::PublicKeyOf(_, _) => OT::Native(PublicKeyOf),
Self::SignedBy(_, _, _) => OT::Native(SignedBy),
Self::ContainerInsertFromEntries(_, _, _, _, _) => { Self::ContainerInsertFromEntries(_, _, _, _, _) => {
OT::Native(ContainerInsertFromEntries) OT::Native(ContainerInsertFromEntries)
} }
@ -272,7 +274,6 @@ impl Operation {
pub fn args(&self) -> Vec<Statement> { pub fn args(&self) -> Vec<Statement> {
match self.clone() { match self.clone() {
Self::None => vec![], Self::None => vec![],
Self::NewEntry => vec![],
Self::CopyStatement(s) => vec![s], Self::CopyStatement(s) => vec![s],
Self::EqualFromEntries(s1, s2) => vec![s1, s2], Self::EqualFromEntries(s1, s2) => vec![s1, s2],
Self::NotEqualFromEntries(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::MaxOf(s1, s2, s3) => vec![s1, s2, s3],
Self::HashOf(s1, s2, s3) => vec![s1, s2, s3], Self::HashOf(s1, s2, s3) => vec![s1, s2, s3],
Self::PublicKeyOf(s1, s2) => vec![s1, s2], 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::ContainerInsertFromEntries(s1, s2, s3, s4, _pf) => vec![s1, s2, s3, s4],
Self::ContainerUpdateFromEntries(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], Self::ContainerDeleteFromEntries(s1, s2, s3, _pf) => vec![s1, s2, s3],
@ -310,7 +312,6 @@ impl Operation {
Ok(match op_code { Ok(match op_code {
OperationType::Native(o) => match (o, &args, aux.clone()) { OperationType::Native(o) => match (o, &args, aux.clone()) {
(NO::None, &[], OA::None) => Self::None, (NO::None, &[], OA::None) => Self::None,
(NO::NewEntry, &[], OA::None) => Self::NewEntry,
(NO::CopyStatement, &[s], OA::None) => Self::CopyStatement(s.clone()), (NO::CopyStatement, &[s], OA::None) => Self::CopyStatement(s.clone()),
(NO::EqualFromEntries, &[s1, s2], OA::None) => { (NO::EqualFromEntries, &[s1, s2], OA::None) => {
Self::EqualFromEntries(s1.clone(), s2.clone()) Self::EqualFromEntries(s1.clone(), s2.clone())
@ -343,6 +344,9 @@ impl Operation {
Self::HashOf(s1.clone(), s2.clone(), s3.clone()) Self::HashOf(s1.clone(), s2.clone(), s3.clone())
} }
(NO::PublicKeyOf, &[s1, s2], OA::None) => Self::PublicKeyOf(s1.clone(), s2.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, NO::ContainerInsertFromEntries,
&[s1, s2, s3, s4], &[s1, s2, s3, s4],
@ -410,6 +414,11 @@ impl Operation {
Ok(sk.0 < *GROUP_ORDER && pk == sk.public_key()) Ok(sk.0 < *GROUP_ORDER && pk == sk.public_key())
} }
pub(crate) fn check_signed_by(msg: &Value, pk: &Value, sig: &Signature) -> Result<bool> {
let pk: PublicKey = pk.typed().try_into()?;
Ok(sig.verify(pk, msg.raw()))
}
/// Checks the given operation against a statement. /// Checks the given operation against a statement.
pub fn check(&self, params: &Params, output_statement: &Statement) -> Result<bool> { pub fn check(&self, params: &Params, output_statement: &Statement) -> Result<bool> {
use Statement::*; use Statement::*;
@ -424,9 +433,6 @@ impl Operation {
}; };
let b = match (self, output_statement) { let b = match (self, output_statement) {
(Self::None, None) => true, (Self::None, None) => true,
(Self::NewEntry, Equal(ValueRef::Key(AnchoredKey { pod_id, .. }), _)) => {
pod_id == &SELF
}
(Self::CopyStatement(s1), s2) => s1 == s2, (Self::CopyStatement(s1), s2) => s1 == s2,
(Self::EqualFromEntries(s1, s2), Equal(v3, v4)) => val(v3, s1)? == val(v4, 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)?, (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::PublicKeyOf(s1, s2), PublicKeyOf(v3, v4)) => {
Self::check_public_key(&val(v3, s1)?, &val(v4, s2)?)? 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), Self::ContainerInsertFromEntries(new_root_s, old_root_s, key_s, val_s, pf),
ContainerInsert(new_root_v, old_root_v, key_v, val_v), 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::None, StatementArg::None) => Ok(()),
(StatementTmplArg::Literal(lhs), StatementArg::Literal(rhs)) if lhs == rhs => Ok(()), (StatementTmplArg::Literal(lhs), StatementArg::Literal(rhs)) if lhs == rhs => Ok(()),
( (
StatementTmplArg::AnchoredKey(pod_id_wc, key_tmpl), StatementTmplArg::AnchoredKey(root_wc, key_tmpl),
StatementArg::Key(AnchoredKey { pod_id, key }), StatementArg::Key(AnchoredKey { root, key }),
) => { ) => {
let pod_id_ok = check_or_set(Value::from(*pod_id), pod_id_wc, wildcard_map); let root_ok = check_or_set(Value::from(*root), root_wc, wildcard_map);
pod_id_ok.and_then(|_| { root_ok.and_then(|_| {
(key_tmpl == key).then_some(()).ok_or( (key_tmpl == key).then_some(()).ok_or(
Error::mismatched_anchored_key_in_statement_tmpl_arg( Error::mismatched_anchored_key_in_statement_tmpl_arg(
pod_id_wc.clone(), root_wc.clone(),
*pod_id, *root,
key_tmpl.clone(), key_tmpl.clone(),
key.clone(), key.clone(),
), ),
@ -742,14 +751,28 @@ impl fmt::Display for Operation {
} }
} }
pub(crate) fn root_key_to_ak(root: &Value, key: &Value) -> Option<AnchoredKey> {
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`. /// Returns the value associated with `output_ref`.
/// If `output_ref` is a concrete value, returns that value. /// 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. /// must be that statement.
pub(crate) fn value_from_op(input_st: &Statement, output_ref: &ValueRef) -> Option<Value> { pub(crate) fn value_from_op(input_st: &Statement, output_ref: &ValueRef) -> Option<Value> {
match (input_st, output_ref) { match (input_st, output_ref) {
(Statement::None, ValueRef::Literal(v)) => Some(v.clone()), (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, _ => None,
} }
} }
@ -761,45 +784,39 @@ mod tests {
use num::BigUint; use num::BigUint;
use crate::{ use crate::{
backends::plonky2::primitives::{ backends::plonky2::{
primitives::{
ec::{curve::GROUP_ORDER, schnorr::SecretKey}, ec::{curve::GROUP_ORDER, schnorr::SecretKey},
merkletree::MerkleTree, merkletree::MerkleTree,
}, },
middleware::{ signer::Signer,
hash_value, AnchoredKey, Error, Key, Operation, Params, PodId, Result, Statement,
}, },
middleware::{hash_value, Error, Operation, Params, Result, Signer as _, Statement, Value},
}; };
#[test] #[test]
fn check_container_ops() -> Result<()> { fn check_container_ops() -> Result<()> {
let params = Params::default(); 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 // Form Merkle tree
let kvs = (0..10) let kvs = (0..10)
.map(|i| (hash_value(&i.into()).into(), i.into())) .map(|i| (hash_value(&i.into()).into(), i.into()))
.collect::<HashMap<_, _>>(); .collect::<HashMap<_, _>>();
let mt = MerkleTree::new(params.max_depth_mt_containers, &kvs)?; 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 // Check existence proofs
kvs.iter().try_for_each(|(k, v)| { 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)?; let (_, pf) = mt.prove(k)?;
// Form op // Form op
let op = Operation::ContainsFromEntries(root_s.clone(), key_s, value_s, pf); let op = Operation::ContainsFromEntries(
// Form output statement Statement::None,
let st = Statement::Contains( Statement::None,
root_ak.clone().into(), Statement::None,
key_ak.clone().into(), pf,
val_ak.clone().into(),
); );
// Form output statement
let st = Statement::Contains(root.into(), (*k).into(), (*v).into());
// Check op against output statement // Check op against output statement
op.check(&params, &st).and_then(|ind| { op.check(&params, &st).and_then(|ind| {
@ -816,11 +833,10 @@ mod tests {
// Check non-existence proofs similarly // Check non-existence proofs similarly
(50..60).try_for_each(|k| { (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 pf = mt.prove_nonexistence(&k.into())?;
let op = Operation::NotContainsFromEntries(root_s.clone(), key_s, pf); let op = Operation::NotContainsFromEntries(Statement::None, Statement::None, pf);
let st = Statement::NotContains(root_ak.clone().into(), key_ak.clone().into()); let st = Statement::NotContains(root.into(), k.into());
op.check(&params, &st).and_then(|ind| { op.check(&params, &st).and_then(|ind| {
if ind { if ind {
@ -838,11 +854,6 @@ mod tests {
#[test] #[test]
fn check_container_update_ops() -> Result<()> { fn check_container_update_ops() -> Result<()> {
let params = Params::default(); 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 // Form Merkle tree
let kvs = (0..10) let kvs = (0..10)
@ -854,23 +865,24 @@ mod tests {
(11..20) (11..20)
.map(|i| (hash_value(&i.into()).into(), i.into())) .map(|i| (hash_value(&i.into()).into(), i.into()))
.try_for_each(|(k, v)| { .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)?; let mtp = mt.insert(&k, &v)?;
// Form op args let new_root = mt.root();
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());
// Form op // Form op
let op = Operation::ContainerInsertFromEntries( 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 // Form output statement
let st = Statement::ContainerInsert( let st = Statement::ContainerInsert(
new_root_ak.clone().into(), new_root.into(),
old_root_ak.clone().into(), old_root.into(),
key_ak.clone().into(), k.into(),
val_ak.clone().into(), v.into(),
); );
// Check op against output statement // Check op against output statement
@ -890,23 +902,24 @@ mod tests {
(11..20) (11..20)
.map(|i| (hash_value(&i.into()).into(), (i + 1).into())) .map(|i| (hash_value(&i.into()).into(), (i + 1).into()))
.try_for_each(|(k, v)| { .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)?; let mtp = mt.update(&k, &v)?;
// Form op args let new_root = mt.root();
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());
// Form op // Form op
let op = Operation::ContainerUpdateFromEntries( 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 // Form output statement
let st = Statement::ContainerUpdate( let st = Statement::ContainerUpdate(
new_root_ak.clone().into(), new_root.into(),
old_root_ak.clone().into(), old_root.into(),
key_ak.clone().into(), k.into(),
val_ak.clone().into(), v.into(),
); );
// Check op against output statement // Check op against output statement
@ -926,20 +939,19 @@ mod tests {
(11..20) (11..20)
.map(|i| hash_value(&i.into()).into()) .map(|i| hash_value(&i.into()).into())
.try_for_each(|k| { .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)?; let mtp = mt.delete(&k)?;
// Form op args let new_root = mt.root();
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());
// Form op // Form op
let op = Operation::ContainerDeleteFromEntries(new_root_s, old_root_s, key_s, mtp); let op = Operation::ContainerDeleteFromEntries(
// Form output statement Statement::None,
let st = Statement::ContainerDelete( Statement::None,
new_root_ak.clone().into(), Statement::None,
old_root_ak.clone().into(), mtp,
key_ak.clone().into(),
); );
// Form output statement
let st = Statement::ContainerDelete(new_root.into(), old_root.into(), k.into());
// Check op against output statement // Check op against output statement
op.check(&params, &st).and_then(|ind| { op.check(&params, &st).and_then(|ind| {
@ -979,20 +991,13 @@ mod tests {
]; ];
let params = Params::default(); 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)| { 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 // Form op
let op = Operation::PublicKeyOf(pk_s.clone(), sk_s.clone()); let op = Operation::PublicKeyOf(Statement::None, Statement::None);
// Form output statement // 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 // Check
op.check(&params, &st).map(|is_good| { op.check(&params, &st).map(|is_good| {
@ -1011,28 +1016,37 @@ mod tests {
let fixed_pk = fixed_sk.public_key(); let fixed_pk = fixed_sk.public_key();
let params = Params::default(); 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 // Bad op and statement with bad first args
let op = Operation::PublicKeyOf(pk_s.clone(), pk_s.clone()); let op = Operation::PublicKeyOf(Statement::None, Statement::None);
let st = Statement::PublicKeyOf(pk_ak.clone().into(), pk_ak.clone().into()); let st = Statement::PublicKeyOf(fixed_pk.into(), fixed_pk.into());
// Check // Check
assert!(op.check(&params, &st).is_err()); assert!(op.check(&params, &st).is_err());
// Bad op and statement with bad second args // Bad op and statement with bad second args
let op = Operation::PublicKeyOf(sk_s.clone(), sk_s.clone()); let op = Operation::PublicKeyOf(Statement::None, Statement::None);
let st = Statement::PublicKeyOf(sk_ak.clone().into(), sk_ak.clone().into()); let st = Statement::PublicKeyOf(fixed_sk.clone().into(), fixed_sk.clone().into());
// Check // Check
assert!(op.check(&params, &st).is_err()); assert!(op.check(&params, &st).is_err());
Ok(()) 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(&params, &st)?;
Ok(())
}
} }

View file

@ -3,14 +3,14 @@ use std::{
sync::{LazyLock, Mutex}, 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( type DeserializeFn = fn(
params: Params, params: Params,
data: serde_json::Value, data: serde_json::Value,
vd_set: VDSet, vd_set: VDSet,
id: PodId, id: Hash,
) -> Result<Box<dyn RecursivePod>, BackendError>; ) -> Result<Box<dyn Pod>, BackendError>;
static DESERIALIZERS: LazyLock<Mutex<HashMap<usize, DeserializeFn>>> = static DESERIALIZERS: LazyLock<Mutex<HashMap<usize, DeserializeFn>>> =
LazyLock::new(backend::deserializers_default); LazyLock::new(backend::deserializers_default);
@ -25,10 +25,10 @@ pub fn register_pod_deserializer(pod_type: usize, deserialize_fn: DeserializeFn)
pub fn deserialize_pod( pub fn deserialize_pod(
pod_type: usize, pod_type: usize,
params: Params, params: Params,
id: PodId, id: Hash,
vd_set: VDSet, vd_set: VDSet,
data: serde_json::Value, data: serde_json::Value,
) -> Result<Box<dyn RecursivePod>, BackendError> { ) -> Result<Box<dyn Pod>, BackendError> {
let deserialize_fn: DeserializeFn = let deserialize_fn: DeserializeFn =
*DESERIALIZERS *DESERIALIZERS
.lock() .lock()
@ -42,14 +42,6 @@ pub fn deserialize_pod(
deserialize_fn(params, data, vd_set, id) deserialize_fn(params, data, vd_set, id)
} }
pub fn deserialize_signed_pod(
pod_type: usize,
id: PodId,
data: serde_json::Value,
) -> Result<Box<dyn Pod>, BackendError> {
backend::deserialize_signed_pod(pod_type, id, data)
}
#[cfg(feature = "backend_plonky2")] #[cfg(feature = "backend_plonky2")]
mod backend { mod backend {
use super::*; use super::*;
@ -57,30 +49,26 @@ mod backend {
emptypod::EmptyPod, emptypod::EmptyPod,
mainpod::MainPod, mainpod::MainPod,
mock::{emptypod::MockEmptyPod, mainpod::MockMainPod}, mock::{emptypod::MockEmptyPod, mainpod::MockMainPod},
signedpod::SignedPod,
}; };
pub(super) fn deserializers_default() -> Mutex<HashMap<usize, DeserializeFn>> { pub(super) fn deserializers_default() -> Mutex<HashMap<usize, DeserializeFn>> {
let mut map: HashMap<usize, DeserializeFn> = HashMap::new(); fn deserialize_data<P: Pod>(
map.insert(PodType::Empty as usize, EmptyPod::deserialize_data); params: Params,
map.insert(PodType::Main as usize, MainPod::deserialize_data); data: serde_json::Value,
map.insert(PodType::MockEmpty as usize, MockEmptyPod::deserialize_data); vd_set: VDSet,
map.insert(PodType::MockMain as usize, MockMainPod::deserialize_data); id: Hash,
Mutex::new(map) ) -> Result<Box<dyn Pod>, BackendError> {
Ok(Box::new(P::deserialize_data(params, data, vd_set, id)?))
} }
pub(super) fn deserialize_signed_pod( let mut map: HashMap<usize, DeserializeFn> = HashMap::new();
pod_type: usize, map.insert(PodType::Empty as usize, deserialize_data::<EmptyPod>);
id: PodId, map.insert(PodType::Main as usize, deserialize_data::<MainPod>);
data: serde_json::Value, map.insert(
) -> Result<Box<dyn Pod>, BackendError> { PodType::MockEmpty as usize,
if pod_type == PodType::Signed as usize { deserialize_data::<MockEmptyPod>,
SignedPod::deserialize(id, data) );
} else { map.insert(PodType::MockMain as usize, deserialize_data::<MockMainPod>);
Err(BackendError::custom(format!( Mutex::new(map)
"unexpected pod_type={} for deserialize_signed_pod",
pod_type
)))
}
} }
} }

View file

@ -9,14 +9,9 @@ use serde::{Deserialize, Serialize};
use strum_macros::FromRepr; use strum_macros::FromRepr;
use crate::middleware::{ 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; pub const STATEMENT_ARG_F_LEN: usize = 8;
#[derive(Clone, Copy, Debug, FromRepr, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Copy, Debug, FromRepr, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
@ -34,9 +29,10 @@ pub enum NativePredicate {
MaxOf = 10, MaxOf = 10,
HashOf = 11, HashOf = 11,
PublicKeyOf = 12, PublicKeyOf = 12,
ContainerInsert = 13, SignedBy = 13,
ContainerUpdate = 14, ContainerInsert = 14,
ContainerDelete = 15, ContainerUpdate = 15,
ContainerDelete = 16,
// Syntactic sugar predicates. These predicates are not supported by the backend. The // Syntactic sugar predicates. These predicates are not supported by the backend. The
// frontend compiler is responsible of translating these predicates into the predicates above. // frontend compiler is responsible of translating these predicates into the predicates above.
@ -73,6 +69,7 @@ impl Display for NativePredicate {
NativePredicate::MaxOf => "MaxOf", NativePredicate::MaxOf => "MaxOf",
NativePredicate::HashOf => "HashOf", NativePredicate::HashOf => "HashOf",
NativePredicate::PublicKeyOf => "PublicKeyOf", NativePredicate::PublicKeyOf => "PublicKeyOf",
NativePredicate::SignedBy => "SignedBy",
NativePredicate::ContainerInsert => "ContainerInsert", NativePredicate::ContainerInsert => "ContainerInsert",
NativePredicate::ContainerUpdate => "ContainerUpdate", NativePredicate::ContainerUpdate => "ContainerUpdate",
NativePredicate::ContainerDelete => "ContainerDelete", 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)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type", content = "value")] #[serde(tag = "type", content = "value")]
pub enum Predicate { pub enum Predicate {
Native(NativePredicate), Native(NativePredicate),
BatchSelf(usize), BatchSelf(usize),
Custom(CustomPredicateRef), Custom(CustomPredicateRef),
Intro(IntroPredicateRef),
} }
impl From<NativePredicate> for Predicate { impl From<NativePredicate> for Predicate {
@ -117,6 +122,7 @@ pub enum PredicatePrefix {
Native = 1, Native = 1,
BatchSelf = 2, BatchSelf = 2,
Custom = 3, Custom = 3,
Intro = 4,
} }
impl From<PredicatePrefix> for F { impl From<PredicatePrefix> for F {
@ -133,6 +139,8 @@ impl ToFields for Predicate {
// CustomPredicateRef(pb, i) as // CustomPredicateRef(pb, i) as
// (3, [hash of pb], i) -- pb hashes to 4 field elements // (3, [hash of pb], i) -- pb hashes to 4 field elements
// -- i: usize // -- i: usize
// IntroPredicateRef(vd_hash) as
// (4, [vd_hash], 0)
// in every case: pad to (hash_size + 2) field elements // in every case: pad to (hash_size + 2) field elements
let mut fields: Vec<F> = match self { let mut fields: Vec<F> = match self {
@ -148,6 +156,11 @@ impl ToFields for Predicate {
.chain(iter::once(F::from_canonical_usize(*index))) .chain(iter::once(F::from_canonical_usize(*index)))
.collect() .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.resize_with(Params::predicate_size(), || F::from_canonical_u64(0));
fields fields
@ -172,6 +185,7 @@ impl fmt::Display for Predicate {
write!(f, "{}", batch.predicates()[*index].name) write!(f, "{}", batch.predicates()[*index].name)
} }
} }
Self::Intro(IntroPredicateRef { name, .. }) => write!(f, "{}", name),
} }
} }
} }
@ -196,6 +210,7 @@ pub enum Statement {
MaxOf(ValueRef, ValueRef, ValueRef), MaxOf(ValueRef, ValueRef, ValueRef),
HashOf(ValueRef, ValueRef, ValueRef), HashOf(ValueRef, ValueRef, ValueRef),
PublicKeyOf(ValueRef, ValueRef), PublicKeyOf(ValueRef, ValueRef),
SignedBy(ValueRef, ValueRef),
ContainerInsert( ContainerInsert(
/* new_root */ ValueRef, /* new_root */ ValueRef,
/* old_root */ ValueRef, /* old_root */ ValueRef,
@ -214,6 +229,7 @@ pub enum Statement {
/* key */ ValueRef, /* key */ ValueRef,
), ),
Custom(CustomPredicateRef, Vec<Value>), Custom(CustomPredicateRef, Vec<Value>),
Intro(IntroPredicateRef, Vec<Value>),
} }
macro_rules! statement_constructor { macro_rules! statement_constructor {
@ -258,6 +274,7 @@ impl Statement {
statement_constructor!(max_of, MaxOf, 3); statement_constructor!(max_of, MaxOf, 3);
statement_constructor!(hash_of, HashOf, 3); statement_constructor!(hash_of, HashOf, 3);
statement_constructor!(public_key_of, PublicKeyOf, 2); statement_constructor!(public_key_of, PublicKeyOf, 2);
statement_constructor!(signed_by, SignedBy, 2);
statement_constructor!(insert, ContainerInsert, 4); statement_constructor!(insert, ContainerInsert, 4);
statement_constructor!(update, ContainerUpdate, 4); statement_constructor!(update, ContainerUpdate, 4);
statement_constructor!(delete, ContainerDelete, 3); statement_constructor!(delete, ContainerDelete, 3);
@ -276,10 +293,12 @@ impl Statement {
Self::MaxOf(_, _, _) => Native(NativePredicate::MaxOf), Self::MaxOf(_, _, _) => Native(NativePredicate::MaxOf),
Self::HashOf(_, _, _) => Native(NativePredicate::HashOf), Self::HashOf(_, _, _) => Native(NativePredicate::HashOf),
Self::PublicKeyOf(_, _) => Native(NativePredicate::PublicKeyOf), Self::PublicKeyOf(_, _) => Native(NativePredicate::PublicKeyOf),
Self::SignedBy(_, _) => Native(NativePredicate::SignedBy),
Self::ContainerInsert(_, _, _, _) => Native(NativePredicate::ContainerInsert), Self::ContainerInsert(_, _, _, _) => Native(NativePredicate::ContainerInsert),
Self::ContainerUpdate(_, _, _, _) => Native(NativePredicate::ContainerUpdate), Self::ContainerUpdate(_, _, _, _) => Native(NativePredicate::ContainerUpdate),
Self::ContainerDelete(_, _, _) => Native(NativePredicate::ContainerDelete), Self::ContainerDelete(_, _, _) => Native(NativePredicate::ContainerDelete),
Self::Custom(cpr, _) => Custom(cpr.clone()), Self::Custom(cpr, _) => Custom(cpr.clone()),
Self::Intro(ir, _) => Intro(ir.clone()),
} }
} }
pub fn args(&self) -> Vec<StatementArg> { pub fn args(&self) -> Vec<StatementArg> {
@ -297,6 +316,7 @@ impl Statement {
Self::MaxOf(ak1, ak2, ak3) => vec![ak1.into(), ak2.into(), ak3.into()], 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::HashOf(ak1, ak2, ak3) => vec![ak1.into(), ak2.into(), ak3.into()],
Self::PublicKeyOf(ak1, ak2) => vec![ak1.into(), ak2.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) => { Self::ContainerInsert(ak1, ak2, ak3, ak4) => {
vec![ak1.into(), ak2.into(), ak3.into(), ak4.into()] 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::ContainerDelete(ak1, ak2, ak3) => vec![ak1.into(), ak2.into(), ak3.into()],
Self::Custom(_, args) => Vec::from_iter(args.into_iter().map(Literal)), 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]) => { (Native(NativePredicate::PublicKeyOf), &[a1, a2]) => {
Self::PublicKeyOf(a1.try_into()?, a2.try_into()?) 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( (Native(NativePredicate::ContainerInsert), &[a1, a2, a3, a4]) => Self::ContainerInsert(
a1.try_into()?, a1.try_into()?,
a2.try_into()?, a2.try_into()?,
@ -380,6 +404,16 @@ impl Statement {
.collect(); .collect();
Self::Custom(cpr, v_args?) Self::Custom(cpr, v_args?)
} }
(Intro(ir), _) => {
let v_args: Result<Vec<Value>> = 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) Ok(st)
} }
@ -453,7 +487,7 @@ impl ToFields for StatementArg {
/// Encoding: /// Encoding:
/// - None => [0, 0, 0, 0, 0, 0, 0, 0] /// - None => [0, 0, 0, 0, 0, 0, 0, 0]
/// - Literal(v) => [[v], 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] /// - WildcardLiteral(v) => [[v], 0, 0, 0, 0]
fn to_fields(&self, params: &Params) -> Vec<F> { fn to_fields(&self, params: &Params) -> Vec<F> {
// NOTE for @ax0: I removed the old comment because may `to_fields` implementations do // 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)) .chain(iter::repeat(F::ZERO).take(STATEMENT_ARG_F_LEN - VALUE_SIZE))
.collect(), .collect(),
StatementArg::Key(ak) => { 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.extend(ak.key.to_fields(params));
fields fields
} }
@ -528,17 +562,3 @@ where
Self::Literal(value.into()) 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);
}
}