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(remote_varname, "ssn")
ValueOf(gov_id_pod_id, 0x4030)
ValueFromPodKey(local_varname, gov_id_pod_id, remote_varname)
ValueOf(gov_id_root, 0x4030)
ValueFromPodKey(local_varname, gov_id_root, remote_varname)
```
## Summary of additional statements in this spec

View file

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

View file

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

View file

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

View file

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

View file

@ -1,21 +1,15 @@
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>;
#[derive(thiserror::Error, Debug)]
pub enum InnerError {
#[error("id does not match, expected {0}, found {1}")]
IdNotEqual(PodId, PodId),
#[error("type does not match, expected {0}, found {1}")]
TypeNotEqual(PodType, Value),
#[error("signer public key does not match, expected {0}, found {1}")]
SignerNotEqual(Value, Value),
#[error("Statements hash does not match, expected {0}, found {1}")]
StsHashNotEqual(Hash, Hash),
// POD related
#[error("invalid POD ID")]
PodIdInvalid,
#[error("verification failed: POD does not have type statement")]
NotTypeStatement,
#[error("repeated ValueOf")]
@ -85,16 +79,7 @@ impl Error {
pub fn not_type_statement() -> Self {
new!(NotTypeStatement)
}
pub fn pod_id_invalid() -> Self {
new!(PodIdInvalid)
}
pub fn id_not_equal(expected: PodId, found: PodId) -> Self {
new!(IdNotEqual(expected, found))
}
pub fn type_not_equal(expected: PodType, found: Value) -> Self {
new!(TypeNotEqual(expected, found))
}
pub(crate) fn signer_not_equal(expected: Value, found: Value) -> Self {
new!(SignerNotEqual(expected, found))
pub fn statements_hash_not_equal(expected: Hash, found: Hash) -> Self {
new!(StsHashNotEqual(expected, found))
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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::{
frontend::{PodRequest, Result},
lang::parse,
middleware::{CustomPredicateBatch, Params, PodType, Value, KEY_SIGNER, KEY_TYPE},
middleware::{CustomPredicateBatch, Params},
};
macro_rules! render {
($tmpl: expr, $($arg:tt)*) => {{
format!(
$tmpl,
KEY_TYPE = Value::from(KEY_TYPE),
KEY_SIGNER = Value::from(KEY_SIGNER),
$($arg)*
)
}};
}
/// Instantiates an ETHDos batch
pub fn eth_dos_batch(params: &Params) -> Result<Arc<CustomPredicateBatch>> {
let input = render!(
r#"
eth_friend(src, dst, private: attestation_pod) = AND(
Equal(?attestation_pod[{KEY_TYPE}], {pod_type})
Equal(?attestation_pod[{KEY_SIGNER}], ?src)
Equal(?attestation_pod["attestation"], ?dst)
let input = r#"
eth_friend(src, dst, private: attestation) = AND(
SignedBy(?attestation, ?src)
Contains(?attestation, "attestation", ?dst)
)
eth_dos_base(src, dst, distance) = AND(
@ -44,10 +31,8 @@ pub fn eth_dos_batch(params: &Params) -> Result<Arc<CustomPredicateBatch>> {
eth_dos_base(?src, ?dst, ?distance)
eth_dos_ind(?src, ?dst, ?distance)
)
"#,
pod_type = Value::from(PodType::Signed),
);
let batch = parse(&input, params, &[]).expect("lang parse").custom_batch;
"#;
let batch = parse(input, params, &[]).expect("lang parse").custom_batch;
println!("a.0. {}", batch.predicates[0]);
println!("a.1. {}", batch.predicates[1]);
println!("a.2. {}", batch.predicates[2]);

View file

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

View file

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

View file

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

View file

@ -1,10 +1,10 @@
use std::fmt;
use crate::{
frontend::{MainPod, SignedPod},
frontend::SignedDict,
middleware::{
AnchoredKey, CustomPredicateRef, NativeOperation, OperationAux, OperationType, Statement,
TypedValue, Value, ValueRef,
containers::Dictionary, root_key_to_ak, CustomPredicateRef, NativeOperation, OperationAux,
OperationType, Signature, Statement, TypedValue, Value, ValueRef,
},
};
@ -16,12 +16,12 @@ pub enum OperationArg {
}
impl OperationArg {
/// Extracts the value underlying literal and `ValueOf` statement
/// Extracts the value underlying literal and `Contains` statement
/// operation args.
pub(crate) fn value(&self) -> Option<&Value> {
match self {
Self::Literal(v) => Some(v),
Self::Statement(Statement::Equal(_, ValueRef::Literal(v))) => Some(v),
Self::Statement(Statement::Contains(_, _, ValueRef::Literal(v))) => Some(v),
_ => None,
}
}
@ -29,7 +29,11 @@ impl OperationArg {
pub(crate) fn value_and_ref(&self) -> Option<(ValueRef, &Value)> {
match self {
Self::Literal(v) => Some((ValueRef::Literal(v.clone()), v)),
Self::Statement(Statement::Equal(k, ValueRef::Literal(v))) => Some((k.clone(), v)),
Self::Statement(Statement::Contains(
ValueRef::Literal(root),
ValueRef::Literal(key),
ValueRef::Literal(v),
)) => root_key_to_ak(root, key).map(|ak| (ValueRef::Key(ak), v)),
_ => None,
}
}
@ -64,27 +68,21 @@ impl From<&Value> for OperationArg {
}
}
impl From<(&SignedPod, &str)> for OperationArg {
fn from((pod, key): (&SignedPod, &str)) -> Self {
// TODO: TryFrom.
let value = pod
.kvs()
.get(&key.into())
.cloned()
.unwrap_or_else(|| panic!("Key {} is not present in POD: {}", key, pod));
Self::Statement(Statement::Equal(
AnchoredKey::from((pod.id(), key)).into(),
impl From<(&Dictionary, &str)> for OperationArg {
fn from((dict, key): (&Dictionary, &str)) -> Self {
// TODO: Use TryFrom
let value = dict.get(&key.into()).cloned().unwrap();
Self::Statement(Statement::Contains(
dict.clone().into(),
key.into(),
value.into(),
))
}
}
impl From<(&MainPod, &str)> for OperationArg {
fn from((pod, key): (&MainPod, &str)) -> Self {
// TODO: TryFrom.
let value = pod
.get(key)
.unwrap_or_else(|| panic!("Key {} is not present in POD: {}", key, pod));
Self::Statement(Statement::equal(AnchoredKey::from((pod.id(), key)), value))
impl From<(&SignedDict, &str)> for OperationArg {
fn from((signed_dict, key): (&SignedDict, &str)) -> Self {
OperationArg::from((&signed_dict.dict, key))
}
}
@ -186,13 +184,6 @@ macro_rules! op_impl_st {
}
impl Operation {
pub fn new_entry(a1: impl Into<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!(ne, NotEqualFromEntries, 2);
op_impl_oa!(gt_eq, GtEqFromEntries, 2);
@ -229,4 +220,22 @@ impl Operation {
op_impl_oa!(set_insert, SetInsertFromEntries, 3);
op_impl_oa!(set_delete, SetDeleteFromEntries, 3);
op_impl_oa!(array_update, ArrayUpdateFromEntries, 4);
pub fn signed_by(
msg: impl Into<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
let pod_id_value = Value::from(stmt_key.pod_id);
self.try_bind_wildcard(&wildcard.name, pod_id_value, current_bindings, new_bindings)
let root_value = Value::from(stmt_key.root);
self.try_bind_wildcard(&wildcard.name, root_value, current_bindings, new_bindings)
}
// Other combinations don't match
@ -176,12 +176,14 @@ impl Display for PodRequest {
mod tests {
use crate::{
backends::plonky2::{
mock::mainpod::MockProver, primitives::ec::schnorr::SecretKey, signedpod::Signer,
mock::mainpod::MockProver, primitives::ec::schnorr::SecretKey, signer::Signer,
},
examples::{
zu_kyc_pod_builder, zu_kyc_pod_request, zu_kyc_sign_dict_builders, MOCK_VD_SET,
},
examples::{zu_kyc_pod_builder, zu_kyc_pod_request, zu_kyc_sign_pod_builders, MOCK_VD_SET},
frontend::{MainPodBuilder, Operation},
lang::parse,
middleware::Params,
middleware::{Params, Value},
};
#[test]
@ -189,7 +191,7 @@ mod tests {
let params = Params::default();
let vd_set = &*MOCK_VD_SET;
let (gov_id, pay_stub) = zu_kyc_sign_pod_builders(&params);
let (gov_id, pay_stub) = zu_kyc_sign_dict_builders(&params);
let gov_id = gov_id.sign(&Signer(SecretKey(1u32.into()))).unwrap();
let pay_stub = pay_stub.sign(&Signer(SecretKey(2u32.into()))).unwrap();
let builder = zu_kyc_pod_builder(&Params::default(), vd_set, &gov_id, &pay_stub).unwrap();
@ -198,8 +200,8 @@ mod tests {
// This request matches the POD
let request = zu_kyc_pod_request(
gov_id.get("_signer").unwrap(),
pay_stub.get("_signer").unwrap(),
&Value::from(gov_id.public_key),
&Value::from(pay_stub.public_key),
)
.unwrap();
assert!(request.exact_match_pod(&*kyc.pod).is_ok());

View file

@ -1,73 +1,30 @@
use std::collections::HashMap;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use super::Error;
use crate::{
frontend::{MainPod, SignedPod},
middleware::{
deserialize_pod, deserialize_signed_pod, Key, Params, PodId, Statement, VDSet, Value,
},
frontend::MainPod,
middleware::{deserialize_pod, Hash, Params, Statement, VDSet},
};
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
#[schemars(rename = "SignedPod")]
pub struct SerializedSignedPod {
pod_type: (usize, String),
id: PodId,
entries: HashMap<Key, Value>,
data: serde_json::Value,
}
impl SerializedSignedPod {
pub fn id(&self) -> PodId {
self.id
}
}
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
#[schemars(rename = "MainPod")]
pub struct SerializedMainPod {
params: Params,
pod_type: (usize, String),
id: PodId,
id: Hash,
vd_set: VDSet,
public_statements: Vec<Statement>,
data: serde_json::Value,
}
impl SerializedMainPod {
pub fn id(&self) -> PodId {
pub fn id(&self) -> Hash {
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 {
fn from(pod: MainPod) -> Self {
let (pod_type, pod_type_name_str) = pod.pod.pod_type();
@ -116,17 +73,17 @@ mod tests {
mainpod::{rec_main_pod_circuit_data, Prover},
mock::mainpod::MockProver,
primitives::ec::schnorr::SecretKey,
signedpod::Signer,
signer::Signer,
},
examples::{
attest_eth_friend, zu_kyc_pod_builder, zu_kyc_sign_pod_builders, EthDosHelper,
attest_eth_friend, zu_kyc_pod_builder, zu_kyc_sign_dict_builders, EthDosHelper,
MOCK_VD_SET,
},
frontend::{Result, SignedPodBuilder},
frontend::{Result, SignedDict, SignedDictBuilder},
middleware::{
self,
containers::{Array, Dictionary, Set},
Params, TypedValue, DEFAULT_VD_LIST,
Params, Signer as _, TypedValue, DEFAULT_VD_LIST,
},
};
@ -182,9 +139,9 @@ mod tests {
}
}
fn signed_pod_builder() -> SignedPodBuilder {
fn signed_dict_builder() -> SignedDictBuilder {
let params = &Params::default();
let mut builder = SignedPodBuilder::new(params);
let mut builder = SignedDictBuilder::new(params);
builder.insert("name", "test");
builder.insert("age", 30);
builder.insert("very_large_int", 1152921504606846976);
@ -228,46 +185,29 @@ mod tests {
}
#[test]
fn test_signed_pod_serialization() {
let builder = signed_pod_builder();
fn test_signed_dict_serialization() {
let builder = signed_dict_builder();
let signer = Signer(SecretKey(1u32.into()));
let pod = builder.sign(&signer).unwrap();
let signed_dict = builder.sign(&signer).unwrap();
let serialized = serde_json::to_string_pretty(&pod).unwrap();
let serialized = serde_json::to_string_pretty(&signed_dict).unwrap();
println!("serialized: {}", serialized);
let deserialized: SignedPod = serde_json::from_str(&serialized).unwrap();
let deserialized: SignedDict = serde_json::from_str(&serialized).unwrap();
println!(
"deserialized: {}",
serde_json::to_string_pretty(&deserialized).unwrap()
);
assert_eq!(pod.kvs, deserialized.kvs);
assert_eq!(pod.verify().is_ok(), deserialized.verify().is_ok());
assert_eq!(pod.id(), deserialized.id())
}
#[test]
fn test_mock_signed_pod_serialization() {
let builder = signed_pod_builder();
let signer = Signer(SecretKey(1u32.into()));
let pod = builder.sign(&signer).unwrap();
let serialized = serde_json::to_string_pretty(&pod).unwrap();
println!("serialized: {}", serialized);
let deserialized: SignedPod = serde_json::from_str(&serialized).unwrap();
println!(
"deserialized: {}",
serde_json::to_string_pretty(&deserialized).unwrap()
);
assert_eq!(pod.kvs, deserialized.kvs);
assert_eq!(pod.verify().is_ok(), deserialized.verify().is_ok());
assert_eq!(pod.id(), deserialized.id())
assert_eq!(signed_dict.dict.kvs(), deserialized.dict.kvs());
assert_eq!(signed_dict.public_key, deserialized.public_key);
assert_eq!(signed_dict.signature, deserialized.signature);
assert_eq!(signed_dict.verify().is_ok(), deserialized.verify().is_ok());
}
fn build_mock_zukyc_pod() -> Result<MainPod> {
let params = middleware::Params::default();
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_pod = gov_id_builder.sign(&signer).unwrap();
let signer = Signer(SecretKey(2u32.into()));
@ -283,18 +223,19 @@ mod tests {
let params = middleware::Params {
// Currently the circuit uses random access that only supports vectors of length 64.
// With max_input_main_pods=3 we need random access to a vector of length 73.
max_input_recursive_pods: 1,
max_input_pods: 1,
..Default::default()
};
let mut vds = DEFAULT_VD_LIST.clone();
vds.push(rec_main_pod_circuit_data(&params).1.verifier_only.clone());
let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap();
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(&params);
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_dict_builders(&params);
let signer = Signer(SecretKey(1u32.into()));
let gov_id_pod = gov_id_builder.sign(&signer)?;
let signer = Signer(SecretKey(2u32.into()));
let pay_stub_pod = pay_stub_builder.sign(&signer)?;
let _signer = Signer(SecretKey(3u32.into()));
let kyc_builder = zu_kyc_pod_builder(&params, &vd_set, &gov_id_pod, &pay_stub_pod)?;
let prover = Prover {};
@ -311,7 +252,10 @@ mod tests {
let deserialized: MainPod = serde_json::from_str(&serialized).unwrap();
assert_eq!(kyc_pod.public_statements, deserialized.public_statements);
assert_eq!(kyc_pod.pod.id(), deserialized.pod.id());
assert_eq!(
kyc_pod.pod.statements_hash(),
deserialized.pod.statements_hash()
);
assert_eq!(kyc_pod.pod.verify()?, deserialized.pod.verify()?);
Ok(())
@ -324,7 +268,10 @@ mod tests {
let deserialized: MainPod = serde_json::from_str(&serialized).unwrap();
assert_eq!(kyc_pod.public_statements, deserialized.public_statements);
assert_eq!(kyc_pod.pod.id(), deserialized.pod.id());
assert_eq!(
kyc_pod.pod.statements_hash(),
deserialized.pod.statements_hash()
);
assert_eq!(kyc_pod.pod.verify()?, deserialized.pod.verify()?);
Ok(())
@ -348,7 +295,7 @@ mod tests {
let alice_attestation = attest_eth_friend(&params, &alice, bob.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 dist_1 = helper.dist_1(&alice_attestation)?.prove(&prover)?;
let dist_2 = helper
@ -360,28 +307,28 @@ mod tests {
#[test]
// This tests that we can generate JSON Schemas for the MainPod and
// SignedPod types, and that we can validate Signed and Main Pods
// SignedDict types, and that we can validate Signed and Main Pods
// against the schemas. Since both Mock and Plonky2 PODs have the same
// public interface, we can assume that the schema works for both.
fn test_schema() {
let mainpod_schema = schema_for!(SerializedMainPod);
let signedpod_schema = schema_for!(SerializedSignedPod);
let signeddict_schema = schema_for!(SignedDict);
let kyc_pod = build_mock_zukyc_pod().unwrap();
let signed_pod = signed_pod_builder()
let signed_dict = signed_dict_builder()
.sign(&Signer(SecretKey(1u32.into())))
.unwrap();
let ethdos_pod = build_ethdos_pod().unwrap();
let mainpod_schema_value = serde_json::to_value(&mainpod_schema).unwrap();
let signedpod_schema_value = serde_json::to_value(&signedpod_schema).unwrap();
let signed_dict_schema_value = serde_json::to_value(&signeddict_schema).unwrap();
let kyc_pod_value = serde_json::to_value(&kyc_pod).unwrap();
let mainpod_valid = jsonschema::validate(&mainpod_schema_value, &kyc_pod_value);
assert!(mainpod_valid.is_ok(), "{:#?}", mainpod_valid);
let signed_pod_value = serde_json::to_value(&signed_pod).unwrap();
let signedpod_valid = jsonschema::validate(&signedpod_schema_value, &signed_pod_value);
assert!(signedpod_valid.is_ok(), "{:#?}", signedpod_valid);
let signed_dict_value = serde_json::to_value(&signed_dict).unwrap();
let signed_dict_valid = jsonschema::validate(&signed_dict_schema_value, &signed_dict_value);
assert!(signed_dict_valid.is_ok(), "{:#?}", signed_dict_valid);
let ethdos_pod_value = serde_json::to_value(&ethdos_pod).unwrap();
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:" }
self_keyword = @{ "SELF" }
// Define wildcard names (start with '?')
wildcard = @{ "?" ~ identifier }
@ -66,10 +64,8 @@ literal_value = {
literal_array |
literal_bool |
literal_raw |
literal_pod_id |
literal_string |
literal_int |
self_keyword
literal_int
}
// Primitive literal types
@ -81,7 +77,6 @@ literal_bool = @{ "true" | "false" }
hash_hex = @{ "0x" ~ (ASCII_HEX_DIGIT ~ ASCII_HEX_DIGIT){32} }
literal_raw = { "Raw" ~ "(" ~ hash_hex ~ ")" }
literal_pod_id = { hash_hex }
// String literal parsing based on https://pest.rs/book/examples/json.html
literal_string = ${ "\"" ~ inner ~ "\"" } // Compound atomic string rule
@ -114,7 +109,6 @@ test_wildcard = { SOI ~ wildcard ~ EOI }
test_literal_int = { SOI ~ literal_int ~ EOI }
test_hash_hex = { SOI ~ hash_hex ~ EOI }
test_literal_raw = { SOI ~ literal_raw ~ EOI }
test_literal_pod_id = { SOI ~ literal_pod_id ~ EOI }
test_literal_value = { SOI ~ literal_value ~ EOI }
test_statement = { SOI ~ statement ~ EOI }
test_custom_predicate_def = { SOI ~ custom_predicate_def ~ EOI }

View file

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

View file

@ -117,7 +117,7 @@ mod tests {
// Use anchored rule for failure cases
assert_fails(
Rule::test_literal_raw,
"0x0000000000000000000000000000000000000000000000000000000000000000)",
"0x0000000000000000000000000000000000000000000000000000000000000000",
); // Missing Raw() wrapper
assert_fails(Rule::test_literal_raw, "Raw(0xabc)"); // Fails (string is too short)
assert_fails(Rule::test_literal_raw, "Raw(0x)"); // Fails (needs at least one pair)
@ -126,22 +126,6 @@ mod tests {
&format!("Raw(0x{})", "a".repeat(66)),
); // Fails (string is too long)
// PodId (essentially identical to Raw but without the wrapper)
assert_parses(
Rule::literal_pod_id,
"0x0000000000000000000000000000000000000000000000000000000000000000",
);
assert_parses(
Rule::literal_pod_id,
"0xabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
);
let long_valid_pod_id = format!("0x{}", "a".repeat(64));
assert_parses(Rule::literal_pod_id, &long_valid_pod_id);
assert_fails(Rule::test_literal_pod_id, "0xabc"); // Fails (string is too short)
assert_fails(Rule::test_literal_pod_id, "0x"); // Fails (needs at least one pair)
assert_fails(Rule::test_literal_pod_id, &format!("0x{}", "a".repeat(66))); // Fails (string is too long)
// String
assert_parses(Rule::literal_string, "\"hello\"");
assert_parses(Rule::literal_string, "\"escaped \\\" quote\"");
@ -163,7 +147,7 @@ mod tests {
assert_parses(Rule::literal_set, "#[1, 2, 3]");
assert_parses(
Rule::literal_set,
"#[ \"a\", 0x0000000000000000000000000000000000000000000000000000000000000000 ]",
"#[ \"a\", Raw(0x0000000000000000000000000000000000000000000000000000000000000000) ]",
);
// Dict
@ -172,7 +156,7 @@ mod tests {
assert_parses(Rule::literal_dict, "{ \"nested\": { \"key\": 1 } }");
assert_parses(
Rule::literal_dict,
"{ \"raw_val\": 0x0000000000000000000000000000000000000000000000000000000000000000 } ",
"{ \"raw_val\": Raw(0x0000000000000000000000000000000000000000000000000000000000000000) } ",
);
assert_fails(Rule::literal_dict, "{ name: \"Alice\" }"); // Key must be string literal with quotes
}

View file

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

View file

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

View file

@ -27,6 +27,18 @@ pub struct Dictionary {
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 {
/// max_depth determines the depth of the underlying MerkleTree, allowing to
/// store 2^max_depth elements in the Dictionary

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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