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:
parent
122f9c3cac
commit
0e2f7b756e
39 changed files with 2127 additions and 3064 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(¶ms);
|
||||
let mut builder = SignedDictBuilder::new(¶ms);
|
||||
builder.insert("player", "Alice");
|
||||
builder.insert("level", 1);
|
||||
builder.insert("points", 3512);
|
||||
|
|
@ -58,7 +58,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
pod_points_lvl_1.verify()?;
|
||||
println!("# pod_points_lvl_1:\n{}", pod_points_lvl_1);
|
||||
|
||||
let mut builder = SignedPodBuilder::new(¶ms);
|
||||
let mut builder = SignedDictBuilder::new(¶ms);
|
||||
builder.insert("player", "Alice");
|
||||
builder.insert("level", 2);
|
||||
builder.insert("points", 5771);
|
||||
|
|
@ -71,12 +71,11 @@ fn main() -> Result<(), Box<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, ¶ms, &[])?.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(¶ms, vd_set);
|
||||
builder.add_signed_pod(&pod_points_lvl_1);
|
||||
let st_type = builder.priv_op(Operation::eq(
|
||||
(&pod_points_lvl_1, KEY_TYPE),
|
||||
PodType::Signed,
|
||||
let st_signed_by = builder.priv_op(Operation::dict_signed_by(&pod_points_lvl_1))?;
|
||||
let st_player = builder.priv_op(Operation::dict_contains(
|
||||
pod_points_lvl_1.dict.clone(),
|
||||
"player",
|
||||
"Alice",
|
||||
))?;
|
||||
let st_level = builder.priv_op(Operation::dict_contains(
|
||||
pod_points_lvl_1.dict.clone(),
|
||||
"level",
|
||||
1,
|
||||
))?;
|
||||
let st_points = builder.priv_op(Operation::dict_contains(
|
||||
pod_points_lvl_1.dict.clone(),
|
||||
"points",
|
||||
3512,
|
||||
))?;
|
||||
let st_signer = builder.priv_op(Operation::eq((&pod_points_lvl_1, KEY_SIGNER), game_pk))?;
|
||||
let st_player = builder.priv_op(Operation::eq((&pod_points_lvl_1, "player"), "Alice"))?;
|
||||
let st_level = builder.priv_op(Operation::eq((&pod_points_lvl_1, "level"), 1))?;
|
||||
let st_points = builder.priv_op(Operation::eq((&pod_points_lvl_1, "points"), 3512))?;
|
||||
let st_points_lvl_1 = builder.pub_op(Operation::custom(
|
||||
points_pred.clone(),
|
||||
[st_type, st_signer, st_player, st_level, st_points],
|
||||
[st_signed_by, st_player, st_level, st_points],
|
||||
))?;
|
||||
|
||||
let pod_alice_lvl_1_points = builder.prove(prover).unwrap();
|
||||
println!("# pod_alice_lvl_1_points\n:{}", pod_alice_lvl_1_points);
|
||||
pod_alice_lvl_1_points.pod.verify().unwrap();
|
||||
|
||||
// Build a pod to prove the statement `points("Alice", 2, 5771)`
|
||||
let mut builder = MainPodBuilder::new(¶ms, vd_set);
|
||||
builder.add_signed_pod(&pod_points_lvl_2);
|
||||
let st_type = builder.priv_op(Operation::eq(
|
||||
(&pod_points_lvl_2, KEY_TYPE),
|
||||
PodType::Signed,
|
||||
let st_signed_by = builder.priv_op(Operation::dict_signed_by(&pod_points_lvl_2))?;
|
||||
let st_player = builder.priv_op(Operation::dict_contains(
|
||||
pod_points_lvl_2.dict.clone(),
|
||||
"player",
|
||||
"Alice",
|
||||
))?;
|
||||
let st_signer = builder.priv_op(Operation::eq((&pod_points_lvl_2, KEY_SIGNER), game_pk))?;
|
||||
let st_player = builder.priv_op(Operation::eq((&pod_points_lvl_2, "player"), "Alice"))?;
|
||||
let st_level = builder.priv_op(Operation::eq((&pod_points_lvl_2, "level"), 2))?;
|
||||
let st_points = builder.priv_op(Operation::eq((&pod_points_lvl_2, "points"), 5771))?;
|
||||
let st_level = builder.priv_op(Operation::dict_contains(
|
||||
pod_points_lvl_2.dict.clone(),
|
||||
"level",
|
||||
2,
|
||||
))?;
|
||||
let st_points = builder.priv_op(Operation::dict_contains(
|
||||
pod_points_lvl_2.dict.clone(),
|
||||
"points",
|
||||
5771,
|
||||
))?;
|
||||
|
||||
let st_points_lvl_2 = builder.pub_op(Operation::custom(
|
||||
points_pred,
|
||||
[st_type, st_signer, st_player, st_level, st_points],
|
||||
[st_signed_by, st_player, st_level, st_points],
|
||||
))?;
|
||||
let pod_alice_lvl_2_points = builder.prove(prover).unwrap();
|
||||
println!("# pod_alice_lvl_2_points\n:{}", pod_alice_lvl_2_points);
|
||||
|
|
@ -136,8 +148,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
|
||||
// Build a pod to prove the statement `over_9000("Alice")`
|
||||
let mut builder = MainPodBuilder::new(¶ms, vd_set);
|
||||
builder.add_recursive_pod(pod_alice_lvl_1_points);
|
||||
builder.add_recursive_pod(pod_alice_lvl_2_points);
|
||||
builder.add_pod(pod_alice_lvl_1_points);
|
||||
builder.add_pod(pod_alice_lvl_2_points);
|
||||
let st_points_total = builder.priv_op(Operation::sum_of(3512 + 5771, 3512, 5771))?;
|
||||
let st_gt_9000 = builder.priv_op(Operation::gt(3512 + 5771, 9000))?;
|
||||
let _st_over_9000 = builder.pub_op(Operation::custom(
|
||||
|
|
|
|||
|
|
@ -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(¶ms);
|
||||
// Build the signed dict
|
||||
let mut builder = SignedDictBuilder::new(¶ms);
|
||||
// The values can be String, i64, bool, Array, Set, Dictionary, ...
|
||||
builder.insert("name", "Alice");
|
||||
builder.insert("lucky_number", 42);
|
||||
|
|
@ -35,11 +35,11 @@ fn main() -> Result<(), Box<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(())
|
||||
}
|
||||
|
|
@ -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(¶ms)
|
||||
// NOTE: We only include the recursive MainPod with default parameters here. We don't need to
|
||||
// include the verifying key of the EmptyPod because it's an Introduction pod and its verifying
|
||||
// key appears in its statement in a self-describing way.
|
||||
vec![cache_get_rec_main_pod_verifier_circuit_data(¶ms)
|
||||
.verifier_only
|
||||
.clone(),
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -3,5 +3,4 @@ pub mod hash;
|
|||
pub mod mainpod;
|
||||
pub mod metrics;
|
||||
pub mod mux_table;
|
||||
pub mod signedpod;
|
||||
pub mod utils;
|
||||
|
|
|
|||
|
|
@ -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(¶ms);
|
||||
pod.insert("idNumber", "4242424242");
|
||||
pod.insert("dateOfBirth", 1169909384);
|
||||
pod.insert("socialSecurityNumber", "G2121210");
|
||||
let sk = SecretKey::new_rand();
|
||||
let signer = Signer(sk);
|
||||
let pod = pod.sign(&signer).unwrap();
|
||||
let signed_pod = (pod.pod as Box<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(¶ms, &mut builder);
|
||||
verify_signed_pod_circuit(&mut builder, &signed_pod_verify)?;
|
||||
|
||||
// set the signed_pod as target values for the circuit
|
||||
signed_pod_verify.set_targets(&mut pw, &signed_pod)?;
|
||||
|
||||
// generate & verify proof
|
||||
let data = builder.build::<C>();
|
||||
let proof = data.prove(pw)?;
|
||||
data.verify(proof)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -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(¶ms, &mut builder, &empty_pod);
|
||||
let common_circuit_data = &*cache_get_standard_rec_main_pod_common_circuit_data();
|
||||
pad_circuit(&mut builder, common_circuit_data);
|
||||
|
||||
let data = timed!("EmptyPod build", builder.build::<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,
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(¶ms);
|
||||
let proof = deserialize_proof(&common, &data.proof)?;
|
||||
let verifier_only = deserialize_verifier_only(&data.verifier_only)?;
|
||||
Ok(Box::new(Self {
|
||||
Ok(Self {
|
||||
params,
|
||||
id,
|
||||
sts_hash: id,
|
||||
verifier_only,
|
||||
common_hash: data.common_hash,
|
||||
vd_set,
|
||||
proof,
|
||||
public_statements: data.public_statements,
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use std::{any::Any, collections::HashSet};
|
||||
|
||||
use num::{BigUint, One};
|
||||
|
||||
use super::*;
|
||||
|
|
@ -789,18 +813,19 @@ pub mod tests {
|
|||
backends::plonky2::{
|
||||
mock::mainpod::{MockMainPod, MockProver},
|
||||
primitives::ec::schnorr::SecretKey,
|
||||
signedpod::Signer,
|
||||
signer::Signer,
|
||||
},
|
||||
dict,
|
||||
examples::{
|
||||
attest_eth_friend, tickets_pod_full_flow, zu_kyc_pod_builder, zu_kyc_sign_pod_builders,
|
||||
EthDosHelper,
|
||||
attest_eth_friend, tickets_pod_full_flow, zu_kyc_pod_builder,
|
||||
zu_kyc_sign_dict_builders, EthDosHelper,
|
||||
},
|
||||
frontend::{
|
||||
self, literal, CustomPredicateBatchBuilder, MainPodBuilder, StatementTmplBuilder as STB,
|
||||
},
|
||||
middleware::{
|
||||
self, containers::Set, CustomPredicateRef, NativePredicate as NP, DEFAULT_VD_LIST,
|
||||
DEFAULT_VD_SET,
|
||||
self, containers::Set, CustomPredicateRef, NativePredicate as NP, Signer as _,
|
||||
DEFAULT_VD_LIST, DEFAULT_VD_SET,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -809,7 +834,7 @@ pub mod tests {
|
|||
let params = middleware::Params {
|
||||
// Currently the circuit uses random access that only supports vectors of length 64.
|
||||
// With max_input_main_pods=3 we need random access to a vector of length 73.
|
||||
max_input_recursive_pods: 0,
|
||||
max_input_pods: 0,
|
||||
max_custom_predicate_batches: 0,
|
||||
max_custom_predicate_verifications: 0,
|
||||
..Default::default()
|
||||
|
|
@ -819,7 +844,7 @@ pub mod tests {
|
|||
vds.push(rec_main_pod_circuit_data(¶ms).1.verifier_only.clone());
|
||||
let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap();
|
||||
|
||||
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(¶ms);
|
||||
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_dict_builders(¶ms);
|
||||
let signer = Signer(SecretKey(BigUint::one()));
|
||||
let gov_id_pod = gov_id_builder.sign(&signer)?;
|
||||
let signer = Signer(SecretKey(2u64.into()));
|
||||
|
|
@ -850,9 +875,8 @@ pub mod tests {
|
|||
#[test]
|
||||
fn test_mini_0() {
|
||||
let params = middleware::Params {
|
||||
max_input_signed_pods: 1,
|
||||
max_input_recursive_pods: 1,
|
||||
max_signed_pod_values: 6,
|
||||
max_signed_by: 1,
|
||||
max_input_pods: 1,
|
||||
max_statements: 8,
|
||||
max_public_statements: 4,
|
||||
max_input_pods_public_statements: 10,
|
||||
|
|
@ -862,7 +886,7 @@ pub mod tests {
|
|||
vds.push(rec_main_pod_circuit_data(¶ms).1.verifier_only.clone());
|
||||
let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap();
|
||||
|
||||
let mut gov_id_builder = frontend::SignedPodBuilder::new(¶ms);
|
||||
let mut gov_id_builder = frontend::SignedDictBuilder::new(¶ms);
|
||||
gov_id_builder.insert("idNumber", "4242424242");
|
||||
gov_id_builder.insert("dateOfBirth", 1169909384);
|
||||
gov_id_builder.insert("socialSecurityNumber", "G2121210");
|
||||
|
|
@ -870,7 +894,10 @@ pub mod tests {
|
|||
let gov_id = gov_id_builder.sign(&signer).unwrap();
|
||||
let now_minus_18y: i64 = 1169909388;
|
||||
let mut kyc_builder = frontend::MainPodBuilder::new(¶ms, &vd_set);
|
||||
kyc_builder.add_signed_pod(&gov_id);
|
||||
|
||||
kyc_builder
|
||||
.priv_op(frontend::Operation::dict_signed_by(&gov_id))
|
||||
.unwrap();
|
||||
kyc_builder
|
||||
.pub_op(frontend::Operation::lt(
|
||||
(&gov_id, "dateOfBirth"),
|
||||
|
|
@ -902,9 +929,8 @@ pub mod tests {
|
|||
#[test]
|
||||
fn test_mini_1() {
|
||||
let params = middleware::Params {
|
||||
max_input_signed_pods: 0,
|
||||
max_input_recursive_pods: 0,
|
||||
max_signed_pod_values: 0,
|
||||
max_signed_by: 0,
|
||||
max_input_pods: 0,
|
||||
max_statements: 2,
|
||||
max_public_statements: 1,
|
||||
max_input_pods_public_statements: 0,
|
||||
|
|
@ -939,13 +965,12 @@ pub mod tests {
|
|||
#[test]
|
||||
fn test_mainpod_small_empty() {
|
||||
let params = middleware::Params {
|
||||
max_input_signed_pods: 0,
|
||||
max_input_recursive_pods: 0,
|
||||
max_signed_by: 0,
|
||||
max_input_pods: 0,
|
||||
max_input_pods_public_statements: 2,
|
||||
max_statements: 5,
|
||||
max_signed_pod_values: 2,
|
||||
max_public_statements: 2,
|
||||
num_public_statements_id: 4,
|
||||
num_public_statements_hash: 4,
|
||||
max_statement_args: 4,
|
||||
max_operation_args: 4,
|
||||
max_custom_predicate_batches: 2,
|
||||
|
|
@ -996,7 +1021,7 @@ pub mod tests {
|
|||
let alice_attestation = attest_eth_friend(¶ms, &alice, bob.public_key());
|
||||
let bob_attestation = attest_eth_friend(¶ms, &bob, charlie.public_key());
|
||||
|
||||
let helper = EthDosHelper::new(¶ms, vd_set, false, alice.public_key())?;
|
||||
let helper = EthDosHelper::new(¶ms, vd_set, alice.public_key())?;
|
||||
let prover = Prover {};
|
||||
let dist_1 = helper.dist_1(&alice_attestation)?.prove(&prover)?;
|
||||
crate::measure_gates_print!();
|
||||
|
|
@ -1010,8 +1035,8 @@ pub mod tests {
|
|||
#[test]
|
||||
fn test_main_mini_custom_1() -> frontend::Result<()> {
|
||||
let params = Params {
|
||||
max_input_signed_pods: 0,
|
||||
max_input_recursive_pods: 0,
|
||||
max_signed_by: 0,
|
||||
max_input_pods: 0,
|
||||
max_statements: 9,
|
||||
max_public_statements: 4,
|
||||
max_statement_args: 4,
|
||||
|
|
@ -1020,7 +1045,7 @@ pub mod tests {
|
|||
max_custom_batch_size: 3,
|
||||
max_custom_predicate_wildcards: 4,
|
||||
max_custom_predicate_verifications: 2,
|
||||
max_merkle_proofs_containers: 0,
|
||||
max_merkle_proofs_containers: 3,
|
||||
max_merkle_tree_state_transition_proofs_containers: 0,
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -1030,17 +1055,20 @@ pub mod tests {
|
|||
let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap();
|
||||
|
||||
let mut cpb_builder = CustomPredicateBatchBuilder::new(params.clone(), "cpb".into());
|
||||
let stb0 = STB::new(NP::Equal).arg(("id", "score")).arg(literal(42));
|
||||
let stb0 = STB::new(NP::Contains)
|
||||
.arg("dict")
|
||||
.arg(literal("score"))
|
||||
.arg(literal(42));
|
||||
let stb1 = STB::new(NP::Equal)
|
||||
.arg(("secret_id", "key"))
|
||||
.arg(("id", "score"));
|
||||
.arg(("secret_dict", "key"))
|
||||
.arg(("dict", "score"));
|
||||
let _ = cpb_builder.predicate_and(
|
||||
"pred_and",
|
||||
&["id"],
|
||||
&["secret_id"],
|
||||
&["dict"],
|
||||
&["secret_dict"],
|
||||
&[stb0.clone(), stb1.clone()],
|
||||
)?;
|
||||
let _ = cpb_builder.predicate_or("pred_or", &["id"], &["secret_id"], &[stb0, stb1])?;
|
||||
let _ = cpb_builder.predicate_or("pred_or", &["dict"], &["secret_dict"], &[stb0, stb1])?;
|
||||
let cpb = cpb_builder.finish();
|
||||
|
||||
let cpb_and = CustomPredicateRef::new(cpb.clone(), 0);
|
||||
|
|
@ -1048,9 +1076,17 @@ pub mod tests {
|
|||
|
||||
let mut pod_builder = MainPodBuilder::new(¶ms, &vd_set);
|
||||
|
||||
let st0 = pod_builder.priv_op(frontend::Operation::new_entry("score", 42))?;
|
||||
let st1 = pod_builder.priv_op(frontend::Operation::new_entry("key", 42))?;
|
||||
let st2 = pod_builder.priv_op(frontend::Operation::eq(st1.clone(), st0.clone()))?;
|
||||
let dict = dict!(32, {"score" => 42})?;
|
||||
let secret_dict = dict!(32, {"key" => 42})?;
|
||||
let st0 = pod_builder.priv_op(frontend::Operation::dict_contains(
|
||||
dict.clone(),
|
||||
"score",
|
||||
42,
|
||||
))?;
|
||||
let st2 = pod_builder.priv_op(frontend::Operation::eq(
|
||||
(&secret_dict.clone(), "key"),
|
||||
(&dict, "score"),
|
||||
))?;
|
||||
|
||||
let _st3 = pod_builder.priv_op(frontend::Operation::custom(cpb_and.clone(), [st0, st2]))?;
|
||||
|
||||
|
|
@ -1071,15 +1107,9 @@ pub mod tests {
|
|||
fn test_set_contains() -> frontend::Result<()> {
|
||||
let params = Params::default();
|
||||
let mut builder = MainPodBuilder::new(¶ms, &DEFAULT_VD_SET);
|
||||
let set = [1, 2, 3].into_iter().map(|n| n.into()).collect();
|
||||
let st = builder
|
||||
.pub_op(frontend::Operation::new_entry(
|
||||
"entry",
|
||||
Set::new(params.max_depth_mt_containers, set).unwrap(),
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
builder.pub_op(frontend::Operation::set_contains(st, 1))?;
|
||||
let set: HashSet<_> = [1, 2, 3].into_iter().map(|n| n.into()).collect();
|
||||
let set = Set::new(params.max_depth_mt_containers, set).unwrap();
|
||||
builder.pub_op(frontend::Operation::set_contains(set, 1))?;
|
||||
|
||||
let prover = Prover {};
|
||||
let proof = builder.prove(&prover).unwrap();
|
||||
|
|
|
|||
|
|
@ -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)?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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!(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(¶ms);
|
||||
let signer = Signer(SecretKey(1u32.into()));
|
||||
let gov_id_pod = gov_id_builder.sign(&signer)?;
|
||||
let signer = Signer(SecretKey(2u32.into()));
|
||||
let pay_stub_pod = pay_stub_builder.sign(&signer)?;
|
||||
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_dict_builders(¶ms);
|
||||
let gov_id_signer = Signer(SecretKey(1u32.into()));
|
||||
let gov_id_pod = gov_id_builder.sign(&gov_id_signer)?;
|
||||
let pay_stub_signer = Signer(SecretKey(2u32.into()));
|
||||
let pay_stub_pod = pay_stub_builder.sign(&pay_stub_signer)?;
|
||||
let kyc_builder = zu_kyc_pod_builder(¶ms, vd_set, &gov_id_pod, &pay_stub_pod)?;
|
||||
|
||||
let prover = MockProver {};
|
||||
|
|
@ -472,8 +407,8 @@ pub mod tests {
|
|||
pod.verify()?;
|
||||
|
||||
let request = zu_kyc_pod_request(
|
||||
gov_id_pod.get("_signer").unwrap(),
|
||||
pay_stub_pod.get("_signer").unwrap(),
|
||||
&Value::from(gov_id_signer.public_key()),
|
||||
&Value::from(pay_stub_signer.public_key()),
|
||||
)?;
|
||||
assert!(request.exact_match_pod(&*pod).is_ok());
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(¶ms);
|
||||
pod.insert("idNumber", "4242424242");
|
||||
pod.insert("dateOfBirth", 1169909384);
|
||||
pod.insert("socialSecurityNumber", "G2121210");
|
||||
|
||||
let sk = SecretKey(123u64.into());
|
||||
let signer = Signer(sk);
|
||||
let pod = pod.sign(&signer).unwrap();
|
||||
let pod = (pod.pod as Box<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(())
|
||||
}
|
||||
}
|
||||
31
src/backends/plonky2/signer.rs
Normal file
31
src/backends/plonky2/signer.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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,13 +372,13 @@ 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.
|
||||
//builder.insert("attendeeName", "John Doe");
|
||||
// builder.insert("attendeeName", "John Doe");
|
||||
builder.insert("attendeeEmail", "john.doe@example.com");
|
||||
builder.insert("attendeePublicKey", TICKET_OWNER_SECRET_KEY.public_key());
|
||||
builder.insert("isConsumed", true);
|
||||
|
|
@ -430,7 +389,7 @@ pub fn tickets_sign_pod_builder(params: &Params) -> SignedPodBuilder {
|
|||
pub fn tickets_pod_builder(
|
||||
params: &Params,
|
||||
vd_set: &VDSet,
|
||||
signed_pod: &SignedPod,
|
||||
signed_dict: &SignedDict,
|
||||
expected_event_id: i64,
|
||||
expect_consumed: bool,
|
||||
blacklisted_emails: &Set,
|
||||
|
|
@ -438,28 +397,28 @@ pub fn tickets_pod_builder(
|
|||
let blacklisted_email_set_value = Value::from(TypedValue::Set(blacklisted_emails.clone()));
|
||||
// Create a main pod referencing this signed pod with some statements
|
||||
let mut builder = MainPodBuilder::new(params, vd_set);
|
||||
builder.add_signed_pod(signed_pod);
|
||||
builder.pub_op(Operation::eq((signed_pod, "eventId"), expected_event_id))?;
|
||||
builder.pub_op(Operation::eq((signed_pod, "isConsumed"), expect_consumed))?;
|
||||
builder.pub_op(Operation::eq((signed_pod, "isRevoked"), false))?;
|
||||
builder.pub_op(Operation::dict_signed_by(signed_dict))?;
|
||||
builder.pub_op(Operation::eq((signed_dict, "eventId"), expected_event_id))?;
|
||||
builder.pub_op(Operation::eq((signed_dict, "isConsumed"), expect_consumed))?;
|
||||
builder.pub_op(Operation::eq((signed_dict, "isRevoked"), false))?;
|
||||
builder.pub_op(Operation::dict_not_contains(
|
||||
blacklisted_email_set_value,
|
||||
(signed_pod, "attendeeEmail"),
|
||||
(signed_dict, "attendeeEmail"),
|
||||
))?;
|
||||
|
||||
// This isn't the most fool-proof way to prove ownership (it requires
|
||||
// verifier to check pod ID on an anchored key to confirm statement wasn't
|
||||
// copied), but it's the simplest.
|
||||
let st_sk = builder.priv_literal(TICKET_OWNER_SECRET_KEY)?;
|
||||
let sk = TICKET_OWNER_SECRET_KEY;
|
||||
builder.pub_op(Operation::public_key_of(
|
||||
(signed_pod, "attendeePublicKey"),
|
||||
st_sk.clone(),
|
||||
(signed_dict, "attendeePublicKey"),
|
||||
sk.clone(),
|
||||
))?;
|
||||
|
||||
// Nullifier calculation is public, but based on the private sk.
|
||||
let external_nullifier = "external nullifier";
|
||||
let nullifier = hash_values(&[TICKET_OWNER_SECRET_KEY.into(), external_nullifier.into()]);
|
||||
builder.pub_op(Operation::hash_of(nullifier, st_sk, external_nullifier))?;
|
||||
builder.pub_op(Operation::hash_of(nullifier, sk, external_nullifier))?;
|
||||
|
||||
Ok(builder)
|
||||
}
|
||||
|
|
@ -467,11 +426,11 @@ pub fn tickets_pod_builder(
|
|||
pub fn tickets_pod_full_flow(params: &Params, vd_set: &VDSet) -> Result<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())?,
|
||||
|
|
|
|||
|
|
@ -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(¶ms, 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(¶ms, 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!(
|
||||
|
|
|
|||
|
|
@ -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(¶ms);
|
||||
let (gov_id, pay_stub) = zu_kyc_sign_dict_builders(¶ms);
|
||||
|
||||
println!("{}", gov_id);
|
||||
println!("{}", pay_stub);
|
||||
|
||||
let signer = Signer(SecretKey(1u32.into()));
|
||||
let gov_id = gov_id.sign(&signer)?;
|
||||
check_kvs(&gov_id)?;
|
||||
let gov_id_signer = Signer(SecretKey(1u32.into()));
|
||||
let gov_id = gov_id.sign(&gov_id_signer)?;
|
||||
println!("{}", gov_id);
|
||||
|
||||
let signer = Signer(SecretKey(2u32.into()));
|
||||
let pay_stub = pay_stub.sign(&signer)?;
|
||||
check_kvs(&pay_stub)?;
|
||||
let pay_stub_signer = Signer(SecretKey(2u32.into()));
|
||||
let pay_stub = pay_stub.sign(&pay_stub_signer)?;
|
||||
println!("{}", pay_stub);
|
||||
|
||||
let kyc_builder = zu_kyc_pod_builder(¶ms, vd_set, &gov_id, &pay_stub)?;
|
||||
|
|
@ -923,14 +828,22 @@ pub mod tests {
|
|||
|
||||
println!("{}", kyc);
|
||||
|
||||
kyc.pod.verify()?;
|
||||
|
||||
let request = zu_kyc_pod_request(
|
||||
gov_id.get("_signer").unwrap(),
|
||||
pay_stub.get("_signer").unwrap(),
|
||||
&Value::from(gov_id_signer.public_key()),
|
||||
&Value::from(pay_stub_signer.public_key()),
|
||||
)?;
|
||||
// Check the bindings of the "gov" and "pay" wildcards from the PodRequest
|
||||
let bindings = request.exact_match_pod(&*kyc.pod).unwrap();
|
||||
assert_eq!(*bindings.get("gov").unwrap(), gov_id.id().into());
|
||||
assert_eq!(*bindings.get("pay").unwrap(), pay_stub.id().into());
|
||||
assert_eq!(
|
||||
*bindings.get("gov").unwrap(),
|
||||
gov_id.dict.commitment().into()
|
||||
);
|
||||
assert_eq!(
|
||||
*bindings.get("pay").unwrap(),
|
||||
pay_stub.dict.commitment().into()
|
||||
);
|
||||
|
||||
check_public_statements(&kyc)
|
||||
}
|
||||
|
|
@ -950,7 +863,7 @@ pub mod tests {
|
|||
let charlie = Signer(SecretKey(3u32.into()));
|
||||
let david = Signer(SecretKey(4u32.into()));
|
||||
|
||||
let helper = EthDosHelper::new(¶ms, vd_set, true, alice.public_key())?;
|
||||
let helper = EthDosHelper::new(¶ms, vd_set, alice.public_key())?;
|
||||
|
||||
let prover = MockProver {};
|
||||
|
||||
|
|
@ -960,8 +873,8 @@ pub mod tests {
|
|||
let request = eth_dos_request()?;
|
||||
assert!(request.exact_match_pod(&*dist_1.pod).is_ok());
|
||||
let bindings = request.exact_match_pod(&*dist_1.pod).unwrap();
|
||||
assert_eq!(*bindings.get("src").unwrap(), alice.public_key());
|
||||
assert_eq!(*bindings.get("dst").unwrap(), bob.public_key());
|
||||
assert_eq!(*bindings.get("src").unwrap(), alice.public_key().into());
|
||||
assert_eq!(*bindings.get("dst").unwrap(), bob.public_key().into());
|
||||
assert_eq!(*bindings.get("distance").unwrap(), 1.into());
|
||||
|
||||
let bob_attestation = attest_eth_friend(¶ms, &bob, charlie.public_key());
|
||||
|
|
@ -971,8 +884,8 @@ pub mod tests {
|
|||
dist_2.pod.verify()?;
|
||||
assert!(request.exact_match_pod(&*dist_2.pod).is_ok());
|
||||
let bindings = request.exact_match_pod(&*dist_2.pod).unwrap();
|
||||
assert_eq!(*bindings.get("src").unwrap(), alice.public_key());
|
||||
assert_eq!(*bindings.get("dst").unwrap(), charlie.public_key());
|
||||
assert_eq!(*bindings.get("src").unwrap(), alice.public_key().into());
|
||||
assert_eq!(*bindings.get("dst").unwrap(), charlie.public_key().into());
|
||||
assert_eq!(*bindings.get("distance").unwrap(), 2.into());
|
||||
|
||||
let charlie_attestation = attest_eth_friend(¶ms, &charlie, david.public_key());
|
||||
|
|
@ -982,8 +895,8 @@ pub mod tests {
|
|||
dist_3.pod.verify()?;
|
||||
assert!(request.exact_match_pod(&*dist_3.pod).is_ok());
|
||||
let bindings = request.exact_match_pod(&*dist_3.pod).unwrap();
|
||||
assert_eq!(*bindings.get("src").unwrap(), alice.public_key());
|
||||
assert_eq!(*bindings.get("dst").unwrap(), david.public_key());
|
||||
assert_eq!(*bindings.get("src").unwrap(), alice.public_key().into());
|
||||
assert_eq!(*bindings.get("dst").unwrap(), david.public_key().into());
|
||||
assert_eq!(*bindings.get("distance").unwrap(), 3.into());
|
||||
|
||||
Ok(())
|
||||
|
|
@ -1014,25 +927,22 @@ pub mod tests {
|
|||
let params = Params::default();
|
||||
let vd_set = &*MOCK_VD_SET;
|
||||
|
||||
let mut signed_builder = SignedPodBuilder::new(¶ms);
|
||||
let mut signed_builder = SignedDictBuilder::new(¶ms);
|
||||
signed_builder.insert("a", 1);
|
||||
signed_builder.insert("b", 1);
|
||||
let signer = Signer(SecretKey(1u32.into()));
|
||||
let signed_pod = signed_builder.sign(&signer).unwrap();
|
||||
let signed_dict = signed_builder.sign(&signer).unwrap();
|
||||
|
||||
let mut builder = MainPodBuilder::new(¶ms, vd_set);
|
||||
builder.add_signed_pod(&signed_pod);
|
||||
|
||||
//let op_val1 = Operation{
|
||||
// OperationType::Native(NativeOperation::CopyStatement),
|
||||
// signed_pod.
|
||||
//}
|
||||
builder
|
||||
.pub_op(Operation::dict_signed_by(&signed_dict))
|
||||
.unwrap();
|
||||
|
||||
let op_eq1 = Operation(
|
||||
OperationType::Native(NativeOperation::EqualFromEntries),
|
||||
vec![
|
||||
OperationArg::from((&signed_pod, "a")),
|
||||
OperationArg::from((&signed_pod, "b")),
|
||||
OperationArg::from((&signed_dict, "a")),
|
||||
OperationArg::from((&signed_dict, "b")),
|
||||
],
|
||||
OperationAux::None,
|
||||
);
|
||||
|
|
@ -1040,8 +950,8 @@ pub mod tests {
|
|||
let op_eq2 = Operation(
|
||||
OperationType::Native(NativeOperation::EqualFromEntries),
|
||||
vec![
|
||||
OperationArg::from((&signed_pod, "b")),
|
||||
OperationArg::from((&signed_pod, "a")),
|
||||
OperationArg::from((&signed_dict, "b")),
|
||||
OperationArg::from((&signed_dict, "a")),
|
||||
],
|
||||
OperationAux::None,
|
||||
);
|
||||
|
|
@ -1065,17 +975,21 @@ pub mod tests {
|
|||
fn test_false_st() {
|
||||
let params = Params::default();
|
||||
let vd_set = &*MOCK_VD_SET;
|
||||
let mut builder = SignedPodBuilder::new(¶ms);
|
||||
let mut builder = SignedDictBuilder::new(¶ms);
|
||||
|
||||
builder.insert("num", 2);
|
||||
let signer = Signer(SecretKey(1u32.into()));
|
||||
let pod = builder.sign(&signer).unwrap();
|
||||
let signed_dict = builder.sign(&signer).unwrap();
|
||||
|
||||
println!("{}", pod);
|
||||
println!("{}", signed_dict);
|
||||
|
||||
let mut builder = MainPodBuilder::new(¶ms, vd_set);
|
||||
builder.add_signed_pod(&pod);
|
||||
builder.pub_op(Operation::gt((&pod, "num"), 5)).unwrap();
|
||||
builder
|
||||
.pub_op(Operation::dict_signed_by(&signed_dict))
|
||||
.unwrap();
|
||||
builder
|
||||
.pub_op(Operation::gt((&signed_dict, "num"), 5))
|
||||
.unwrap();
|
||||
|
||||
let prover = MockProver {};
|
||||
let false_pod = builder.prove(&prover).unwrap();
|
||||
|
|
@ -1088,26 +1002,28 @@ pub mod tests {
|
|||
fn test_dictionaries() -> Result<()> {
|
||||
let params = Params::default();
|
||||
let vd_set = &*MOCK_VD_SET;
|
||||
let mut builder = SignedPodBuilder::new(¶ms);
|
||||
let mut builder = SignedDictBuilder::new(¶ms);
|
||||
|
||||
let mut my_dict_kvs: HashMap<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(¶ms, vd_set);
|
||||
builder.add_signed_pod(&pod);
|
||||
let st0 = pod.get_statement("dict").unwrap();
|
||||
let st1 = builder.op(true, Operation::new_entry("key", "a")).unwrap();
|
||||
let st2 = builder.literal(false, Value::from(1)).unwrap();
|
||||
builder
|
||||
.pub_op(Operation::dict_signed_by(&signed_dict))
|
||||
.unwrap();
|
||||
let st0 = signed_dict.get_statement("dict").unwrap();
|
||||
let local = dict!(32, {"key" => "a"})?;
|
||||
let st1 = builder
|
||||
.op(true, Operation::dict_contains(local, "key", "a"))
|
||||
.unwrap();
|
||||
|
||||
builder.pub_op(Operation(
|
||||
// OperationType
|
||||
|
|
@ -1116,7 +1032,7 @@ pub mod tests {
|
|||
vec![
|
||||
OperationArg::Statement(st0.clone()),
|
||||
OperationArg::Statement(st1),
|
||||
OperationArg::Statement(st2),
|
||||
OperationArg::Literal(Value::from(1)),
|
||||
],
|
||||
OperationAux::MerkleProof(dict.prove(&Key::from("a")).unwrap().1),
|
||||
))?;
|
||||
|
|
@ -1259,25 +1175,35 @@ pub mod tests {
|
|||
let sk = SecretKey::new_rand();
|
||||
let pk = sk.public_key();
|
||||
|
||||
// Signed POD contains public key as owner
|
||||
let mut builder = SignedPodBuilder::new(¶ms);
|
||||
// Signed Dict contains public key as owner
|
||||
let mut builder = SignedDictBuilder::new(¶ms);
|
||||
builder.insert("owner", Value::from(pk));
|
||||
builder.insert("other_data", Value::from(123));
|
||||
let signer = Signer(SecretKey(1u32.into()));
|
||||
let signed_pod = builder.sign(&signer).unwrap();
|
||||
let signed_dict = builder.sign(&signer).unwrap();
|
||||
|
||||
// Main POD proves ownership of the owner's secret key.
|
||||
let mut builder = MainPodBuilder::new(¶ms, vd_set);
|
||||
builder.add_signed_pod(&signed_pod);
|
||||
let st0 = signed_pod.get_statement("owner").unwrap();
|
||||
let st1 = builder
|
||||
.priv_op(Operation::new_entry("known_secret", Value::from(sk)))
|
||||
.unwrap();
|
||||
builder.pub_op(Operation::signed_by(
|
||||
Value::from(signed_dict.dict.clone()),
|
||||
Value::from(signed_dict.public_key),
|
||||
signed_dict.signature.clone(),
|
||||
))?;
|
||||
|
||||
let st0 = builder.priv_op(Operation::dict_contains(
|
||||
signed_dict.dict,
|
||||
"owner",
|
||||
Value::from(pk),
|
||||
))?;
|
||||
let local = dict!(32, { "known_secret" => sk.clone() })?;
|
||||
let st1 = builder.priv_op(Operation::dict_contains(
|
||||
local,
|
||||
"known_secret",
|
||||
Value::from(sk),
|
||||
))?;
|
||||
builder
|
||||
.pub_op(Operation(
|
||||
// OperationType
|
||||
OperationType::Native(NativeOperation::PublicKeyOf),
|
||||
// Vec<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(¶ms);
|
||||
let mut builder = SignedDictBuilder::new(¶ms);
|
||||
builder.insert("owner", Value::from(pk));
|
||||
builder.insert("other_data", Value::from(123));
|
||||
let signer = Signer(SecretKey(1u32.into()));
|
||||
let signed_pod = builder.sign(&signer).unwrap();
|
||||
let signed_dict = builder.sign(&signer).unwrap();
|
||||
|
||||
// Try to build with the wrong secret key. The pre-proving checks
|
||||
// will catch this.
|
||||
let mut builder = MainPodBuilder::new(¶ms, vd_set);
|
||||
builder.add_signed_pod(&signed_pod);
|
||||
let st0 = signed_pod.get_statement("owner").unwrap();
|
||||
builder
|
||||
.pub_op(Operation::dict_signed_by(&signed_dict))
|
||||
.unwrap();
|
||||
let st0 = signed_dict.get_statement("owner").unwrap();
|
||||
let local = dict!(32, {"known_secret" => SecretKey(BigUint::from(123u32))})?;
|
||||
let st1 = builder
|
||||
.priv_op(Operation::new_entry(
|
||||
"known_secret",
|
||||
Value::from(SecretKey(BigUint::from(123u32))),
|
||||
))
|
||||
.op(
|
||||
true,
|
||||
Operation::dict_contains(local, "known_secret", SecretKey(BigUint::from(123u32))),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(builder
|
||||
.pub_op(Operation(
|
||||
|
|
@ -1341,33 +1270,30 @@ pub mod tests {
|
|||
|
||||
// Try to build with wrong type in 1st arg
|
||||
let mut builder = MainPodBuilder::new(¶ms, vd_set);
|
||||
let st_pk = builder.literal(false, Value::from(pk)).unwrap();
|
||||
let st_int1 = builder.literal(false, Value::from(123)).unwrap();
|
||||
let int2 = Value::from(123);
|
||||
let sk = Value::from(sk);
|
||||
assert!(builder
|
||||
.pub_op(Operation(
|
||||
// OperationType
|
||||
OperationType::Native(NativeOperation::PublicKeyOf),
|
||||
// Vec<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(¶ms, vd_set);
|
||||
let st_sk = builder.literal(false, Value::from(pk)).unwrap();
|
||||
let st_int2 = builder.literal(false, Value::from(123)).unwrap();
|
||||
let mut builder = MainPodBuilder::new(¶ms, vd_set);
|
||||
let pk = Value::from(pk);
|
||||
let int1 = Value::from(123);
|
||||
assert!(builder
|
||||
.pub_op(Operation(
|
||||
// OperationType
|
||||
OperationType::Native(NativeOperation::PublicKeyOf),
|
||||
// Vec<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(¶ms, vd_set);
|
||||
let st = Statement::equal(AnchoredKey::from((SELF, "a")), Value::from(3));
|
||||
let op_new_entry = Operation(
|
||||
OperationType::Native(NativeOperation::NewEntry),
|
||||
vec![],
|
||||
OperationAux::None,
|
||||
);
|
||||
builder.insert(false, (st, op_new_entry.clone())).unwrap();
|
||||
|
||||
let st = Statement::equal(AnchoredKey::from((SELF, "a")), Value::from(28));
|
||||
builder.insert(false, (st, op_new_entry.clone())).unwrap();
|
||||
|
||||
let prover = MockProver {};
|
||||
let pod = builder.prove(&prover).unwrap();
|
||||
pod.pod.verify().unwrap();
|
||||
}
|
||||
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn test_reject_unsound_statement() {
|
||||
|
|
@ -1411,23 +1310,25 @@ pub mod tests {
|
|||
let params = Params::default();
|
||||
let vd_set = &*MOCK_VD_SET;
|
||||
let mut builder = MainPodBuilder::new(¶ms, vd_set);
|
||||
let self_a = AnchoredKey::from((SELF, "a"));
|
||||
let self_b = AnchoredKey::from((SELF, "b"));
|
||||
let value_of_a = Statement::equal(self_a.clone(), Value::from(3));
|
||||
let value_of_b = Statement::equal(self_b.clone(), Value::from(27));
|
||||
let local = dict!(32, {"a" => 3, "b" => 27}).unwrap();
|
||||
let value_of_a = Statement::contains(local.clone(), "a", 3);
|
||||
let value_of_b = Statement::contains(local.clone(), "b", 27);
|
||||
|
||||
let op_new_entry = Operation(
|
||||
OperationType::Native(NativeOperation::NewEntry),
|
||||
let op_contains = Operation(
|
||||
OperationType::Native(NativeOperation::DictContainsFromEntries),
|
||||
vec![],
|
||||
OperationAux::None,
|
||||
);
|
||||
builder
|
||||
.insert(false, (value_of_a.clone(), op_new_entry.clone()))
|
||||
.insert(false, (value_of_a.clone(), op_contains.clone()))
|
||||
.unwrap();
|
||||
builder
|
||||
.insert(false, (value_of_b.clone(), op_new_entry))
|
||||
.insert(false, (value_of_b.clone(), op_contains))
|
||||
.unwrap();
|
||||
let st = Statement::equal(self_a, self_b);
|
||||
let st = Statement::equal(
|
||||
AnchoredKey::from((&local, "a")),
|
||||
AnchoredKey::from((&local, "b")),
|
||||
);
|
||||
let op = Operation(
|
||||
OperationType::Native(NativeOperation::EqualFromEntries),
|
||||
vec![
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,8 +128,8 @@ impl PodRequest {
|
|||
}
|
||||
|
||||
// Try to bind wildcard to the POD ID
|
||||
let pod_id_value = Value::from(stmt_key.pod_id);
|
||||
self.try_bind_wildcard(&wildcard.name, pod_id_value, current_bindings, new_bindings)
|
||||
let root_value = Value::from(stmt_key.root);
|
||||
self.try_bind_wildcard(&wildcard.name, root_value, current_bindings, new_bindings)
|
||||
}
|
||||
|
||||
// Other combinations don't match
|
||||
|
|
@ -176,12 +176,14 @@ impl Display for PodRequest {
|
|||
mod tests {
|
||||
use crate::{
|
||||
backends::plonky2::{
|
||||
mock::mainpod::MockProver, primitives::ec::schnorr::SecretKey, signedpod::Signer,
|
||||
mock::mainpod::MockProver, primitives::ec::schnorr::SecretKey, signer::Signer,
|
||||
},
|
||||
examples::{
|
||||
zu_kyc_pod_builder, zu_kyc_pod_request, zu_kyc_sign_dict_builders, MOCK_VD_SET,
|
||||
},
|
||||
examples::{zu_kyc_pod_builder, zu_kyc_pod_request, zu_kyc_sign_pod_builders, MOCK_VD_SET},
|
||||
frontend::{MainPodBuilder, Operation},
|
||||
lang::parse,
|
||||
middleware::Params,
|
||||
middleware::{Params, Value},
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
|
@ -189,7 +191,7 @@ mod tests {
|
|||
let params = Params::default();
|
||||
let vd_set = &*MOCK_VD_SET;
|
||||
|
||||
let (gov_id, pay_stub) = zu_kyc_sign_pod_builders(¶ms);
|
||||
let (gov_id, pay_stub) = zu_kyc_sign_dict_builders(¶ms);
|
||||
let gov_id = gov_id.sign(&Signer(SecretKey(1u32.into()))).unwrap();
|
||||
let pay_stub = pay_stub.sign(&Signer(SecretKey(2u32.into()))).unwrap();
|
||||
let builder = zu_kyc_pod_builder(&Params::default(), vd_set, &gov_id, &pay_stub).unwrap();
|
||||
|
|
@ -198,8 +200,8 @@ mod tests {
|
|||
|
||||
// This request matches the POD
|
||||
let request = zu_kyc_pod_request(
|
||||
gov_id.get("_signer").unwrap(),
|
||||
pay_stub.get("_signer").unwrap(),
|
||||
&Value::from(gov_id.public_key),
|
||||
&Value::from(pay_stub.public_key),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(request.exact_match_pod(&*kyc.pod).is_ok());
|
||||
|
|
|
|||
|
|
@ -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(¶ms);
|
||||
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_dict_builders(¶ms);
|
||||
let signer = Signer(SecretKey(1u32.into()));
|
||||
let gov_id_pod = gov_id_builder.sign(&signer).unwrap();
|
||||
let signer = Signer(SecretKey(2u32.into()));
|
||||
|
|
@ -283,18 +223,19 @@ mod tests {
|
|||
let params = middleware::Params {
|
||||
// Currently the circuit uses random access that only supports vectors of length 64.
|
||||
// With max_input_main_pods=3 we need random access to a vector of length 73.
|
||||
max_input_recursive_pods: 1,
|
||||
max_input_pods: 1,
|
||||
..Default::default()
|
||||
};
|
||||
let mut vds = DEFAULT_VD_LIST.clone();
|
||||
vds.push(rec_main_pod_circuit_data(¶ms).1.verifier_only.clone());
|
||||
let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap();
|
||||
|
||||
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(¶ms);
|
||||
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_dict_builders(¶ms);
|
||||
let signer = Signer(SecretKey(1u32.into()));
|
||||
let gov_id_pod = gov_id_builder.sign(&signer)?;
|
||||
let signer = Signer(SecretKey(2u32.into()));
|
||||
let pay_stub_pod = pay_stub_builder.sign(&signer)?;
|
||||
let _signer = Signer(SecretKey(3u32.into()));
|
||||
let kyc_builder = zu_kyc_pod_builder(¶ms, &vd_set, &gov_id_pod, &pay_stub_pod)?;
|
||||
|
||||
let prover = Prover {};
|
||||
|
|
@ -311,7 +252,10 @@ mod tests {
|
|||
let deserialized: MainPod = serde_json::from_str(&serialized).unwrap();
|
||||
|
||||
assert_eq!(kyc_pod.public_statements, deserialized.public_statements);
|
||||
assert_eq!(kyc_pod.pod.id(), deserialized.pod.id());
|
||||
assert_eq!(
|
||||
kyc_pod.pod.statements_hash(),
|
||||
deserialized.pod.statements_hash()
|
||||
);
|
||||
assert_eq!(kyc_pod.pod.verify()?, deserialized.pod.verify()?);
|
||||
|
||||
Ok(())
|
||||
|
|
@ -324,7 +268,10 @@ mod tests {
|
|||
let deserialized: MainPod = serde_json::from_str(&serialized).unwrap();
|
||||
|
||||
assert_eq!(kyc_pod.public_statements, deserialized.public_statements);
|
||||
assert_eq!(kyc_pod.pod.id(), deserialized.pod.id());
|
||||
assert_eq!(
|
||||
kyc_pod.pod.statements_hash(),
|
||||
deserialized.pod.statements_hash()
|
||||
);
|
||||
assert_eq!(kyc_pod.pod.verify()?, deserialized.pod.verify()?);
|
||||
|
||||
Ok(())
|
||||
|
|
@ -348,7 +295,7 @@ mod tests {
|
|||
let alice_attestation = attest_eth_friend(¶ms, &alice, bob.public_key());
|
||||
let bob_attestation = attest_eth_friend(¶ms, &bob, charlie.public_key());
|
||||
|
||||
let helper = EthDosHelper::new(¶ms, vd_set, true, alice.public_key())?;
|
||||
let helper = EthDosHelper::new(¶ms, vd_set, alice.public_key())?;
|
||||
let prover = MockProver {};
|
||||
let dist_1 = helper.dist_1(&alice_attestation)?.prove(&prover)?;
|
||||
let dist_2 = helper
|
||||
|
|
@ -360,28 +307,28 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
// This tests that we can generate JSON Schemas for the MainPod and
|
||||
// SignedPod types, and that we can validate Signed and Main Pods
|
||||
// SignedDict types, and that we can validate Signed and Main Pods
|
||||
// against the schemas. Since both Mock and Plonky2 PODs have the same
|
||||
// public interface, we can assume that the schema works for both.
|
||||
fn test_schema() {
|
||||
let mainpod_schema = schema_for!(SerializedMainPod);
|
||||
let signedpod_schema = schema_for!(SerializedSignedPod);
|
||||
let signeddict_schema = schema_for!(SignedDict);
|
||||
|
||||
let kyc_pod = build_mock_zukyc_pod().unwrap();
|
||||
let signed_pod = signed_pod_builder()
|
||||
let signed_dict = signed_dict_builder()
|
||||
.sign(&Signer(SecretKey(1u32.into())))
|
||||
.unwrap();
|
||||
let ethdos_pod = build_ethdos_pod().unwrap();
|
||||
let mainpod_schema_value = serde_json::to_value(&mainpod_schema).unwrap();
|
||||
let signedpod_schema_value = serde_json::to_value(&signedpod_schema).unwrap();
|
||||
let signed_dict_schema_value = serde_json::to_value(&signeddict_schema).unwrap();
|
||||
|
||||
let kyc_pod_value = serde_json::to_value(&kyc_pod).unwrap();
|
||||
let mainpod_valid = jsonschema::validate(&mainpod_schema_value, &kyc_pod_value);
|
||||
assert!(mainpod_valid.is_ok(), "{:#?}", mainpod_valid);
|
||||
|
||||
let signed_pod_value = serde_json::to_value(&signed_pod).unwrap();
|
||||
let signedpod_valid = jsonschema::validate(&signedpod_schema_value, &signed_pod_value);
|
||||
assert!(signedpod_valid.is_ok(), "{:#?}", signedpod_valid);
|
||||
let signed_dict_value = serde_json::to_value(&signed_dict).unwrap();
|
||||
let signed_dict_valid = jsonschema::validate(&signed_dict_schema_value, &signed_dict_value);
|
||||
assert!(signed_dict_valid.is_ok(), "{:#?}", signed_dict_valid);
|
||||
|
||||
let ethdos_pod_value = serde_json::to_value(ðdos_pod).unwrap();
|
||||
let ethdos_pod_valid = jsonschema::validate(&mainpod_schema_value, ðdos_pod_value);
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -32,9 +32,8 @@ mod tests {
|
|||
backends::plonky2::primitives::ec::schnorr::SecretKey,
|
||||
lang::error::ProcessorError,
|
||||
middleware::{
|
||||
hash_str, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, Key,
|
||||
NativePredicate, Params, PodId, PodType, Predicate, RawValue, StatementTmpl,
|
||||
StatementTmplArg, Value, Wildcard, KEY_SIGNER, KEY_TYPE, SELF,
|
||||
CustomPredicate, CustomPredicateBatch, CustomPredicateRef, Key, NativePredicate,
|
||||
Params, Predicate, RawValue, StatementTmpl, StatementTmplArg, Value, Wildcard,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -107,7 +106,7 @@ mod tests {
|
|||
fn test_e2e_simple_request() -> Result<(), LangError> {
|
||||
let input = r#"
|
||||
REQUEST(
|
||||
Equal(?ConstPod["my_val"], 0x0000000000000000000000000000000000000000000000000000000000000001)
|
||||
Equal(?ConstPod["my_val"], Raw(0x0000000000000000000000000000000000000000000000000000000000000001))
|
||||
Lt(?GovPod["dob"], ?ConstPod["my_val"])
|
||||
)
|
||||
"#;
|
||||
|
|
@ -482,10 +481,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_e2e_ethdos_predicates() -> Result<(), LangError> {
|
||||
let params = Params {
|
||||
max_input_signed_pods: 3,
|
||||
max_input_recursive_pods: 3,
|
||||
max_input_pods: 3,
|
||||
max_statements: 31,
|
||||
max_signed_pod_values: 8,
|
||||
max_public_statements: 10,
|
||||
max_statement_args: 6,
|
||||
max_operation_args: 5,
|
||||
|
|
@ -496,10 +493,9 @@ mod tests {
|
|||
};
|
||||
|
||||
let input = r#"
|
||||
eth_friend(src, dst, private: attestation_pod) = AND(
|
||||
Equal(?attestation_pod["_type"], 1)
|
||||
Equal(?attestation_pod["_signer"], ?src)
|
||||
Equal(?attestation_pod["attestation"], ?dst)
|
||||
eth_friend(src, dst, private: attestation_dict) = AND(
|
||||
SignedBy(?attestation_dict, ?src)
|
||||
Equal(?attestation_dict["attestation"], ?dst)
|
||||
)
|
||||
|
||||
eth_dos_distance_base(src, dst, distance) = AND(
|
||||
|
|
@ -536,23 +532,13 @@ mod tests {
|
|||
// eth_friend (Index 0)
|
||||
let expected_friend_stmts = vec![
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![
|
||||
sta_ak(("attestation_pod", 2), "_type"), // Pub(0-1), Priv(2)
|
||||
sta_lit(PodType::Signed),
|
||||
],
|
||||
pred: Predicate::Native(NativePredicate::SignedBy),
|
||||
args: vec![sta_wc_lit("attestation_dict", 2), sta_wc_lit("src", 0)],
|
||||
},
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![
|
||||
sta_ak(("attestation_pod", 2), "_signer"),
|
||||
sta_wc_lit("src", 0), // Pub arg 0
|
||||
],
|
||||
},
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![
|
||||
sta_ak(("attestation_pod", 2), "attestation"),
|
||||
sta_ak(("attestation_dict", 2), "attestation"),
|
||||
sta_wc_lit("dst", 1), // Pub arg 1
|
||||
],
|
||||
},
|
||||
|
|
@ -563,7 +549,7 @@ mod tests {
|
|||
true, // AND
|
||||
expected_friend_stmts,
|
||||
2, // public_args_len: src, dst
|
||||
names(&["src", "dst", "attestation_pod"]),
|
||||
names(&["src", "dst", "attestation_dict"]),
|
||||
)?;
|
||||
|
||||
// eth_dos_distance_base (Index 1)
|
||||
|
|
@ -853,7 +839,6 @@ mod tests {
|
|||
#[test]
|
||||
fn test_e2e_literals() -> Result<(), LangError> {
|
||||
let pk = crate::backends::plonky2::primitives::ec::curve::Point::generator();
|
||||
let pod_id = PodId(hash_str("test"));
|
||||
let raw = RawValue::from(1);
|
||||
let string = "hello";
|
||||
let int = 123;
|
||||
|
|
@ -864,17 +849,14 @@ mod tests {
|
|||
r#"
|
||||
REQUEST(
|
||||
Equal(?A["pk"], {})
|
||||
Equal(?B["pod_id"], {})
|
||||
Equal(?C["raw"], {})
|
||||
Equal(?D["string"], {})
|
||||
Equal(?E["int"], {})
|
||||
Equal(?F["bool"], {})
|
||||
Equal(?G["sk"], {})
|
||||
Equal(?H["self"], SELF)
|
||||
Equal(?B["raw"], {})
|
||||
Equal(?C["string"], {})
|
||||
Equal(?D["int"], {})
|
||||
Equal(?E["bool"], {})
|
||||
Equal(?F["sk"], {})
|
||||
)
|
||||
"#,
|
||||
Value::from(pk).to_podlang_string(),
|
||||
Value::from(pod_id).to_podlang_string(),
|
||||
Value::from(raw).to_podlang_string(),
|
||||
Value::from(string).to_podlang_string(),
|
||||
Value::from(int).to_podlang_string(),
|
||||
|
|
@ -884,7 +866,6 @@ mod tests {
|
|||
/*
|
||||
REQUEST(
|
||||
Equal(?A["pk"], PublicKey(3t9fNuU194n7mSJPRdeaJRMqw6ZQCUddzvECWNe1k2b1rdBezXpJxF))
|
||||
Equal(?B["pod_id"], 0x735b31d3aad0f5b66002ffe1dc7d2eaa0ee9c59c09b641e8261530c5f3a02f29)
|
||||
Equal(?C["raw"], Raw(0x0000000000000000000000000000000000000000000000000000000000000001))
|
||||
Equal(?D["string"], "hello")
|
||||
Equal(?E["int"], 123)
|
||||
|
|
@ -905,31 +886,23 @@ mod tests {
|
|||
},
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![sta_ak(("B", 1), "pod_id"), sta_lit(Value::from(pod_id))],
|
||||
args: vec![sta_ak(("B", 1), "raw"), sta_lit(Value::from(raw))],
|
||||
},
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![sta_ak(("C", 2), "raw"), sta_lit(Value::from(raw))],
|
||||
args: vec![sta_ak(("C", 2), "string"), sta_lit(Value::from(string))],
|
||||
},
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![sta_ak(("D", 3), "string"), sta_lit(Value::from(string))],
|
||||
args: vec![sta_ak(("D", 3), "int"), sta_lit(Value::from(int))],
|
||||
},
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![sta_ak(("E", 4), "int"), sta_lit(Value::from(int))],
|
||||
args: vec![sta_ak(("E", 4), "bool"), sta_lit(Value::from(bool))],
|
||||
},
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![sta_ak(("F", 5), "bool"), sta_lit(Value::from(bool))],
|
||||
},
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![sta_ak(("G", 6), "sk"), sta_lit(Value::from(sk))],
|
||||
},
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![sta_ak(("H", 7), "self"), sta_lit(Value::from(SELF))],
|
||||
args: vec![sta_ak(("F", 5), "sk"), sta_lit(Value::from(sk))],
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -972,21 +945,13 @@ mod tests {
|
|||
let params = Params::default();
|
||||
let available_batches = &[];
|
||||
|
||||
let input = format!(
|
||||
r#"
|
||||
identity_verified(username, private: identity_pod) = AND(
|
||||
Equal(?identity_pod["{key_type}"], {signed_pod_type})
|
||||
Equal(?identity_pod["{key_signer}"], {identity_server_pk})
|
||||
Equal(?identity_pod["username"], ?username)
|
||||
Equal(?identity_pod["user_public_key"], ?user_public_key)
|
||||
let input = r#"
|
||||
identity_verified(username, private: identity_dict) = AND(
|
||||
Equal(?identity_dict["username"], ?username)
|
||||
Equal(?identity_dict["user_public_key"], ?user_public_key)
|
||||
)
|
||||
"#,
|
||||
key_type = KEY_TYPE,
|
||||
signed_pod_type = PodType::Signed as u32,
|
||||
key_signer = KEY_SIGNER,
|
||||
identity_server_pk =
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
);
|
||||
"#
|
||||
.to_string();
|
||||
|
||||
let result = parse(&input, ¶ms, available_batches);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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#"
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
))
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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(¶ms, &st).and_then(|ind| {
|
||||
|
|
@ -816,11 +833,10 @@ mod tests {
|
|||
|
||||
// Check non-existence proofs similarly
|
||||
(50..60).try_for_each(|k| {
|
||||
let key_s = Statement::Equal(key_ak.clone().into(), k.into());
|
||||
let pf = mt.prove_nonexistence(&k.into())?;
|
||||
|
||||
let op = Operation::NotContainsFromEntries(root_s.clone(), key_s, pf);
|
||||
let st = Statement::NotContains(root_ak.clone().into(), key_ak.clone().into());
|
||||
let op = Operation::NotContainsFromEntries(Statement::None, Statement::None, pf);
|
||||
let st = Statement::NotContains(root.into(), k.into());
|
||||
|
||||
op.check(¶ms, &st).and_then(|ind| {
|
||||
if ind {
|
||||
|
|
@ -838,11 +854,6 @@ mod tests {
|
|||
#[test]
|
||||
fn check_container_update_ops() -> Result<()> {
|
||||
let params = Params::default();
|
||||
let pod_id = PodId::default();
|
||||
let new_root_ak = AnchoredKey::new(pod_id, Key::new("new_root".into()));
|
||||
let old_root_ak = AnchoredKey::new(pod_id, Key::new("new_root".into()));
|
||||
let key_ak = AnchoredKey::new(pod_id, Key::new("key".into()));
|
||||
let val_ak = AnchoredKey::new(pod_id, Key::new("value".into()));
|
||||
|
||||
// Form Merkle tree
|
||||
let kvs = (0..10)
|
||||
|
|
@ -854,23 +865,24 @@ mod tests {
|
|||
(11..20)
|
||||
.map(|i| (hash_value(&i.into()).into(), i.into()))
|
||||
.try_for_each(|(k, v)| {
|
||||
let old_root_s = Statement::Equal(old_root_ak.clone().into(), mt.root().into());
|
||||
let old_root = mt.root();
|
||||
let mtp = mt.insert(&k, &v)?;
|
||||
// Form op args
|
||||
let new_root_s = Statement::Equal(new_root_ak.clone().into(), mt.root().into());
|
||||
let key_s = Statement::Equal(key_ak.clone().into(), k.into());
|
||||
let value_s = Statement::Equal(val_ak.clone().into(), v.into());
|
||||
let new_root = mt.root();
|
||||
|
||||
// Form op
|
||||
let op = Operation::ContainerInsertFromEntries(
|
||||
new_root_s, old_root_s, key_s, value_s, mtp,
|
||||
Statement::None,
|
||||
Statement::None,
|
||||
Statement::None,
|
||||
Statement::None,
|
||||
mtp,
|
||||
);
|
||||
// Form output statement
|
||||
let st = Statement::ContainerInsert(
|
||||
new_root_ak.clone().into(),
|
||||
old_root_ak.clone().into(),
|
||||
key_ak.clone().into(),
|
||||
val_ak.clone().into(),
|
||||
new_root.into(),
|
||||
old_root.into(),
|
||||
k.into(),
|
||||
v.into(),
|
||||
);
|
||||
|
||||
// Check op against output statement
|
||||
|
|
@ -890,23 +902,24 @@ mod tests {
|
|||
(11..20)
|
||||
.map(|i| (hash_value(&i.into()).into(), (i + 1).into()))
|
||||
.try_for_each(|(k, v)| {
|
||||
let old_root_s = Statement::Equal(old_root_ak.clone().into(), mt.root().into());
|
||||
let old_root = mt.root();
|
||||
let mtp = mt.update(&k, &v)?;
|
||||
// Form op args
|
||||
let new_root_s = Statement::Equal(new_root_ak.clone().into(), mt.root().into());
|
||||
let key_s = Statement::Equal(key_ak.clone().into(), k.into());
|
||||
let value_s = Statement::Equal(val_ak.clone().into(), v.into());
|
||||
let new_root = mt.root();
|
||||
|
||||
// Form op
|
||||
let op = Operation::ContainerUpdateFromEntries(
|
||||
new_root_s, old_root_s, key_s, value_s, mtp,
|
||||
Statement::None,
|
||||
Statement::None,
|
||||
Statement::None,
|
||||
Statement::None,
|
||||
mtp,
|
||||
);
|
||||
// Form output statement
|
||||
let st = Statement::ContainerUpdate(
|
||||
new_root_ak.clone().into(),
|
||||
old_root_ak.clone().into(),
|
||||
key_ak.clone().into(),
|
||||
val_ak.clone().into(),
|
||||
new_root.into(),
|
||||
old_root.into(),
|
||||
k.into(),
|
||||
v.into(),
|
||||
);
|
||||
|
||||
// Check op against output statement
|
||||
|
|
@ -926,20 +939,19 @@ mod tests {
|
|||
(11..20)
|
||||
.map(|i| hash_value(&i.into()).into())
|
||||
.try_for_each(|k| {
|
||||
let old_root_s = Statement::Equal(old_root_ak.clone().into(), mt.root().into());
|
||||
let old_root = mt.root();
|
||||
let mtp = mt.delete(&k)?;
|
||||
// Form op args
|
||||
let new_root_s = Statement::Equal(new_root_ak.clone().into(), mt.root().into());
|
||||
let key_s = Statement::Equal(key_ak.clone().into(), k.into());
|
||||
let new_root = mt.root();
|
||||
|
||||
// Form op
|
||||
let op = Operation::ContainerDeleteFromEntries(new_root_s, old_root_s, key_s, mtp);
|
||||
// Form output statement
|
||||
let st = Statement::ContainerDelete(
|
||||
new_root_ak.clone().into(),
|
||||
old_root_ak.clone().into(),
|
||||
key_ak.clone().into(),
|
||||
let op = Operation::ContainerDeleteFromEntries(
|
||||
Statement::None,
|
||||
Statement::None,
|
||||
Statement::None,
|
||||
mtp,
|
||||
);
|
||||
// Form output statement
|
||||
let st = Statement::ContainerDelete(new_root.into(), old_root.into(), k.into());
|
||||
|
||||
// Check op against output statement
|
||||
op.check(¶ms, &st).and_then(|ind| {
|
||||
|
|
@ -979,20 +991,13 @@ mod tests {
|
|||
];
|
||||
|
||||
let params = Params::default();
|
||||
let pod_id = PodId::default();
|
||||
let pk_ak = AnchoredKey::new(pod_id, Key::new("pubkey".into()));
|
||||
let sk_ak = AnchoredKey::new(pod_id, Key::new("secret".into()));
|
||||
|
||||
test_cases.iter().try_for_each(|(pk, sk, expect_good)| {
|
||||
// Form op args
|
||||
let pk_s = Statement::Equal(pk_ak.clone().into(), (*pk).into());
|
||||
let sk_s = Statement::Equal(sk_ak.clone().into(), sk.clone().into());
|
||||
|
||||
// Form op
|
||||
let op = Operation::PublicKeyOf(pk_s.clone(), sk_s.clone());
|
||||
let op = Operation::PublicKeyOf(Statement::None, Statement::None);
|
||||
|
||||
// Form output statement
|
||||
let st = Statement::PublicKeyOf(pk_ak.clone().into(), sk_ak.clone().into());
|
||||
let st = Statement::PublicKeyOf((*pk).into(), sk.clone().into());
|
||||
|
||||
// Check
|
||||
op.check(¶ms, &st).map(|is_good| {
|
||||
|
|
@ -1011,28 +1016,37 @@ mod tests {
|
|||
let fixed_pk = fixed_sk.public_key();
|
||||
|
||||
let params = Params::default();
|
||||
let pod_id = PodId::default();
|
||||
let pk_ak = AnchoredKey::new(pod_id, Key::new("pubkey".into()));
|
||||
let sk_ak = AnchoredKey::new(pod_id, Key::new("secret".into()));
|
||||
|
||||
// Form op args
|
||||
let pk_s = Statement::Equal(pk_ak.clone().into(), fixed_pk.into());
|
||||
let sk_s = Statement::Equal(sk_ak.clone().into(), fixed_sk.clone().into());
|
||||
|
||||
// Bad op and statement with bad first args
|
||||
let op = Operation::PublicKeyOf(pk_s.clone(), pk_s.clone());
|
||||
let st = Statement::PublicKeyOf(pk_ak.clone().into(), pk_ak.clone().into());
|
||||
let op = Operation::PublicKeyOf(Statement::None, Statement::None);
|
||||
let st = Statement::PublicKeyOf(fixed_pk.into(), fixed_pk.into());
|
||||
|
||||
// Check
|
||||
assert!(op.check(¶ms, &st).is_err());
|
||||
|
||||
// Bad op and statement with bad second args
|
||||
let op = Operation::PublicKeyOf(sk_s.clone(), sk_s.clone());
|
||||
let st = Statement::PublicKeyOf(sk_ak.clone().into(), sk_ak.clone().into());
|
||||
let op = Operation::PublicKeyOf(Statement::None, Statement::None);
|
||||
let st = Statement::PublicKeyOf(fixed_sk.clone().into(), fixed_sk.clone().into());
|
||||
|
||||
// Check
|
||||
assert!(op.check(¶ms, &st).is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_signed_by_op() -> Result<()> {
|
||||
let params = Params::default();
|
||||
|
||||
let sk = SecretKey(BigUint::from(0x1234567890abcdefu64));
|
||||
let pk = sk.public_key();
|
||||
let msg = Value::from("hello");
|
||||
let sig = Signer(sk).sign(msg.raw());
|
||||
|
||||
let op = Operation::SignedBy(Statement::None, Statement::None, sig);
|
||||
let st = Statement::SignedBy(msg.into(), pk.into());
|
||||
op.check(¶ms, &st)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue