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
|
|
@ -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)
|
||||
.verifier_only
|
||||
.clone(),
|
||||
cache_get_standard_empty_pod_verifier_circuit_data()
|
||||
.verifier_only
|
||||
.clone(),
|
||||
]
|
||||
// NOTE: We only include the recursive MainPod with default parameters here. We don't need to
|
||||
// include the verifying key of the EmptyPod because it's an Introduction pod and its verifying
|
||||
// key appears in its statement in a self-describing way.
|
||||
vec![cache_get_rec_main_pod_verifier_circuit_data(¶ms)
|
||||
.verifier_only
|
||||
.clone()]
|
||||
});
|
||||
|
||||
pub static DEFAULT_VD_SET: LazyLock<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 {
|
||||
let verifier_data_hash =
|
||||
crate::backends::plonky2::recursion::circuit::hash_verifier_data(vd);
|
||||
let p = self
|
||||
.proofs_map
|
||||
.get(&Hash(verifier_data_hash.elements))
|
||||
.ok_or(crate::middleware::Error::custom(
|
||||
"verifier_data not found in VDSet".to_string(),
|
||||
))?;
|
||||
proofs.push(p.clone());
|
||||
}
|
||||
Ok(proofs)
|
||||
pub fn get_vds_proof(&self, vd: &VerifierOnlyCircuitData) -> Result<MerkleClaimAndProof> {
|
||||
let verifier_data_hash =
|
||||
crate::backends::plonky2::recursion::circuit::hash_verifier_data(vd);
|
||||
Ok(self
|
||||
.proofs_map
|
||||
.get(&Hash(verifier_data_hash.elements))
|
||||
.ok_or(crate::middleware::Error::custom(
|
||||
"verifier_data not found in VDSet".to_string(),
|
||||
))?
|
||||
.clone())
|
||||
}
|
||||
/// Returns true if the `verifier_data_hash` is in the set
|
||||
pub fn contains(&self, verifier_data_hash: HashOut) -> bool {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
// We mocking or we don't need padding so we skip creating an EmptyPod
|
||||
MockEmptyPod::new_boxed(params, inputs.vd_set.clone())
|
||||
} else {
|
||||
EmptyPod::new_boxed(params, inputs.vd_set.clone())
|
||||
};
|
||||
// Input pods region
|
||||
let empty_pod_box: Box<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()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue