Integrate recursion into MainPod (#243)

* calculate MainPod id in a dynamic-friendly way

The MainPod id is now calculated with front padding and a fixed size
independent of max_public_statements so that introduction gadgets can be
verified by a MainPod while paying only for the number of statements
they use.  This is because with front padding of none-statements we can
precompute the poseidon state corresponding to absorbing all the padding
statements and only pay constraints for the non-padding statements.

The id is calculated as follows:
`id = hash(serialize(reverse(statements || none-statements)))`

* add time feature and disable timing by default

* apply suggestions from @arnaucube

* link issues in todos
This commit is contained in:
Eduard S. 2025-05-29 17:10:19 +02:00 committed by GitHub
parent d3fef8392e
commit 88a75986b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 1405 additions and 729 deletions

View file

@ -22,4 +22,6 @@ jobs:
run: cargo build run: cargo build
- name: Build metrics - name: Build metrics
run: cargo build --features metrics run: cargo build --features metrics
- name: Build time
run: cargo build --features time

View file

@ -39,3 +39,4 @@ jsonschema = "0.30.0"
default = ["backend_plonky2"] default = ["backend_plonky2"]
backend_plonky2 = ["plonky2"] backend_plonky2 = ["plonky2"]
metrics = [] metrics = []
time = []

View file

@ -1,15 +1,16 @@
//! This file exposes the middleware::basetypes to be used in the middleware when the //! This file exposes the basetypes to be used in the middleware when the `backend_plonky2` feature
//! `backend_plonky2` feature is enabled. //! is enabled.
//! See src/middleware/basetypes.rs for more details. //! See src/middleware/basetypes.rs for more details.
use plonky2::{ use plonky2::{
field::extension::quadratic::QuadraticExtension, field::{extension::quadratic::QuadraticExtension, goldilocks_field::GoldilocksField},
hash::poseidon::PoseidonHash, hash::poseidon::PoseidonHash,
plonk::{config::GenericConfig, proof::Proof as Plonky2Proof}, plonk::{circuit_builder, circuit_data, config::GenericConfig, proof},
}; };
use serde::Serialize; use serde::Serialize;
use crate::middleware::F; /// F is the native field we use everywhere. Currently it's Goldilocks from plonky2
pub type F = GoldilocksField;
/// D defines the extension degree of the field used in the Plonky2 proofs (quadratic extension). /// D defines the extension degree of the field used in the Plonky2 proofs (quadratic extension).
pub const D: usize = 2; pub const D: usize = 2;
@ -27,5 +28,11 @@ impl GenericConfig<D> for C {
type InnerHasher = PoseidonHash; type InnerHasher = PoseidonHash;
} }
/// proof system proof pub type CircuitData = circuit_data::CircuitData<F, C, D>;
pub type Proof = Plonky2Proof<F, C, D>; pub type CommonCircuitData = circuit_data::CommonCircuitData<F, D>;
pub type ProverOnlyCircuitData = circuit_data::ProverOnlyCircuitData<F, C, D>;
pub type VerifierOnlyCircuitData = circuit_data::VerifierOnlyCircuitData<C, D>;
pub type VerifierCircuitData = circuit_data::VerifierCircuitData<F, C, D>;
pub type CircuitBuilder = circuit_builder::CircuitBuilder<F, D>;
pub type Proof = proof::Proof<F, C, D>;
pub type ProofWithPublicInputs = proof::ProofWithPublicInputs<F, C, D>;

View file

@ -17,13 +17,12 @@ use plonky2::{
target::{BoolTarget, Target}, target::{BoolTarget, Target},
witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite}, witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite},
}, },
plonk::{circuit_builder::CircuitBuilder, circuit_data::CommonCircuitData},
util::serialization::{Buffer, IoResult, Read, Write}, util::serialization::{Buffer, IoResult, Read, Write},
}; };
use crate::{ use crate::{
backends::plonky2::{ backends::plonky2::{
basetypes::D, basetypes::{CircuitBuilder, CommonCircuitData, D},
circuits::mainpod::CustomPredicateVerification, circuits::mainpod::CustomPredicateVerification,
error::Result, error::Result,
mainpod::{Operation, OperationArg, Statement}, mainpod::{Operation, OperationArg, Statement},
@ -47,13 +46,13 @@ pub struct ValueTarget {
} }
impl ValueTarget { impl ValueTarget {
pub fn zero(builder: &mut CircuitBuilder<F, D>) -> Self { pub fn zero(builder: &mut CircuitBuilder) -> Self {
Self { Self {
elements: [builder.zero(); VALUE_SIZE], elements: [builder.zero(); VALUE_SIZE],
} }
} }
pub fn one(builder: &mut CircuitBuilder<F, D>) -> Self { pub fn one(builder: &mut CircuitBuilder) -> Self {
Self { Self {
elements: array::from_fn(|i| { elements: array::from_fn(|i| {
if i == 0 { if i == 0 {
@ -99,25 +98,25 @@ impl StatementArgTarget {
} }
} }
pub fn none(builder: &mut CircuitBuilder<F, D>) -> Self { pub fn none(builder: &mut CircuitBuilder) -> Self {
let empty = builder.constant_value(EMPTY_VALUE); let empty = builder.constant_value(EMPTY_VALUE);
Self::new(empty, empty) Self::new(empty, empty)
} }
pub fn literal(builder: &mut CircuitBuilder<F, D>, value: &ValueTarget) -> Self { pub fn literal(builder: &mut CircuitBuilder, value: &ValueTarget) -> Self {
let empty = builder.constant_value(EMPTY_VALUE); let empty = builder.constant_value(EMPTY_VALUE);
Self::new(*value, empty) Self::new(*value, empty)
} }
pub fn anchored_key( pub fn anchored_key(
_builder: &mut CircuitBuilder<F, D>, _builder: &mut CircuitBuilder,
pod_id: &ValueTarget, pod_id: &ValueTarget,
key: &ValueTarget, key: &ValueTarget,
) -> Self { ) -> Self {
Self::new(*pod_id, *key) Self::new(*pod_id, *key)
} }
pub fn wildcard_literal(builder: &mut CircuitBuilder<F, D>, value: &ValueTarget) -> Self { pub fn wildcard_literal(builder: &mut CircuitBuilder, value: &ValueTarget) -> Self {
let empty = builder.constant_value(EMPTY_VALUE); let empty = builder.constant_value(EMPTY_VALUE);
Self::new(*value, empty) Self::new(*value, empty)
} }
@ -137,17 +136,17 @@ pub struct StatementTarget {
} }
pub trait Build<T> { pub trait Build<T> {
fn build(self, builder: &mut CircuitBuilder<F, D>, params: &Params) -> T; fn build(self, builder: &mut CircuitBuilder, params: &Params) -> T;
} }
impl Build<NativePredicateTarget> for NativePredicate { impl Build<NativePredicateTarget> for NativePredicate {
fn build(self, builder: &mut CircuitBuilder<F, D>, params: &Params) -> NativePredicateTarget { fn build(self, builder: &mut CircuitBuilder, params: &Params) -> NativePredicateTarget {
NativePredicateTarget::constant(builder, params, self) NativePredicateTarget::constant(builder, params, self)
} }
} }
impl<T> Build<T> for T { impl<T> Build<T> for T {
fn build(self, _builder: &mut CircuitBuilder<F, D>, _params: &Params) -> T { fn build(self, _builder: &mut CircuitBuilder, _params: &Params) -> T {
self self
} }
} }
@ -155,7 +154,7 @@ impl<T> Build<T> for T {
impl StatementTarget { impl StatementTarget {
/// Build a new native StatementTarget. Pads the arguments. /// Build a new native StatementTarget. Pads the arguments.
pub fn new_native( pub fn new_native(
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
params: &Params, params: &Params,
native_predicate: impl Build<NativePredicateTarget>, native_predicate: impl Build<NativePredicateTarget>,
args: &[StatementArgTarget], args: &[StatementArgTarget],
@ -194,7 +193,7 @@ impl StatementTarget {
pub fn has_native_type( pub fn has_native_type(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
params: &Params, params: &Params,
t: NativePredicate, t: NativePredicate,
) -> BoolTarget { ) -> BoolTarget {
@ -210,7 +209,7 @@ pub struct OperationTypeTarget {
impl OperationTypeTarget { impl OperationTypeTarget {
pub fn new_custom( pub fn new_custom(
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
batch_id: HashOutTarget, batch_id: HashOutTarget,
index: Target, index: Target,
) -> Self { ) -> Self {
@ -222,10 +221,7 @@ impl OperationTypeTarget {
} }
} }
pub fn as_custom( pub fn as_custom(&self, builder: &mut CircuitBuilder) -> (BoolTarget, HashOutTarget, Target) {
&self,
builder: &mut CircuitBuilder<F, D>,
) -> (BoolTarget, HashOutTarget, Target) {
// TODO: Use an enum for these prefixes // TODO: Use an enum for these prefixes
let three = builder.constant(F::from_canonical_usize(3)); let three = builder.constant(F::from_canonical_usize(3));
let op_is_custom = builder.is_equal(self.elements[0], three); let op_is_custom = builder.is_equal(self.elements[0], three);
@ -234,7 +230,7 @@ impl OperationTypeTarget {
(op_is_custom, batch_id, index) (op_is_custom, batch_id, index)
} }
pub fn has_native(&self, builder: &mut CircuitBuilder<F, D>, t: NativeOperation) -> BoolTarget { pub fn has_native(&self, builder: &mut CircuitBuilder, t: NativeOperation) -> BoolTarget {
// TODO: Use an enum for these prefixes // TODO: Use an enum for these prefixes
let one = builder.one(); let one = builder.one();
let op_is_native = builder.is_equal(self.elements[0], one); let op_is_native = builder.is_equal(self.elements[0], one);
@ -288,7 +284,7 @@ pub struct NativePredicateTarget(Target);
impl NativePredicateTarget { impl NativePredicateTarget {
pub fn constant( pub fn constant(
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
params: &Params, params: &Params,
native_predicate: NativePredicate, native_predicate: NativePredicate,
) -> Self { ) -> Self {
@ -316,7 +312,7 @@ pub struct PredicateTarget {
impl PredicateTarget { impl PredicateTarget {
pub fn new_native( pub fn new_native(
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
params: &Params, params: &Params,
native_predicate: impl Build<NativePredicateTarget>, native_predicate: impl Build<NativePredicateTarget>,
) -> Self { ) -> Self {
@ -328,7 +324,7 @@ impl PredicateTarget {
} }
} }
pub fn new_batch_self(builder: &mut CircuitBuilder<F, D>, index: Target) -> Self { pub fn new_batch_self(builder: &mut CircuitBuilder, index: Target) -> Self {
let prefix = builder.constant(F::from(PredicatePrefix::BatchSelf)); let prefix = builder.constant(F::from(PredicatePrefix::BatchSelf));
let zero = builder.zero(); let zero = builder.zero();
Self { Self {
@ -337,7 +333,7 @@ impl PredicateTarget {
} }
pub fn new_custom( pub fn new_custom(
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
batch_id: HashOutTarget, batch_id: HashOutTarget,
index: Target, index: Target,
) -> Self { ) -> Self {
@ -373,7 +369,7 @@ impl LiteralOrWildcardTarget {
/// cases: ((is_key, key), (is_wildcard, wildcard_index)) /// cases: ((is_key, key), (is_wildcard, wildcard_index))
pub fn cases( pub fn cases(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
) -> ((BoolTarget, ValueTarget), (BoolTarget, Target)) { ) -> ((BoolTarget, ValueTarget), (BoolTarget, Target)) {
let zero = builder.zero(); let zero = builder.zero();
let is_zero_tail: Vec<_> = (1..4) let is_zero_tail: Vec<_> = (1..4)
@ -397,12 +393,12 @@ pub struct StatementTmplArgTarget {
} }
impl StatementTmplArgTarget { impl StatementTmplArgTarget {
pub fn as_none(&self, builder: &mut CircuitBuilder<F, D>) -> BoolTarget { pub fn as_none(&self, builder: &mut CircuitBuilder) -> BoolTarget {
let prefix = builder.constant(F::from(StatementTmplArgPrefix::None)); let prefix = builder.constant(F::from(StatementTmplArgPrefix::None));
builder.is_equal(self.elements[0], prefix) builder.is_equal(self.elements[0], prefix)
} }
pub fn as_literal(&self, builder: &mut CircuitBuilder<F, D>) -> (BoolTarget, ValueTarget) { pub fn as_literal(&self, builder: &mut CircuitBuilder) -> (BoolTarget, ValueTarget) {
let prefix = builder.constant(F::from(StatementTmplArgPrefix::Literal)); let prefix = builder.constant(F::from(StatementTmplArgPrefix::Literal));
let case_ok = builder.is_equal(self.elements[0], prefix); let case_ok = builder.is_equal(self.elements[0], prefix);
let value = ValueTarget::from_slice(&self.elements[1..5]); let value = ValueTarget::from_slice(&self.elements[1..5]);
@ -411,7 +407,7 @@ impl StatementTmplArgTarget {
pub fn as_anchored_key( pub fn as_anchored_key(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
) -> (BoolTarget, Target, LiteralOrWildcardTarget) { ) -> (BoolTarget, Target, LiteralOrWildcardTarget) {
let prefix = builder.constant(F::from(StatementTmplArgPrefix::AnchoredKey)); let prefix = builder.constant(F::from(StatementTmplArgPrefix::AnchoredKey));
let case_ok = builder.is_equal(self.elements[0], prefix); let case_ok = builder.is_equal(self.elements[0], prefix);
@ -420,7 +416,7 @@ impl StatementTmplArgTarget {
(case_ok, id_wildcard_index, value_key_or_wildcard) (case_ok, id_wildcard_index, value_key_or_wildcard)
} }
pub fn as_wildcard_literal(&self, builder: &mut CircuitBuilder<F, D>) -> (BoolTarget, Target) { pub fn as_wildcard_literal(&self, builder: &mut CircuitBuilder) -> (BoolTarget, Target) {
let prefix = builder.constant(F::from(StatementTmplArgPrefix::WildcardLiteral)); let prefix = builder.constant(F::from(StatementTmplArgPrefix::WildcardLiteral));
let case_ok = builder.is_equal(self.elements[0], prefix); let case_ok = builder.is_equal(self.elements[0], prefix);
let wildcard_index = self.elements[1]; let wildcard_index = self.elements[1];
@ -479,7 +475,7 @@ pub struct CustomPredicateBatchTarget {
} }
impl CustomPredicateBatchTarget { impl CustomPredicateBatchTarget {
pub fn id(&self, builder: &mut CircuitBuilder<F, D>) -> HashOutTarget { pub fn id(&self, builder: &mut CircuitBuilder) -> HashOutTarget {
let flattened = self.predicates.iter().flat_map(|cp| cp.flatten()).collect(); let flattened = self.predicates.iter().flat_map(|cp| cp.flatten()).collect();
builder.hash_n_to_hash_no_pad::<PoseidonHash>(flattened) builder.hash_n_to_hash_no_pad::<PoseidonHash>(flattened)
} }
@ -573,7 +569,7 @@ impl Flattenable for CustomPredicateEntryTarget {
} }
impl CustomPredicateEntryTarget { impl CustomPredicateEntryTarget {
pub fn hash(&self, builder: &mut CircuitBuilder<F, D>) -> HashOutTarget { pub fn hash(&self, builder: &mut CircuitBuilder) -> HashOutTarget {
builder.hash_n_to_hash_no_pad::<PoseidonHash>(self.flatten()) builder.hash_n_to_hash_no_pad::<PoseidonHash>(self.flatten())
} }
} }
@ -630,7 +626,7 @@ pub struct CustomPredicateVerifyQueryTarget {
} }
impl CustomPredicateVerifyQueryTarget { impl CustomPredicateVerifyQueryTarget {
pub fn hash(&self, builder: &mut CircuitBuilder<F, D>) -> HashOutTarget { pub fn hash(&self, builder: &mut CircuitBuilder) -> HashOutTarget {
builder.hash_n_to_hash_no_pad::<PoseidonHash>(self.flatten()) builder.hash_n_to_hash_no_pad::<PoseidonHash>(self.flatten())
} }
} }
@ -930,7 +926,7 @@ pub trait CircuitBuilderPod<F: RichField + Extendable<D>, const D: usize> {
fn lt_mask(&mut self, len: usize, n: Target) -> Vec<BoolTarget>; fn lt_mask(&mut self, len: usize, n: Target) -> Vec<BoolTarget>;
} }
impl CircuitBuilderPod<F, D> for CircuitBuilder<F, D> { impl CircuitBuilderPod<F, D> for CircuitBuilder {
fn connect_slice(&mut self, xs: &[Target], ys: &[Target]) { fn connect_slice(&mut self, xs: &[Target], ys: &[Target]) {
assert_eq!(xs.len(), ys.len()); assert_eq!(xs.len(), ys.len());
for (x, y) in xs.iter().zip(ys.iter()) { for (x, y) in xs.iter().zip(ys.iter()) {
@ -1267,11 +1263,11 @@ impl CircuitBuilderPod<F, D> for CircuitBuilder<F, D> {
// then do `ts: &[HashCache<T>]`. // then do `ts: &[HashCache<T>]`.
fn vec_ref<T: Flattenable>(&mut self, params: &Params, ts: &[T], i: Target) -> T { fn vec_ref<T: Flattenable>(&mut self, params: &Params, ts: &[T], i: Target) -> T {
// TODO: Revisit this when we need more than 64 statements. // TODO: Revisit this when we need more than 64 statements.
let vector_ref = |builder: &mut CircuitBuilder<F, D>, v: &[Target], i| { let vector_ref = |builder: &mut CircuitBuilder, v: &[Target], i| {
assert!(v.len() <= 64); assert!(v.len() <= 64);
builder.random_access(i, v.to_vec()) builder.random_access(i, v.to_vec())
}; };
let matrix_row_ref = |builder: &mut CircuitBuilder<F, D>, m: &[Vec<Target>], i| { let matrix_row_ref = |builder: &mut CircuitBuilder, m: &[Vec<Target>], i| {
let num_rows = m.len(); let num_rows = m.len();
let num_columns = m let num_columns = m
.first() .first()
@ -1367,7 +1363,7 @@ pub struct LtMaskGenerator {
pub(crate) mask: Vec<Target>, pub(crate) mask: Vec<Target>,
} }
impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F, D> for LtMaskGenerator { impl SimpleGenerator<F, D> for LtMaskGenerator {
fn id(&self) -> String { fn id(&self) -> String {
"LtMaskGenerator".to_string() "LtMaskGenerator".to_string()
} }
@ -1390,12 +1386,12 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F, D> for LtM
Ok(()) Ok(())
} }
fn serialize(&self, dst: &mut Vec<u8>, _common_data: &CommonCircuitData<F, D>) -> IoResult<()> { fn serialize(&self, dst: &mut Vec<u8>, _common_data: &CommonCircuitData) -> IoResult<()> {
dst.write_target(self.n)?; dst.write_target(self.n)?;
dst.write_target_vec(&self.mask) dst.write_target_vec(&self.mask)
} }
fn deserialize(src: &mut Buffer, _common_data: &CommonCircuitData<F, D>) -> IoResult<Self> { fn deserialize(src: &mut Buffer, _common_data: &CommonCircuitData) -> IoResult<Self> {
let n = src.read_target()?; let n = src.read_target()?;
let mask = src.read_target_vec()?; let mask = src.read_target_vec()?;
Ok(Self { n, mask }) Ok(Self { n, mask })

View file

@ -10,14 +10,14 @@ use plonky2::{
}, },
iop::{ iop::{
target::{BoolTarget, Target}, target::{BoolTarget, Target},
witness::PartialWitness, witness::{PartialWitness, WitnessWrite},
}, },
plonk::{circuit_builder::CircuitBuilder, config::AlgebraicHasher}, plonk::config::AlgebraicHasher,
}; };
use crate::{ use crate::{
backends::plonky2::{ backends::plonky2::{
basetypes::D, basetypes::CircuitBuilder,
circuits::{ circuits::{
common::{ common::{
CircuitBuilderPod, CustomPredicateBatchTarget, CustomPredicateEntryTarget, CircuitBuilderPod, CustomPredicateBatchTarget, CustomPredicateEntryTarget,
@ -28,18 +28,21 @@ use crate::{
}, },
signedpod::{SignedPodVerifyGadget, SignedPodVerifyTarget}, signedpod::{SignedPodVerifyGadget, SignedPodVerifyTarget},
}, },
emptypod::EmptyPod,
error::Result, error::Result,
mainpod::{self, pad_statement}, mainpod::{self, pad_statement},
primitives::merkletree::{ primitives::merkletree::{
MerkleClaimAndProof, MerkleClaimAndProofTarget, MerkleProofGadget, MerkleClaimAndProof, MerkleClaimAndProofTarget, MerkleProofGadget,
}, },
recursion::{InnerCircuit, VerifiedProofTarget},
signedpod::SignedPod, signedpod::SignedPod,
}, },
measure_gates_begin, measure_gates_end, measure_gates_begin, measure_gates_end,
middleware::{ middleware::{
AnchoredKey, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, NativeOperation, AnchoredKey, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, Hash,
NativePredicate, Params, PodType, PredicatePrefix, Statement, StatementArg, ToFields, NativeOperation, NativePredicate, Params, PodType, PredicatePrefix, Statement,
Value, WildcardValue, F, KEY_TYPE, SELF, VALUE_SIZE, StatementArg, ToFields, Value, WildcardValue, EMPTY_VALUE, F, HASH_SIZE, KEY_TYPE, SELF,
VALUE_SIZE,
}, },
}; };
@ -47,6 +50,13 @@ use crate::{
// MainPod verification // MainPod verification
// //
/// Offset in public inputs where we store the pod id
pub const PI_OFFSET_ID: usize = 0;
/// Offset in public inputs where we store the verified data array root
pub const PI_OFFSET_VDSROOT: usize = 4;
pub const NUM_PUBLIC_INPUTS: usize = 8;
struct OperationVerifyGadget { struct OperationVerifyGadget {
params: Params, params: Params,
} }
@ -58,7 +68,7 @@ impl OperationVerifyGadget {
/// argument. /// argument.
fn first_n_args_as_values<const N: usize>( fn first_n_args_as_values<const N: usize>(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
resolved_op_args: &[StatementTarget], resolved_op_args: &[StatementTarget],
) -> (BoolTarget, [ValueTarget; N]) { ) -> (BoolTarget, [ValueTarget; N]) {
let arg_is_valueof = resolved_op_args[..N] let arg_is_valueof = resolved_op_args[..N]
@ -80,7 +90,7 @@ impl OperationVerifyGadget {
fn eval( fn eval(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st: &StatementTarget, st: &StatementTarget,
op: &OperationTarget, op: &OperationTarget,
prev_statements: &[StatementTarget], prev_statements: &[StatementTarget],
@ -212,7 +222,7 @@ impl OperationVerifyGadget {
fn eval_contains_from_entries( fn eval_contains_from_entries(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st: &StatementTarget, st: &StatementTarget,
op_type: &OperationTypeTarget, op_type: &OperationTypeTarget,
resolved_merkle_claim: MerkleClaimTarget, resolved_merkle_claim: MerkleClaimTarget,
@ -260,7 +270,7 @@ impl OperationVerifyGadget {
fn eval_not_contains_from_entries( fn eval_not_contains_from_entries(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st: &StatementTarget, st: &StatementTarget,
op_type: &OperationTypeTarget, op_type: &OperationTypeTarget,
resolved_merkle_claim: MerkleClaimTarget, resolved_merkle_claim: MerkleClaimTarget,
@ -306,7 +316,7 @@ impl OperationVerifyGadget {
fn eval_custom( fn eval_custom(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st: &StatementTarget, st: &StatementTarget,
op_type: &OperationTypeTarget, op_type: &OperationTypeTarget,
resolved_custom_pred_verification: HashOutTarget, resolved_custom_pred_verification: HashOutTarget,
@ -331,7 +341,7 @@ impl OperationVerifyGadget {
/// NotEqualFromEntries. /// NotEqualFromEntries.
fn eval_eq_neq_from_entries( fn eval_eq_neq_from_entries(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st: &StatementTarget, st: &StatementTarget,
op_type: &OperationTypeTarget, op_type: &OperationTypeTarget,
resolved_op_args: &[StatementTarget], resolved_op_args: &[StatementTarget],
@ -382,7 +392,7 @@ impl OperationVerifyGadget {
/// LtEqFromEntries. /// LtEqFromEntries.
fn eval_lt_lteq_from_entries( fn eval_lt_lteq_from_entries(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st: &StatementTarget, st: &StatementTarget,
op_type: &OperationTypeTarget, op_type: &OperationTypeTarget,
resolved_op_args: &[StatementTarget], resolved_op_args: &[StatementTarget],
@ -451,7 +461,7 @@ impl OperationVerifyGadget {
fn eval_hash_of( fn eval_hash_of(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st: &StatementTarget, st: &StatementTarget,
op_type: &OperationTypeTarget, op_type: &OperationTypeTarget,
resolved_op_args: &[StatementTarget], resolved_op_args: &[StatementTarget],
@ -485,7 +495,7 @@ impl OperationVerifyGadget {
fn eval_sum_of( fn eval_sum_of(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st: &StatementTarget, st: &StatementTarget,
op_type: &OperationTypeTarget, op_type: &OperationTypeTarget,
resolved_op_args: &[StatementTarget], resolved_op_args: &[StatementTarget],
@ -524,7 +534,7 @@ impl OperationVerifyGadget {
fn eval_product_of( fn eval_product_of(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st: &StatementTarget, st: &StatementTarget,
op_type: &OperationTypeTarget, op_type: &OperationTypeTarget,
resolved_op_args: &[StatementTarget], resolved_op_args: &[StatementTarget],
@ -563,7 +573,7 @@ impl OperationVerifyGadget {
fn eval_max_of( fn eval_max_of(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st: &StatementTarget, st: &StatementTarget,
op_type: &OperationTypeTarget, op_type: &OperationTypeTarget,
resolved_op_args: &[StatementTarget], resolved_op_args: &[StatementTarget],
@ -609,7 +619,7 @@ impl OperationVerifyGadget {
fn eval_transitive_eq( fn eval_transitive_eq(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st: &StatementTarget, st: &StatementTarget,
op_type: &OperationTypeTarget, op_type: &OperationTypeTarget,
resolved_op_args: &[StatementTarget], resolved_op_args: &[StatementTarget],
@ -645,7 +655,7 @@ impl OperationVerifyGadget {
} }
fn eval_none( fn eval_none(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st: &StatementTarget, st: &StatementTarget,
op_type: &OperationTypeTarget, op_type: &OperationTypeTarget,
) -> BoolTarget { ) -> BoolTarget {
@ -663,7 +673,7 @@ impl OperationVerifyGadget {
fn eval_new_entry( fn eval_new_entry(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st: &StatementTarget, st: &StatementTarget,
op_type: &OperationTypeTarget, op_type: &OperationTypeTarget,
prev_statements: &[StatementTarget], prev_statements: &[StatementTarget],
@ -701,7 +711,7 @@ impl OperationVerifyGadget {
fn eval_lt_to_neq( fn eval_lt_to_neq(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st: &StatementTarget, st: &StatementTarget,
op_type: &OperationTypeTarget, op_type: &OperationTypeTarget,
resolved_op_args: &[StatementTarget], resolved_op_args: &[StatementTarget],
@ -730,7 +740,7 @@ impl OperationVerifyGadget {
fn eval_copy( fn eval_copy(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st: &StatementTarget, st: &StatementTarget,
op_type: &OperationTypeTarget, op_type: &OperationTypeTarget,
resolved_op_args: &[StatementTarget], resolved_op_args: &[StatementTarget],
@ -759,7 +769,7 @@ struct CustomOperationVerifyGadget {
impl CustomOperationVerifyGadget { impl CustomOperationVerifyGadget {
fn statement_arg_from_template( fn statement_arg_from_template(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st_tmpl_arg: &StatementTmplArgTarget, st_tmpl_arg: &StatementTmplArgTarget,
args: &[ValueTarget], args: &[ValueTarget],
) -> StatementArgTarget { ) -> StatementArgTarget {
@ -812,7 +822,7 @@ impl CustomOperationVerifyGadget {
fn statement_from_template( fn statement_from_template(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st_tmpl: &StatementTmplTarget, st_tmpl: &StatementTmplTarget,
args: &[ValueTarget], args: &[ValueTarget],
) -> StatementTarget { ) -> StatementTarget {
@ -836,7 +846,7 @@ impl CustomOperationVerifyGadget {
/// - Build the expected operation type /// - Build the expected operation type
fn eval( fn eval(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
custom_predicate: &CustomPredicateEntryTarget, custom_predicate: &CustomPredicateEntryTarget,
op_args: &[StatementTarget], op_args: &[StatementTarget],
args: &[ValueTarget], // arguments to the custom predicate, public and private args: &[ValueTarget], // arguments to the custom predicate, public and private
@ -901,10 +911,49 @@ impl CustomOperationVerifyGadget {
} }
} }
struct CalculateIdGadget { /// Replace references to SELF by `self_id` in a statement.
struct NormalizeStatementGadget {
params: Params, params: Params,
} }
impl NormalizeStatementGadget {
fn eval(
&self,
builder: &mut CircuitBuilder,
statement: &StatementTarget,
self_id: &ValueTarget,
) -> StatementTarget {
let zero_value = builder.constant_value(EMPTY_VALUE);
let self_value = builder.constant_value(SELF.0.into());
let args = statement
.args
.iter()
.map(|arg| {
let first = ValueTarget::from_slice(&arg.elements[..VALUE_SIZE]);
let second = ValueTarget::from_slice(&arg.elements[VALUE_SIZE..]);
let is_not_ak = builder.is_equal_flattenable(&zero_value, &second);
let is_ak = builder.not(is_not_ak);
let is_self = builder.is_equal_flattenable(&self_value, &first);
let normalize = builder.and(is_ak, is_self);
let first_normalized =
builder.select_flattenable(&self.params, normalize, self_id, &first);
StatementArgTarget::new(first_normalized, second)
})
.collect_vec();
StatementTarget {
predicate: statement.predicate.clone(),
args,
}
}
}
pub struct CalculateIdGadget {
/// `params.num_public_statements_id` is the total number of statements that will be hashed.
/// The id is calculated with front-padded none-statements and then the input statements
/// reversed. The part of the hash from the front-padded none-statements is precomputed.
pub params: Params,
}
impl CalculateIdGadget { impl CalculateIdGadget {
/// Precompute the hash state by absorbing all full chunks from `inputs` and return the reminder /// Precompute the hash state by absorbing all full chunks from `inputs` and return the reminder
/// elements that didn't fit into a chunk. /// elements that didn't fit into a chunk.
@ -923,7 +972,7 @@ impl CalculateIdGadget {
/// Hash `inputs` starting from a circuit-constant `perm` state. /// Hash `inputs` starting from a circuit-constant `perm` state.
fn hash_from_state<H: AlgebraicHasher<F>, P: PlonkyPermutation<F>>( fn hash_from_state<H: AlgebraicHasher<F>, P: PlonkyPermutation<F>>(
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
perm: P, perm: P,
inputs: &[Target], inputs: &[Target],
) -> HashOutTarget { ) -> HashOutTarget {
@ -953,17 +1002,19 @@ impl CalculateIdGadget {
} }
} }
fn eval( pub fn eval(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
// These statements will be padded to reach `self.num_statements`
statements: &[StatementTarget], statements: &[StatementTarget],
) -> HashOutTarget { ) -> HashOutTarget {
assert!(statements.len() <= self.params.num_public_statements_id);
let measure = measure_gates_begin!(builder, "CalculateId"); let measure = measure_gates_begin!(builder, "CalculateId");
let statements_rev_flattened = statements.iter().rev().flat_map(|s| s.flatten()); let statements_rev_flattened = statements.iter().rev().flat_map(|s| s.flatten());
let mut none_st = mainpod::Statement::from(Statement::None); let mut none_st = mainpod::Statement::from(Statement::None);
pad_statement(&self.params, &mut none_st); pad_statement(&self.params, &mut none_st);
let front_pad_elts = iter::repeat(&none_st) let front_pad_elts = iter::repeat(&none_st)
.take(self.params.num_public_statements_id - self.params.max_public_statements) .take(self.params.num_public_statements_id - statements.len())
.flat_map(|s| s.to_fields(&self.params)) .flat_map(|s| s.to_fields(&self.params))
.collect_vec(); .collect_vec();
let (perm, front_pad_elts_rem) = let (perm, front_pad_elts_rem) =
@ -992,7 +1043,7 @@ impl MainPodVerifyGadget {
// index // index
fn normalize_st_tmpl( fn normalize_st_tmpl(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
st_tmpl: &StatementTmplTarget, st_tmpl: &StatementTmplTarget,
id: HashOutTarget, id: HashOutTarget,
) -> StatementTmplTarget { ) -> StatementTmplTarget {
@ -1012,7 +1063,7 @@ impl MainPodVerifyGadget {
/// calculate the id of each batch. /// calculate the id of each batch.
fn build_custom_predicate_table( fn build_custom_predicate_table(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
) -> Result<(Vec<HashOutTarget>, Vec<CustomPredicateBatchTarget>)> { ) -> Result<(Vec<HashOutTarget>, Vec<CustomPredicateBatchTarget>)> {
let measure = measure_gates_begin!(builder, "BuildCustomPredicateTable"); let measure = measure_gates_begin!(builder, "BuildCustomPredicateTable");
let params = &self.params; let params = &self.params;
@ -1053,7 +1104,7 @@ impl MainPodVerifyGadget {
/// custom predicate against the operation and statement. /// custom predicate against the operation and statement.
fn build_custom_predicate_verification_table( fn build_custom_predicate_verification_table(
&self, &self,
builder: &mut CircuitBuilder<F, D>, builder: &mut CircuitBuilder,
custom_predicate_table: &[HashOutTarget], custom_predicate_table: &[HashOutTarget],
) -> Result<(Vec<HashOutTarget>, Vec<CustomPredicateVerifyEntryTarget>)> { ) -> Result<(Vec<HashOutTarget>, Vec<CustomPredicateVerifyEntryTarget>)> {
let measure = measure_gates_begin!(builder, "BuildCustomPredicateVerificationTable"); let measure = measure_gates_begin!(builder, "BuildCustomPredicateVerificationTable");
@ -1105,7 +1156,13 @@ impl MainPodVerifyGadget {
)) ))
} }
fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<MainPodVerifyTarget> { fn eval(
&self,
builder: &mut CircuitBuilder,
verified_proofs: &[VerifiedProofTarget],
) -> Result<MainPodVerifyTarget> {
assert_eq!(self.params.max_input_recursive_pods, verified_proofs.len());
let measure = measure_gates_begin!(builder, "MainPodVerify"); let measure = measure_gates_begin!(builder, "MainPodVerify");
let params = &self.params; let params = &self.params;
// 1. Verify all input signed pods // 1. Verify all input signed pods
@ -1115,6 +1172,7 @@ impl MainPodVerifyGadget {
params: params.clone(), params: params.clone(),
} }
.eval(builder)?; .eval(builder)?;
builder.assert_one(signed_pod.signature.enabled.target);
signed_pods.push(signed_pod); signed_pods.push(signed_pod);
} }
@ -1132,16 +1190,47 @@ impl MainPodVerifyGadget {
statements.len(), statements.len(),
1 + self.params.max_input_signed_pods * self.params.max_signed_pod_values 1 + self.params.max_input_signed_pods * self.params.max_signed_pod_values
); );
// TODO: Fill with input main pods
for _main_pod in 0..self.params.max_input_main_pods { let id_gadget = CalculateIdGadget {
for _statement in 0..self.params.max_public_statements { params: params.clone(),
statements.push(StatementTarget::new_native( };
builder, let mut input_pods_self_statements: Vec<Vec<StatementTarget>> = Vec::new();
&self.params, let normalize_statement_gadget = NormalizeStatementGadget {
NativePredicate::None, params: self.params.clone(),
&[], };
)) for verified_proof in verified_proofs {
let expected_id = HashOutTarget::try_from(
&verified_proof.public_inputs[PI_OFFSET_ID..PI_OFFSET_ID + HASH_SIZE],
)
.expect("4 elements");
let id_value = ValueTarget {
elements: expected_id.elements,
};
let mut input_pod_self_statements = Vec::new();
for _ in 0..self.params.max_input_pods_public_statements {
let self_st = builder.add_virtual_statement(params);
let normalized_st = normalize_statement_gadget.eval(builder, &self_st, &id_value);
input_pod_self_statements.push(self_st);
statements.push(normalized_st);
} }
let id = id_gadget.eval(builder, &input_pod_self_statements);
builder.connect_hashes(expected_id, id);
input_pods_self_statements.push(input_pod_self_statements);
}
let vds_root = builder.add_virtual_hash();
// TODO: verify that all input pod proofs use verifier data from the public input VD array
// This requires merkle proofs
// https://github.com/0xPARC/pod2/issues/250
// Verify that VD array that input pod uses is the same we use now.
for verified_proof in verified_proofs {
let verified_proof_vds_root = HashOutTarget::try_from(
&verified_proof.public_inputs[PI_OFFSET_VDSROOT..PI_OFFSET_VDSROOT + HASH_SIZE],
)
.expect("4 elements");
builder.connect_hashes(vds_root, verified_proof_vds_root);
} }
// Add the input (private and public) statements and corresponding operations // Add the input (private and public) statements and corresponding operations
@ -1220,8 +1309,10 @@ impl MainPodVerifyGadget {
measure_gates_end!(builder, measure); measure_gates_end!(builder, measure);
Ok(MainPodVerifyTarget { Ok(MainPodVerifyTarget {
params: params.clone(), params: params.clone(),
vds_root,
id, id,
signed_pods, signed_pods,
input_pods_self_statements,
statements: input_statements.to_vec(), statements: input_statements.to_vec(),
operations, operations,
merkle_proofs, merkle_proofs,
@ -1233,8 +1324,10 @@ impl MainPodVerifyGadget {
pub struct MainPodVerifyTarget { pub struct MainPodVerifyTarget {
params: Params, params: Params,
vds_root: HashOutTarget,
id: HashOutTarget, id: HashOutTarget,
signed_pods: Vec<SignedPodVerifyTarget>, signed_pods: Vec<SignedPodVerifyTarget>,
input_pods_self_statements: Vec<Vec<StatementTarget>>,
// The KEY_TYPE statement must be the first public one // The KEY_TYPE statement must be the first public one
statements: Vec<StatementTarget>, statements: Vec<StatementTarget>,
operations: Vec<OperationTarget>, operations: Vec<OperationTarget>,
@ -1251,7 +1344,9 @@ pub struct CustomPredicateVerification {
} }
pub struct MainPodVerifyInput { pub struct MainPodVerifyInput {
pub vds_root: Hash,
pub signed_pods: Vec<SignedPod>, pub signed_pods: Vec<SignedPod>,
pub recursive_pods_pub_self_statements: Vec<Vec<Statement>>,
pub statements: Vec<mainpod::Statement>, pub statements: Vec<mainpod::Statement>,
pub operations: Vec<mainpod::Operation>, pub operations: Vec<mainpod::Operation>,
pub merkle_proofs: Vec<MerkleClaimAndProof>, pub merkle_proofs: Vec<MerkleClaimAndProof>,
@ -1259,18 +1354,44 @@ pub struct MainPodVerifyInput {
pub custom_predicate_verifications: Vec<CustomPredicateVerification>, pub custom_predicate_verifications: Vec<CustomPredicateVerification>,
} }
fn set_targets_input_pods_self_statements(
pw: &mut PartialWitness<F>,
params: &Params,
statements_target: &[StatementTarget],
statements: &[Statement],
) -> Result<()> {
assert_eq!(
statements_target.len(),
params.max_input_pods_public_statements
);
assert!(statements.len() <= params.num_public_statements_id);
for (i, statement) in statements.iter().enumerate() {
statements_target[i].set_targets(pw, params, &statement.clone().into())?;
}
// Padding
let mut none_st = mainpod::Statement::from(Statement::None);
pad_statement(params, &mut none_st);
for statement_target in statements_target.iter().skip(statements.len()) {
statement_target.set_targets(pw, params, &none_st)?;
}
Ok(())
}
impl MainPodVerifyTarget { impl MainPodVerifyTarget {
pub fn set_targets( pub fn set_targets(
&self, &self,
pw: &mut PartialWitness<F>, pw: &mut PartialWitness<F>,
input: &MainPodVerifyInput, input: &MainPodVerifyInput,
) -> Result<()> { ) -> Result<()> {
pw.set_target_arr(&self.vds_root.elements, &input.vds_root.0)?;
assert!(input.signed_pods.len() <= self.params.max_input_signed_pods); assert!(input.signed_pods.len() <= self.params.max_input_signed_pods);
for (i, signed_pod) in input.signed_pods.iter().enumerate() { for (i, signed_pod) in input.signed_pods.iter().enumerate() {
self.signed_pods[i].set_targets(pw, signed_pod)?; self.signed_pods[i].set_targets(pw, signed_pod)?;
} }
// Padding // Padding
if self.params.max_input_signed_pods > 0 { if input.signed_pods.len() != self.params.max_input_signed_pods {
// TODO: Instead of using an input for padding, use a canonical minimal SignedPod, // TODO: Instead of using an input for padding, use a canonical minimal SignedPod,
// without it a MainPod configured to support input signed pods must have at least one // without it a MainPod configured to support input signed pods must have at least one
// input signed pod :( // input signed pod :(
@ -1279,6 +1400,34 @@ impl MainPodVerifyTarget {
self.signed_pods[i].set_targets(pw, pad_pod)?; self.signed_pods[i].set_targets(pw, pad_pod)?;
} }
} }
assert!(
input.recursive_pods_pub_self_statements.len() <= self.params.max_input_recursive_pods
);
for (i, pod_pub_statements) in input.recursive_pods_pub_self_statements.iter().enumerate() {
set_targets_input_pods_self_statements(
pw,
&self.params,
&self.input_pods_self_statements[i],
pod_pub_statements,
)?;
}
// Padding
if input.recursive_pods_pub_self_statements.len() != self.params.max_input_recursive_pods {
let empty_pod = EmptyPod::new_boxed(&self.params, input.vds_root);
let empty_pod_statements = empty_pod.pub_statements();
for i in
input.recursive_pods_pub_self_statements.len()..self.params.max_input_recursive_pods
{
set_targets_input_pods_self_statements(
pw,
&self.params,
&self.input_pods_self_statements[i],
&empty_pod_statements,
)?;
}
}
assert_eq!(input.statements.len(), self.params.max_statements); assert_eq!(input.statements.len(), self.params.max_statements);
for (i, (st, op)) in zip_eq(&input.statements, &input.operations).enumerate() { for (i, (st, op)) in zip_eq(&input.statements, &input.operations).enumerate() {
self.statements[i].set_targets(pw, &self.params, st)?; self.statements[i].set_targets(pw, &self.params, st)?;
@ -1342,17 +1491,44 @@ pub struct MainPodVerifyCircuit {
pub params: Params, pub params: Params,
} }
// TODO: Remove this type and implement it's logic directly in `impl InnerCircuit for MainPodVerifyTarget`
impl MainPodVerifyCircuit { impl MainPodVerifyCircuit {
pub fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<MainPodVerifyTarget> { pub fn eval(
&self,
builder: &mut CircuitBuilder,
verified_proofs: &[VerifiedProofTarget],
) -> Result<MainPodVerifyTarget> {
let main_pod = MainPodVerifyGadget { let main_pod = MainPodVerifyGadget {
params: self.params.clone(), params: self.params.clone(),
} }
.eval(builder)?; .eval(builder, verified_proofs)?;
builder.register_public_inputs(&main_pod.id.elements); builder.register_public_inputs(&main_pod.id.elements);
builder.register_public_inputs(&main_pod.vds_root.elements);
Ok(main_pod) Ok(main_pod)
} }
} }
impl InnerCircuit for MainPodVerifyTarget {
type Input = MainPodVerifyInput;
type Params = Params;
fn build(
builder: &mut CircuitBuilder,
params: &Self::Params,
verified_proofs: &[VerifiedProofTarget],
) -> Result<Self> {
MainPodVerifyCircuit {
params: params.clone(),
}
.eval(builder, verified_proofs)
}
/// assigns the values to the targets
fn set_targets(&self, pw: &mut PartialWitness<F>, input: &Self::Input) -> Result<()> {
self.set_targets(pw, input)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{iter, ops::Not}; use std::{iter, ops::Not};
@ -1395,7 +1571,7 @@ mod tests {
}; };
let config = CircuitConfig::standard_recursion_config(); let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config); let mut builder = CircuitBuilder::new(config);
let st_target = builder.add_virtual_statement(&params); let st_target = builder.add_virtual_statement(&params);
let op_target = builder.add_virtual_operation(&params); let op_target = builder.add_virtual_operation(&params);
@ -2268,7 +2444,7 @@ mod tests {
expected_st_arg: StatementArg, expected_st_arg: StatementArg,
) -> Result<()> { ) -> Result<()> {
let config = CircuitConfig::standard_recursion_config(); let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config); let mut builder = CircuitBuilder::new(config);
let gadget = CustomOperationVerifyGadget { let gadget = CustomOperationVerifyGadget {
params: params.clone(), params: params.clone(),
}; };
@ -2369,7 +2545,7 @@ mod tests {
expected_st: Statement, expected_st: Statement,
) -> Result<()> { ) -> Result<()> {
let config = CircuitConfig::standard_recursion_config(); let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config); let mut builder = CircuitBuilder::new(config);
let gadget = CustomOperationVerifyGadget { let gadget = CustomOperationVerifyGadget {
params: params.clone(), params: params.clone(),
}; };
@ -2433,7 +2609,7 @@ mod tests {
expected_st: Option<Statement>, expected_st: Option<Statement>,
) -> Result<()> { ) -> Result<()> {
let config = CircuitConfig::standard_recursion_config(); let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config); let mut builder = CircuitBuilder::new(config);
let gadget = CustomOperationVerifyGadget { let gadget = CustomOperationVerifyGadget {
params: params.clone(), params: params.clone(),
}; };
@ -2775,7 +2951,7 @@ mod tests {
fn helper_calculate_id(params: &Params, statements: &[Statement]) -> Result<()> { fn helper_calculate_id(params: &Params, statements: &[Statement]) -> Result<()> {
let config = CircuitConfig::standard_recursion_config(); let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config); let mut builder = CircuitBuilder::new(config);
let gadget = CalculateIdGadget { let gadget = CalculateIdGadget {
params: params.clone(), params: params.clone(),
}; };

View file

@ -83,7 +83,7 @@ pub struct SignedPodVerifyTarget {
// the KEY_TYPE entry must be the first one // the KEY_TYPE entry must be the first one
// the KEY_SIGNER entry must be the second one // the KEY_SIGNER entry must be the second one
mt_proofs: Vec<MerkleProofExistenceTarget>, mt_proofs: Vec<MerkleProofExistenceTarget>,
signature: SignatureVerifyTarget, pub(crate) signature: SignatureVerifyTarget,
} }
impl SignedPodVerifyTarget { impl SignedPodVerifyTarget {

View file

@ -0,0 +1,207 @@
use std::{collections::HashMap, sync::Mutex};
use base64::{prelude::BASE64_STANDARD, Engine};
use itertools::Itertools;
use plonky2::{
hash::hash_types::HashOutTarget,
iop::witness::{PartialWitness, WitnessWrite},
plonk::{
circuit_builder::CircuitBuilder,
circuit_data::{self, CircuitConfig},
proof::ProofWithPublicInputs,
},
};
use crate::{
backends::plonky2::{
basetypes::{Proof, C, D},
circuits::{
common::{Flattenable, StatementTarget},
mainpod::{CalculateIdGadget, PI_OFFSET_ID},
},
error::{Error, Result},
mainpod::{self, calculate_id},
recursion::pad_circuit,
LazyLock, DEFAULT_PARAMS, STANDARD_REC_MAIN_POD_CIRCUIT_DATA,
},
middleware::{
self, AnchoredKey, DynError, Hash, Params, Pod, PodId, PodType, RecursivePod, Statement,
ToFields, Value, VerifierOnlyCircuitData, EMPTY_HASH, F, HASH_SIZE, KEY_TYPE, SELF,
},
timed,
};
struct EmptyPodVerifyCircuit {
params: Params,
}
fn type_statement() -> Statement {
Statement::ValueOf(
AnchoredKey::from((SELF, KEY_TYPE)),
Value::from(PodType::Empty),
)
}
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 = CalculateIdGadget {
params: self.params.clone(),
}
.eval(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 })
}
}
pub struct EmptyPodVerifyTarget {
vds_root: HashOutTarget,
}
impl EmptyPodVerifyTarget {
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)?)
}
}
#[derive(Clone, Debug)]
pub struct EmptyPod {
params: Params,
id: PodId,
vds_root: Hash,
proof: Proof,
}
type CircuitData = circuit_data::CircuitData<F, C, D>;
static STANDARD_EMPTY_POD_DATA: LazyLock<(EmptyPodVerifyTarget, CircuitData)> =
LazyLock::new(|| build().expect("successful build"));
fn build() -> Result<(EmptyPodVerifyTarget, CircuitData)> {
let params = &*DEFAULT_PARAMS;
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let empty_pod_verify_target = EmptyPodVerifyCircuit {
params: params.clone(),
}
.eval(&mut builder)?;
let circuit_data = &*STANDARD_REC_MAIN_POD_CIRCUIT_DATA;
pad_circuit(&mut builder, &circuit_data.common);
let data = timed!("EmptyPod build", builder.build::<C>());
assert_eq!(circuit_data.common, data.common);
Ok((empty_pod_verify_target, data))
}
static EMPTY_POD_CACHE: LazyLock<Mutex<HashMap<Hash, EmptyPod>>> =
LazyLock::new(|| Mutex::new(HashMap::new()));
impl EmptyPod {
pub fn _prove(params: &Params, vds_root: Hash) -> Result<EmptyPod> {
let (empty_pod_verify_target, data) = &*STANDARD_EMPTY_POD_DATA;
let mut pw = PartialWitness::<F>::new();
empty_pod_verify_target.set_targets(&mut pw, vds_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]]));
Ok(EmptyPod {
params: params.clone(),
id,
vds_root,
proof: proof.proof,
})
}
pub fn new_boxed(params: &Params, vds_root: Hash) -> Box<dyn RecursivePod> {
let default_params = &*DEFAULT_PARAMS;
assert_eq!(default_params.id_params(), params.id_params());
let empty_pod = EMPTY_POD_CACHE
.lock()
.unwrap()
.entry(vds_root)
.or_insert_with(|| Self::_prove(params, vds_root).expect("prove EmptyPod"))
.clone();
Box::new(empty_pod)
}
fn _verify(&self) -> Result<()> {
let statements = self
.pub_self_statements()
.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 public_inputs = id
.to_fields(&self.params)
.iter()
.chain(EMPTY_HASH.0.iter()) // slot for the unused vds root
.cloned()
.collect_vec();
let (_, data) = &*STANDARD_EMPTY_POD_DATA;
data.verify(ProofWithPublicInputs {
proof: self.proof.clone(),
public_inputs,
})
.map_err(|e| Error::custom(format!("EmptyPod proof verification failure: {:?}", e)))
}
}
impl Pod for EmptyPod {
fn params(&self) -> &Params {
&self.params
}
fn verify(&self) -> Result<(), Box<DynError>> {
Ok(self._verify()?)
}
fn id(&self) -> PodId {
self.id
}
fn pub_self_statements(&self) -> Vec<middleware::Statement> {
vec![type_statement()]
}
fn serialized_proof(&self) -> String {
let mut buffer = Vec::new();
use plonky2::util::serialization::Write;
buffer.write_proof(&self.proof).unwrap();
BASE64_STANDARD.encode(buffer)
}
}
impl RecursivePod for EmptyPod {
fn verifier_data(&self) -> VerifierOnlyCircuitData {
let (_, data) = &*STANDARD_EMPTY_POD_DATA;
data.verifier_only.clone()
}
fn proof(&self) -> Proof {
self.proof.clone()
}
fn vds_root(&self) -> Hash {
self.vds_root
}
}
#[cfg(test)]
pub mod tests {
use super::*;
#[test]
fn test_empty_pod() {
let params = Params::default();
let empty_pod = EmptyPod::new_boxed(&params, EMPTY_HASH);
empty_pod.verify().unwrap();
}
}

View file

@ -7,31 +7,29 @@ use itertools::Itertools;
pub use operation::*; pub use operation::*;
use plonky2::{ use plonky2::{
hash::poseidon::PoseidonHash, hash::poseidon::PoseidonHash,
iop::witness::PartialWitness, plonk::{circuit_data::CommonCircuitData, config::Hasher},
plonk::{
circuit_builder::CircuitBuilder,
circuit_data::{CircuitConfig, CommonCircuitData},
config::Hasher,
proof::{Proof, ProofWithPublicInputs},
},
util::serialization::{Buffer, Read}, util::serialization::{Buffer, Read},
}; };
pub use statement::*; pub use statement::*;
use crate::{ use crate::{
backends::plonky2::{ backends::plonky2::{
basetypes::{C, D}, basetypes::{Proof, ProofWithPublicInputs, VerifierOnlyCircuitData, D},
circuits::mainpod::{ circuits::mainpod::{
CustomPredicateVerification, MainPodVerifyCircuit, MainPodVerifyInput, CustomPredicateVerification, MainPodVerifyInput, MainPodVerifyTarget, NUM_PUBLIC_INPUTS,
}, },
emptypod::EmptyPod,
error::{Error, Result}, error::{Error, Result},
mock::emptypod::MockEmptyPod,
primitives::merkletree::MerkleClaimAndProof, primitives::merkletree::MerkleClaimAndProof,
recursion::{self, RecursiveCircuit, RecursiveParams},
signedpod::SignedPod, signedpod::SignedPod,
STANDARD_REC_MAIN_POD_CIRCUIT_DATA,
}, },
middleware::{ middleware::{
self, resolve_wildcard_values, AnchoredKey, CustomPredicateBatch, DynError, Hash, self, resolve_wildcard_values, AnchoredKey, CustomPredicateBatch, DynError, Hash,
MainPodInputs, NativeOperation, NonePod, OperationType, Params, Pod, PodId, PodProver, MainPodInputs, NativeOperation, NonePod, OperationType, Params, Pod, PodId, PodProver,
PodType, StatementArg, ToFields, F, KEY_TYPE, SELF, PodType, RecursivePod, StatementArg, ToFields, F, KEY_TYPE, SELF,
}, },
}; };
@ -42,11 +40,8 @@ use crate::{
/// with a precomputed constant corresponding to the front-padding part: /// with a precomputed constant corresponding to the front-padding part:
/// `id = hash(serialize(reverse(statements || none-statements)))` /// `id = hash(serialize(reverse(statements || none-statements)))`
pub(crate) fn calculate_id(statements: &[Statement], params: &Params) -> middleware::Hash { pub(crate) fn calculate_id(statements: &[Statement], params: &Params) -> middleware::Hash {
assert_eq!(params.max_public_statements, statements.len()); assert!(statements.len() <= params.num_public_statements_id);
assert!(params.max_public_statements <= params.num_public_statements_id); assert!(params.max_public_statements <= params.num_public_statements_id);
statements
.iter()
.for_each(|st| assert_eq!(params.max_statement_args, st.1.len()));
let mut none_st: Statement = middleware::Statement::None.into(); let mut none_st: Statement = middleware::Statement::None.into();
pad_statement(params, &mut none_st); pad_statement(params, &mut none_st);
@ -250,7 +245,11 @@ fn pad_operation_args(params: &Params, args: &mut Vec<OperationArg>) {
/// Returns the statements from the given MainPodInputs, padding to the /// Returns the statements from the given MainPodInputs, padding to the
/// respective max lengths defined at the given Params. /// respective max lengths defined at the given Params.
pub(crate) fn layout_statements(params: &Params, inputs: &MainPodInputs) -> Vec<Statement> { pub(crate) fn layout_statements(
params: &Params,
mock: bool,
inputs: &MainPodInputs,
) -> Result<Vec<Statement>> {
let mut statements = Vec::new(); let mut statements = Vec::new();
// Statement at index 0 is always None to be used for padding operation arguments in custom // Statement at index 0 is always None to be used for padding operation arguments in custom
@ -258,6 +257,8 @@ pub(crate) fn layout_statements(params: &Params, inputs: &MainPodInputs) -> Vec<
statements.push(middleware::Statement::None.into()); statements.push(middleware::Statement::None.into());
// Input signed pods region // Input signed pods region
// TODO: Replace this with a dumb signed pod
// https://github.com/0xPARC/pod2/issues/246
let none_sig_pod_box: Box<dyn Pod> = Box::new(NonePod {}); let none_sig_pod_box: Box<dyn Pod> = Box::new(NonePod {});
let none_sig_pod = none_sig_pod_box.as_ref(); let none_sig_pod = none_sig_pod_box.as_ref();
assert!(inputs.signed_pods.len() <= params.max_input_signed_pods); assert!(inputs.signed_pods.len() <= params.max_input_signed_pods);
@ -277,14 +278,20 @@ pub(crate) fn layout_statements(params: &Params, inputs: &MainPodInputs) -> Vec<
} }
// Input main pods region // Input main pods region
let none_main_pod_box: Box<dyn Pod> = Box::new(NonePod {}); let empty_pod_box: Box<dyn RecursivePod> =
let none_main_pod = none_main_pod_box.as_ref(); if mock || inputs.recursive_pods.len() == params.max_input_recursive_pods {
assert!(inputs.main_pods.len() <= params.max_input_main_pods); // We mocking or we don't need padding so we skip creating an EmptyPod
for i in 0..params.max_input_main_pods { MockEmptyPod::new_boxed(params)
let pod = inputs.main_pods.get(i).copied().unwrap_or(none_main_pod); } else {
EmptyPod::new_boxed(params, inputs.vds_root)
};
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);
let sts = pod.pub_statements(); let sts = pod.pub_statements();
assert!(sts.len() <= params.max_public_statements); assert!(sts.len() <= params.max_public_statements);
for j in 0..params.max_public_statements { for j in 0..params.max_input_pods_public_statements {
let mut st = sts let mut st = sts
.get(j) .get(j)
.unwrap_or(&middleware::Statement::None) .unwrap_or(&middleware::Statement::None)
@ -329,7 +336,7 @@ pub(crate) fn layout_statements(params: &Params, inputs: &MainPodInputs) -> Vec<
statements.push(st); statements.push(st);
} }
statements Ok(statements)
} }
pub(crate) fn process_private_statements_operations( pub(crate) fn process_private_statements_operations(
@ -399,15 +406,25 @@ pub(crate) fn process_public_statements_operations(
pub struct Prover {} pub struct Prover {}
impl Prover { impl Prover {
fn _prove(&mut self, params: &Params, inputs: MainPodInputs) -> Result<MainPod> { fn _prove(&self, params: &Params, inputs: MainPodInputs) -> Result<MainPod> {
let config = CircuitConfig::standard_recursion_config(); let rec_circuit_data = &*STANDARD_REC_MAIN_POD_CIRCUIT_DATA;
let mut builder = CircuitBuilder::<F, D>::new(config); let (main_pod_target, circuit_data) =
let main_pod = MainPodVerifyCircuit { RecursiveCircuit::<MainPodVerifyTarget>::circuit_data_padded(
params: params.clone(), params.max_input_recursive_pods,
} &rec_circuit_data.common,
.eval(&mut builder)?; params,
)?;
let rec_params = RecursiveParams {
arity: params.max_input_recursive_pods,
common_data: circuit_data.common.clone(),
verifier_data: circuit_data.verifier_data(),
};
let main_pod = RecursiveCircuit {
params: rec_params,
prover: circuit_data.prover_data(),
target: main_pod_target,
};
let mut pw = PartialWitness::<F>::new();
let signed_pods_input: Vec<SignedPod> = inputs let signed_pods_input: Vec<SignedPod> = inputs
.signed_pods .signed_pods
.iter() .iter()
@ -419,6 +436,33 @@ impl Prover {
}) })
.collect_vec(); .collect_vec();
// Pad input recursive pods with empty pods if necessary
let empty_pod = if inputs.recursive_pods.len() == params.max_input_recursive_pods {
// We don't need padding so we skip creating an EmptyPod
MockEmptyPod::new_boxed(params)
} else {
EmptyPod::new_boxed(params, inputs.vds_root)
};
let inputs = MainPodInputs {
recursive_pods: &inputs
.recursive_pods
.iter()
.copied()
.chain(iter::repeat(&*empty_pod))
.take(params.max_input_recursive_pods)
.collect_vec(),
..inputs
};
let recursive_pods_pub_self_statements = inputs
.recursive_pods
.iter()
.map(|pod| {
assert_eq!(params.id_params(), pod.params().id_params());
pod.pub_self_statements()
})
.collect_vec();
let merkle_proofs = extract_merkle_proofs(params, inputs.operations)?; let merkle_proofs = extract_merkle_proofs(params, inputs.operations)?;
let custom_predicate_batches = extract_custom_predicate_batches(params, inputs.operations)?; let custom_predicate_batches = extract_custom_predicate_batches(params, inputs.operations)?;
let custom_predicate_verifications = extract_custom_predicate_verifications( let custom_predicate_verifications = extract_custom_predicate_verifications(
@ -427,7 +471,7 @@ impl Prover {
&custom_predicate_batches, &custom_predicate_batches,
)?; )?;
let statements = layout_statements(params, &inputs); let statements = layout_statements(params, false, &inputs)?;
let operations = process_private_statements_operations( let operations = process_private_statements_operations(
params, params,
&statements, &statements,
@ -442,23 +486,38 @@ impl Prover {
// get the id out of the public statements // get the id out of the public statements
let id: PodId = PodId(calculate_id(&public_statements, params)); let id: PodId = PodId(calculate_id(&public_statements, params));
let proofs = inputs
.recursive_pods
.iter()
.map(|pod| {
assert_eq!(inputs.vds_root, pod.vds_root());
ProofWithPublicInputs {
proof: pod.proof(),
public_inputs: [pod.id().0 .0, inputs.vds_root.0].concat(),
}
})
.collect_vec();
let verifier_datas = inputs
.recursive_pods
.iter()
.map(|pod| pod.verifier_data())
.collect_vec();
let input = MainPodVerifyInput { let input = MainPodVerifyInput {
vds_root: inputs.vds_root,
signed_pods: signed_pods_input, signed_pods: signed_pods_input,
recursive_pods_pub_self_statements,
statements: statements[statements.len() - params.max_statements..].to_vec(), statements: statements[statements.len() - params.max_statements..].to_vec(),
operations, operations,
merkle_proofs, merkle_proofs,
custom_predicate_batches, custom_predicate_batches,
custom_predicate_verifications, custom_predicate_verifications,
}; };
main_pod.set_targets(&mut pw, &input)?; let proof_with_pis = main_pod.prove(&input, proofs, verifier_datas)?;
// generate & verify proof
let data = builder.build::<C>();
let proof_with_pis = data.prove(pw)?;
Ok(MainPod { Ok(MainPod {
params: params.clone(), params: params.clone(),
id, id,
vds_root: inputs.vds_root,
public_statements, public_statements,
proof: proof_with_pis.proof, proof: proof_with_pis.proof,
}) })
@ -467,41 +526,21 @@ impl Prover {
impl PodProver for Prover { impl PodProver for Prover {
fn prove( fn prove(
&mut self, &self,
params: &Params, params: &Params,
inputs: MainPodInputs, inputs: MainPodInputs,
) -> Result<Box<dyn Pod>, Box<DynError>> { ) -> Result<Box<dyn RecursivePod>, Box<DynError>> {
Ok(self._prove(params, inputs).map(Box::new)?) Ok(self._prove(params, inputs).map(Box::new)?)
} }
} }
pub type MainPodProof = Proof<F, C, D>;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct MainPod { pub struct MainPod {
params: Params, params: Params,
id: PodId, id: PodId,
vds_root: Hash,
public_statements: Vec<Statement>, public_statements: Vec<Statement>,
proof: MainPodProof, proof: Proof,
}
/// Convert a Statement into middleware::Statement and replace references to SELF by `self_id`.
pub(crate) fn normalize_statement(statement: &Statement, self_id: PodId) -> middleware::Statement {
Statement(
statement.0.clone(),
statement
.1
.iter()
.map(|sa| match &sa {
StatementArg::Key(AnchoredKey { pod_id, key }) if *pod_id == SELF => {
StatementArg::Key(AnchoredKey::new(self_id, key.clone()))
}
_ => sa.clone(),
})
.collect(),
)
.try_into()
.unwrap()
} }
// This is a helper function to get the CommonCircuitData necessary to decode // This is a helper function to get the CommonCircuitData necessary to decode
@ -509,66 +548,76 @@ pub(crate) fn normalize_statement(statement: &Statement, self_id: PodId) -> midd
// as a constant or with static initialization, but in the meantime we can // as a constant or with static initialization, but in the meantime we can
// generate it on-demand. // generate it on-demand.
fn get_common_data(params: &Params) -> Result<CommonCircuitData<F, D>, Error> { fn get_common_data(params: &Params) -> Result<CommonCircuitData<F, D>, Error> {
let config = CircuitConfig::standard_recursion_config(); // TODO: Cache this somehow
let mut builder = CircuitBuilder::<F, D>::new(config); // https://github.com/0xPARC/pod2/issues/247
let _main_pod = MainPodVerifyCircuit { let rec_params = recursion::new_params::<MainPodVerifyTarget>(
params: params.clone(), params.max_input_recursive_pods,
} NUM_PUBLIC_INPUTS,
.eval(&mut builder) params,
.map_err(|e| Error::custom(format!("Failed to evaluate MainPodVerifyCircuit: {}", e)))?; )?;
Ok(rec_params.common_data().clone())
let data = builder.build::<C>();
Ok(data.common)
} }
impl MainPod { impl MainPod {
fn _verify(&self) -> Result<()> { fn _verify(&self) -> Result<()> {
// 2. get the id out of the public statements // 2. get the id out of the public statements
let id: PodId = PodId(calculate_id(&self.public_statements, &self.params)); let id = PodId(calculate_id(&self.public_statements, &self.params));
if id != self.id { if id != self.id {
return Err(Error::id_not_equal(self.id, id)); return Err(Error::id_not_equal(self.id, id));
} }
// 1, 3, 4, 5 verification via the zkSNARK proof // 1, 3, 4, 5 verification via the zkSNARK proof
let rec_circuit_data = &*STANDARD_REC_MAIN_POD_CIRCUIT_DATA;
// TODO: cache these artefacts // TODO: cache these artefacts
let config = CircuitConfig::standard_recursion_config(); // https://github.com/0xPARC/pod2/issues/247
let mut builder = CircuitBuilder::<F, D>::new(config); let (_, circuit_data) = RecursiveCircuit::<MainPodVerifyTarget>::circuit_data_padded(
let _main_pod = MainPodVerifyCircuit { self.params.max_input_recursive_pods,
params: self.params.clone(), &rec_circuit_data.common,
} &self.params,
.eval(&mut builder)?; )?;
let public_inputs = id
let data = builder.build::<C>(); .to_fields(&self.params)
data.verify(ProofWithPublicInputs { .iter()
proof: self.proof.clone(), .chain(self.vds_root.0.iter())
public_inputs: id.to_fields(&self.params), .cloned()
}) .collect_vec();
.map_err(|e| Error::custom(format!("MainPod proof verification failure: {:?}", e))) circuit_data
.verify(ProofWithPublicInputs {
proof: self.proof.clone(),
public_inputs,
})
.map_err(|e| Error::custom(format!("MainPod proof verification failure: {:?}", e)))
} }
pub fn proof(&self) -> MainPodProof { pub fn proof(&self) -> Proof {
self.proof.clone() self.proof.clone()
} }
pub fn vds_root(&self) -> Hash {
self.vds_root
}
pub fn params(&self) -> &Params { pub fn params(&self) -> &Params {
&self.params &self.params
} }
pub(crate) fn new( pub(crate) fn new(
proof: MainPodProof, proof: Proof,
public_statements: Vec<Statement>, public_statements: Vec<Statement>,
id: PodId, id: PodId,
vds_root: Hash,
params: Params, params: Params,
) -> Self { ) -> Self {
Self { Self {
params, params,
id, id,
vds_root,
public_statements, public_statements,
proof, proof,
} }
} }
pub fn decode_proof(proof: &str, params: &Params) -> Result<MainPodProof, Error> { pub fn decode_proof(proof: &str, params: &Params) -> Result<Proof, Error> {
let decoded = BASE64_STANDARD.decode(proof).map_err(|e| { let decoded = BASE64_STANDARD.decode(proof).map_err(|e| {
Error::custom(format!( Error::custom(format!(
"Failed to decode proof from base64: {}. Value: {}", "Failed to decode proof from base64: {}. Value: {}",
@ -590,6 +639,9 @@ impl MainPod {
} }
impl Pod for MainPod { impl Pod for MainPod {
fn params(&self) -> &Params {
&self.params
}
fn verify(&self) -> Result<(), Box<DynError>> { fn verify(&self) -> Result<(), Box<DynError>> {
Ok(self._verify()?) Ok(self._verify()?)
} }
@ -598,12 +650,11 @@ impl Pod for MainPod {
self.id self.id
} }
fn pub_statements(&self) -> Vec<middleware::Statement> { fn pub_self_statements(&self) -> Vec<middleware::Statement> {
// return the public statements, where when origin=SELF is replaced by origin=self.id()
self.public_statements self.public_statements
.iter() .iter()
.cloned() .cloned()
.map(|statement| normalize_statement(&statement, self.id())) .map(|st| st.try_into().expect("valid statement"))
.collect() .collect()
} }
@ -615,6 +666,19 @@ impl Pod for MainPod {
} }
} }
impl RecursivePod for MainPod {
fn verifier_data(&self) -> VerifierOnlyCircuitData {
let data = &*STANDARD_REC_MAIN_POD_CIRCUIT_DATA;
data.verifier_only.clone()
}
fn proof(&self) -> Proof {
self.proof.clone()
}
fn vds_root(&self) -> Hash {
self.vds_root
}
}
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;
@ -642,7 +706,7 @@ pub mod tests {
let params = middleware::Params { let params = middleware::Params {
// Currently the circuit uses random access that only supports vectors of length 64. // Currently the circuit uses random access that only supports vectors of length 64.
// With max_input_main_pods=3 we need random access to a vector of length 73. // With max_input_main_pods=3 we need random access to a vector of length 73.
max_input_main_pods: 0, max_input_recursive_pods: 0,
max_custom_predicate_batches: 0, max_custom_predicate_batches: 0,
max_custom_predicate_verifications: 0, max_custom_predicate_verifications: 0,
..Default::default() ..Default::default()
@ -672,10 +736,11 @@ pub mod tests {
fn test_mini_0() { fn test_mini_0() {
let params = middleware::Params { let params = middleware::Params {
max_input_signed_pods: 1, max_input_signed_pods: 1,
max_input_main_pods: 1, max_input_recursive_pods: 1,
max_signed_pod_values: 6, max_signed_pod_values: 6,
max_statements: 8, max_statements: 8,
max_public_statements: 4, max_public_statements: 4,
max_input_pods_public_statements: 10,
..Default::default() ..Default::default()
}; };
@ -715,7 +780,8 @@ pub mod tests {
fn test_mainpod_small_empty() { fn test_mainpod_small_empty() {
let params = middleware::Params { let params = middleware::Params {
max_input_signed_pods: 0, max_input_signed_pods: 0,
max_input_main_pods: 0, max_input_recursive_pods: 0,
max_input_pods_public_statements: 2,
max_statements: 5, max_statements: 5,
max_signed_pod_values: 2, max_signed_pod_values: 2,
max_public_statements: 2, max_public_statements: 2,
@ -753,14 +819,11 @@ pub mod tests {
fn test_main_ethdos() -> frontend::Result<()> { fn test_main_ethdos() -> frontend::Result<()> {
let params = Params { let params = Params {
max_input_signed_pods: 2, max_input_signed_pods: 2,
max_input_main_pods: 1, max_input_recursive_pods: 1,
max_statements: 26, max_statements: 26,
max_public_statements: 5, max_public_statements: 5,
max_signed_pod_values: 8, max_signed_pod_values: 8,
max_statement_args: 3, max_operation_args: 5,
max_operation_args: 4,
max_custom_predicate_arity: 4,
max_custom_batch_size: 3,
max_custom_predicate_wildcards: 6, max_custom_predicate_wildcards: 6,
max_custom_predicate_verifications: 8, max_custom_predicate_verifications: 8,
..Default::default() ..Default::default()
@ -805,7 +868,7 @@ pub mod tests {
fn test_main_mini_custom_1() -> frontend::Result<()> { fn test_main_mini_custom_1() -> frontend::Result<()> {
let params = Params { let params = Params {
max_input_signed_pods: 0, max_input_signed_pods: 0,
max_input_main_pods: 0, max_input_recursive_pods: 0,
max_statements: 9, max_statements: 9,
max_public_statements: 4, max_public_statements: 4,
max_statement_args: 3, max_statement_args: 3,

View file

@ -1,4 +1,4 @@
use std::fmt; use std::{fmt, iter};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -28,9 +28,15 @@ impl Statement {
} }
impl ToFields for Statement { impl ToFields for Statement {
fn to_fields(&self, _params: &Params) -> Vec<middleware::F> { fn to_fields(&self, params: &Params) -> Vec<middleware::F> {
let mut fields = self.0.to_fields(_params); let mut fields = self.0.to_fields(params);
fields.extend(self.1.iter().flat_map(|arg| arg.to_fields(_params))); fields.extend(
self.1
.iter()
.chain(iter::repeat(&StatementArg::None))
.take(params.max_statement_args)
.flat_map(|arg| arg.to_fields(params)),
);
fields fields
} }
} }

View file

@ -0,0 +1,93 @@
use itertools::Itertools;
use crate::{
backends::plonky2::{
basetypes::{Proof, VerifierOnlyCircuitData},
error::{Error, Result},
mainpod::{self, calculate_id},
},
middleware::{
AnchoredKey, DynError, Hash, Params, Pod, PodId, PodType, RecursivePod, Statement, Value,
KEY_TYPE, SELF,
},
};
#[derive(Clone, Debug)]
pub struct MockEmptyPod {
params: Params,
id: PodId,
}
fn type_statement() -> Statement {
Statement::ValueOf(
AnchoredKey::from((SELF, KEY_TYPE)),
Value::from(PodType::Empty),
)
}
impl MockEmptyPod {
pub fn new_boxed(params: &Params) -> Box<dyn RecursivePod> {
let statements = [mainpod::Statement::from(type_statement())];
let id = PodId(calculate_id(&statements, params));
Box::new(Self {
params: params.clone(),
id,
})
}
fn _verify(&self) -> Result<()> {
let statements = self
.pub_self_statements()
.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));
}
Ok(())
}
}
impl Pod for MockEmptyPod {
fn params(&self) -> &Params {
&self.params
}
fn verify(&self) -> Result<(), Box<DynError>> {
Ok(self._verify()?)
}
fn id(&self) -> PodId {
self.id
}
fn pub_self_statements(&self) -> Vec<Statement> {
vec![type_statement()]
}
fn serialized_proof(&self) -> String {
todo!()
}
}
impl RecursivePod for MockEmptyPod {
fn verifier_data(&self) -> VerifierOnlyCircuitData {
panic!("MockEmptyPod can't be verified in a recursive MainPod circuit");
}
fn proof(&self) -> Proof {
panic!("MockEmptyPod can't be verified in a recursive MainPod circuit");
}
fn vds_root(&self) -> Hash {
panic!("MockEmptyPod can't be verified in a recursive MainPod circuit");
}
}
#[cfg(test)]
pub mod tests {
use super::*;
#[test]
fn test_mock_empty_pod() {
let params = Params::default();
let empty_pod = MockEmptyPod::new_boxed(&params);
empty_pod.verify().unwrap();
}
}

View file

@ -9,17 +9,18 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
backends::plonky2::{ backends::plonky2::{
basetypes::{Proof, VerifierOnlyCircuitData},
error::{Error, Result}, error::{Error, Result},
mainpod::{ mainpod::{
calculate_id, extract_merkle_proofs, layout_statements, normalize_statement, calculate_id, extract_merkle_proofs, layout_statements,
process_private_statements_operations, process_public_statements_operations, Operation, process_private_statements_operations, process_public_statements_operations, Operation,
Statement, Statement,
}, },
primitives::merkletree::MerkleClaimAndProof, primitives::merkletree::MerkleClaimAndProof,
}, },
middleware::{ middleware::{
self, hash_str, AnchoredKey, DynError, MainPodInputs, NativePredicate, Params, Pod, PodId, self, hash_str, AnchoredKey, DynError, Hash, MainPodInputs, NativePredicate, Params, Pod,
PodProver, Predicate, StatementArg, KEY_TYPE, SELF, PodId, PodProver, Predicate, RecursivePod, StatementArg, KEY_TYPE, SELF,
}, },
}; };
@ -27,10 +28,10 @@ pub struct MockProver {}
impl PodProver for MockProver { impl PodProver for MockProver {
fn prove( fn prove(
&mut self, &self,
params: &Params, params: &Params,
inputs: MainPodInputs, inputs: MainPodInputs,
) -> Result<Box<dyn Pod>, Box<DynError>> { ) -> Result<Box<dyn RecursivePod>, Box<DynError>> {
Ok(Box::new(MockMainPod::new(params, inputs)?)) Ok(Box::new(MockMainPod::new(params, inputs)?))
} }
} }
@ -73,7 +74,8 @@ impl fmt::Display for MockMainPod {
} }
if (i >= offset_input_main_pods) if (i >= offset_input_main_pods)
&& (i < offset_input_statements) && (i < offset_input_statements)
&& ((i - offset_input_main_pods) % self.params.max_public_statements == 0) && ((i - offset_input_main_pods) % self.params.max_input_pods_public_statements
== 0)
{ {
writeln!( writeln!(
f, f,
@ -137,7 +139,7 @@ impl MockMainPod {
} }
fn offset_input_statements(&self) -> usize { fn offset_input_statements(&self) -> usize {
self.offset_input_main_pods() self.offset_input_main_pods()
+ self.params.max_input_main_pods * self.params.max_public_statements + self.params.max_input_recursive_pods * self.params.max_input_pods_public_statements
} }
fn offset_public_statements(&self) -> usize { fn offset_public_statements(&self) -> usize {
self.offset_input_statements() + self.params.max_priv_statements() self.offset_input_statements() + self.params.max_priv_statements()
@ -146,7 +148,7 @@ impl MockMainPod {
pub fn new(params: &Params, inputs: MainPodInputs) -> Result<Self> { pub fn new(params: &Params, inputs: MainPodInputs) -> Result<Self> {
// TODO: Insert a new public statement of ValueOf with `key=KEY_TYPE, // TODO: Insert a new public statement of ValueOf with `key=KEY_TYPE,
// value=PodType::MockMainPod` // value=PodType::MockMainPod`
let statements = layout_statements(params, &inputs); let statements = layout_statements(params, true, &inputs)?;
// Extract Merkle proofs and pad. // Extract Merkle proofs and pad.
let merkle_proofs = extract_merkle_proofs(params, inputs.operations)?; let merkle_proofs = extract_merkle_proofs(params, inputs.operations)?;
@ -278,20 +280,20 @@ impl MockMainPod {
} }
impl Pod for MockMainPod { impl Pod for MockMainPod {
fn params(&self) -> &Params {
&self.params
}
fn verify(&self) -> Result<(), Box<DynError>> { fn verify(&self) -> Result<(), Box<DynError>> {
Ok(self._verify()?) Ok(self._verify()?)
} }
fn id(&self) -> PodId { fn id(&self) -> PodId {
self.id self.id
} }
fn pub_statements(&self) -> Vec<middleware::Statement> { fn pub_self_statements(&self) -> Vec<middleware::Statement> {
// return the public statements, where when origin=SELF is replaced by origin=self.id() self.public_statements
// By convention we expect the KEY_TYPE to be the first statement
self.statements
.iter() .iter()
.skip(self.offset_public_statements())
.cloned() .cloned()
.map(|statement| normalize_statement(&statement, self.id())) .map(|st| st.try_into().expect("valid statement"))
.collect() .collect()
} }
@ -300,6 +302,18 @@ impl Pod for MockMainPod {
} }
} }
impl RecursivePod for MockMainPod {
fn verifier_data(&self) -> VerifierOnlyCircuitData {
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 vds_root(&self) -> Hash {
panic!("MockMainPod can't be verified in a recursive MainPod circuit");
}
}
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use std::any::Any; use std::any::Any;

View file

@ -1,2 +1,3 @@
pub mod emptypod;
pub mod mainpod; pub mod mainpod;
pub mod signedpod; pub mod signedpod;

View file

@ -10,7 +10,7 @@ use crate::{
constants::MAX_DEPTH, constants::MAX_DEPTH,
middleware::{ middleware::{
containers::Dictionary, hash_str, AnchoredKey, DynError, Hash, Key, Params, Pod, PodId, containers::Dictionary, hash_str, AnchoredKey, DynError, Hash, Key, Params, Pod, PodId,
PodSigner, PodType, RawValue, Statement, Value, KEY_SIGNER, KEY_TYPE, PodSigner, PodType, RawValue, Statement, Value, KEY_SIGNER, KEY_TYPE, SELF,
}, },
}; };
@ -111,6 +111,9 @@ impl MockSignedPod {
} }
impl Pod for MockSignedPod { impl Pod for MockSignedPod {
fn params(&self) -> &Params {
panic!("MockSignedPod doesn't have params");
}
fn verify(&self) -> Result<(), Box<DynError>> { fn verify(&self) -> Result<(), Box<DynError>> {
Ok(self._verify()?) Ok(self._verify()?)
} }
@ -119,8 +122,7 @@ impl Pod for MockSignedPod {
self.id self.id
} }
fn pub_statements(&self) -> Vec<Statement> { fn pub_self_statements(&self) -> Vec<Statement> {
let id = self.id();
// By convention we put the KEY_TYPE first and KEY_SIGNER second // By convention we put the KEY_TYPE first and KEY_SIGNER second
let mut kvs = self.kvs.clone(); let mut kvs = self.kvs.clone();
let key_type = Key::from(KEY_TYPE); let key_type = Key::from(KEY_TYPE);
@ -130,7 +132,7 @@ impl Pod for MockSignedPod {
[(key_type, value_type), (key_signer, value_signer)] [(key_type, value_type), (key_signer, value_signer)]
.into_iter() .into_iter()
.chain(kvs.into_iter().sorted_by_key(|kv| kv.0.hash())) .chain(kvs.into_iter().sorted_by_key(|kv| kv.0.hash()))
.map(|(k, v)| Statement::ValueOf(AnchoredKey::from((id, k)), v)) .map(|(k, v)| Statement::ValueOf(AnchoredKey::from((SELF, k)), v))
.collect() .collect()
} }

View file

@ -1,5 +1,6 @@
pub mod basetypes; pub mod basetypes;
pub mod circuits; pub mod circuits;
pub mod emptypod;
mod error; mod error;
pub mod mainpod; pub mod mainpod;
pub mod mock; pub mod mock;
@ -7,4 +8,32 @@ pub mod primitives;
pub mod recursion; pub mod recursion;
pub mod signedpod; pub mod signedpod;
use std::sync::LazyLock;
pub use error::*; pub use error::*;
use crate::{
backends::plonky2::{
basetypes::CircuitData,
circuits::mainpod::{MainPodVerifyTarget, NUM_PUBLIC_INPUTS},
recursion::RecursiveCircuit,
},
middleware::Params,
timed,
};
pub static DEFAULT_PARAMS: LazyLock<Params> = LazyLock::new(Params::default);
pub static STANDARD_REC_MAIN_POD_CIRCUIT_DATA: LazyLock<CircuitData> = LazyLock::new(|| {
let params = &*DEFAULT_PARAMS;
timed!(
"recursive MainPod circuit_data",
RecursiveCircuit::<MainPodVerifyTarget>::target_and_circuit_data(
params.max_input_recursive_pods,
NUM_PUBLIC_INPUTS,
params
)
.expect("calculate circuit_data")
.1
)
});

View file

@ -23,7 +23,7 @@ use plonky2::{
use crate::{ use crate::{
backends::plonky2::{ backends::plonky2::{
basetypes::{Proof, C, D}, basetypes::{C, D},
circuits::common::{CircuitBuilderPod, ValueTarget}, circuits::common::{CircuitBuilderPod, ValueTarget},
error::Result, error::Result,
primitives::signature::{ primitives::signature::{
@ -31,7 +31,7 @@ use crate::{
}, },
}, },
measure_gates_begin, measure_gates_end, measure_gates_begin, measure_gates_end,
middleware::{Hash, RawValue, EMPTY_HASH, EMPTY_VALUE, F, VALUE_SIZE}, middleware::{Hash, Proof, RawValue, EMPTY_HASH, EMPTY_VALUE, F, VALUE_SIZE},
}; };
lazy_static! { lazy_static! {

File diff suppressed because it is too large Load diff

View file

@ -1,2 +1,5 @@
pub mod circuit; pub mod circuit;
pub use circuit::{InnerCircuit, RecursiveCircuit, RecursiveParams}; pub use circuit::{
common_data_for_recursion, new_params, new_params_padded, pad_circuit, InnerCircuit,
RecursiveCircuit, RecursiveParams, VerifiedProofTarget,
};

View file

@ -15,7 +15,7 @@ use crate::{
constants::MAX_DEPTH, constants::MAX_DEPTH,
middleware::{ middleware::{
containers::Dictionary, AnchoredKey, DynError, Hash, Key, Params, Pod, PodId, PodSigner, containers::Dictionary, AnchoredKey, DynError, Hash, Key, Params, Pod, PodId, PodSigner,
PodType, RawValue, Statement, Value, KEY_SIGNER, KEY_TYPE, PodType, RawValue, Statement, Value, KEY_SIGNER, KEY_TYPE, SELF,
}, },
}; };
@ -120,6 +120,9 @@ impl SignedPod {
} }
impl Pod for SignedPod { impl Pod for SignedPod {
fn params(&self) -> &Params {
panic!("SignedPod doesn't have params");
}
fn verify(&self) -> Result<(), Box<DynError>> { fn verify(&self) -> Result<(), Box<DynError>> {
Ok(self._verify().map_err(Box::new)?) Ok(self._verify().map_err(Box::new)?)
} }
@ -128,8 +131,7 @@ impl Pod for SignedPod {
self.id self.id
} }
fn pub_statements(&self) -> Vec<Statement> { fn pub_self_statements(&self) -> Vec<Statement> {
let id = self.id();
// By convention we put the KEY_TYPE first and KEY_SIGNER second // By convention we put the KEY_TYPE first and KEY_SIGNER second
let mut kvs: HashMap<Key, Value> = self.dict.kvs().clone(); let mut kvs: HashMap<Key, Value> = self.dict.kvs().clone();
let key_type = Key::from(KEY_TYPE); let key_type = Key::from(KEY_TYPE);
@ -139,7 +141,7 @@ impl Pod for SignedPod {
[(key_type, value_type), (key_signer, value_signer)] [(key_type, value_type), (key_signer, value_signer)]
.into_iter() .into_iter()
.chain(kvs.into_iter().sorted_by_key(|kv| kv.0.hash())) .chain(kvs.into_iter().sorted_by_key(|kv| kv.0.hash()))
.map(|(k, v)| Statement::ValueOf(AnchoredKey::from((id, k)), v)) .map(|(k, v)| Statement::ValueOf(AnchoredKey::from((SELF, k)), v))
.collect() .collect()
} }

View file

@ -10,7 +10,8 @@ use serialization::{SerializedMainPod, SerializedSignedPod};
use crate::middleware::{ use crate::middleware::{
self, check_st_tmpl, hash_str, hash_values, AnchoredKey, Hash, Key, MainPodInputs, self, check_st_tmpl, hash_str, hash_values, AnchoredKey, Hash, Key, MainPodInputs,
NativeOperation, NativePredicate, OperationAux, OperationType, Params, PodId, PodProver, NativeOperation, NativePredicate, OperationAux, OperationType, Params, PodId, PodProver,
PodSigner, Predicate, Statement, StatementArg, Value, WildcardValue, KEY_TYPE, SELF, PodSigner, Predicate, Statement, StatementArg, Value, WildcardValue, EMPTY_HASH, KEY_TYPE,
SELF,
}; };
mod custom; mod custom;
@ -553,7 +554,7 @@ impl MainPodBuilder {
.iter() .iter()
.map(|p| p.pod.as_ref()) .map(|p| p.pod.as_ref())
.collect_vec(), .collect_vec(),
main_pods: &self recursive_pods: &self
.input_main_pods .input_main_pods
.iter() .iter()
.map(|p| p.pod.as_ref()) .map(|p| p.pod.as_ref())
@ -561,6 +562,7 @@ impl MainPodBuilder {
statements: &statements, statements: &statements,
operations: &operations, operations: &operations,
public_statements: &public_statements, public_statements: &public_statements,
vds_root: EMPTY_HASH, // TODO https://github.com/0xPARC/pod2/issues/249
}; };
let pod = prover.prove(&self.params, inputs)?; let pod = prover.prove(&self.params, inputs)?;
@ -618,7 +620,7 @@ impl MainPodBuilder {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(try_from = "SerializedMainPod", into = "SerializedMainPod")] #[serde(try_from = "SerializedMainPod", into = "SerializedMainPod")]
pub struct MainPod { pub struct MainPod {
pub pod: Box<dyn middleware::Pod>, pub pod: Box<dyn middleware::RecursivePod>,
pub public_statements: Vec<Statement>, pub public_statements: Vec<Statement>,
pub params: Params, pub params: Params,
} }
@ -898,7 +900,7 @@ pub mod tests {
fn test_ethdos() -> Result<()> { fn test_ethdos() -> Result<()> {
let params = Params { let params = Params {
max_input_signed_pods: 3, max_input_signed_pods: 3,
max_input_main_pods: 3, max_input_recursive_pods: 3,
max_statements: 31, max_statements: 31,
max_signed_pod_values: 8, max_signed_pod_values: 8,
max_public_statements: 10, max_public_statements: 10,

View file

@ -12,8 +12,8 @@ use crate::{
}, },
frontend::{MainPod, SignedPod}, frontend::{MainPod, SignedPod},
middleware::{ middleware::{
self, containers::Dictionary, serialization::ordered_map, AnchoredKey, Key, Params, PodId, self, containers::Dictionary, serialization::ordered_map, AnchoredKey, Hash, Key, Params,
Statement, StatementArg, Value, SELF, PodId, Statement, StatementArg, Value, EMPTY_HASH, SELF,
}, },
}; };
@ -45,6 +45,7 @@ pub enum MainPodType {
#[schemars(rename = "MainPod")] #[schemars(rename = "MainPod")]
pub struct SerializedMainPod { pub struct SerializedMainPod {
id: PodId, id: PodId,
vds_root: Hash,
public_statements: Vec<Statement>, public_statements: Vec<Statement>,
proof: String, proof: String,
params: Params, params: Params,
@ -99,23 +100,23 @@ impl From<SerializedSignedPod> for SignedPod {
impl From<MainPod> for SerializedMainPod { impl From<MainPod> for SerializedMainPod {
fn from(pod: MainPod) -> Self { fn from(pod: MainPod) -> Self {
SerializedMainPod { let (pod_type, vds_root) =
id: pod.id(), if let Some(pod) = (&*pod.pod as &dyn Any).downcast_ref::<Plonky2MainPod>() {
proof: pod.pod.serialized_proof(), (MainPodType::Main, pod.vds_root())
params: pod.params.clone(),
pod_type: if (&*pod.pod as &dyn Any)
.downcast_ref::<Plonky2MainPod>()
.is_some()
{
MainPodType::Main
} else if (&*pod.pod as &dyn Any) } else if (&*pod.pod as &dyn Any)
.downcast_ref::<MockMainPod>() .downcast_ref::<MockMainPod>()
.is_some() .is_some()
{ {
MainPodType::MockMain (MainPodType::MockMain, EMPTY_HASH)
} else { } else {
unreachable!() unreachable!()
}, };
SerializedMainPod {
id: pod.id(),
vds_root,
proof: pod.pod.serialized_proof(),
params: pod.params.clone(),
pod_type,
public_statements: pod.public_statements.clone(), public_statements: pod.public_statements.clone(),
} }
} }
@ -142,6 +143,7 @@ impl TryFrom<SerializedMainPod> for MainPod {
serialized.id, serialized.id,
), ),
serialized.id, serialized.id,
serialized.vds_root,
serialized.params.clone(), serialized.params.clone(),
)), )),
public_statements: serialized.public_statements, public_statements: serialized.public_statements,
@ -369,7 +371,7 @@ mod tests {
let params = middleware::Params { let params = middleware::Params {
// Currently the circuit uses random access that only supports vectors of length 64. // Currently the circuit uses random access that only supports vectors of length 64.
// With max_input_main_pods=3 we need random access to a vector of length 73. // With max_input_main_pods=3 we need random access to a vector of length 73.
max_input_main_pods: 1, max_input_recursive_pods: 1,
..Default::default() ..Default::default()
}; };
@ -420,7 +422,7 @@ mod tests {
fn build_ethdos_pod() -> Result<MainPod> { fn build_ethdos_pod() -> Result<MainPod> {
let params = Params { let params = Params {
max_input_signed_pods: 3, max_input_signed_pods: 3,
max_input_main_pods: 3, max_input_recursive_pods: 3,
max_statements: 31, max_statements: 31,
max_signed_pod_values: 8, max_signed_pod_values: 8,
max_public_statements: 10, max_public_statements: 10,

View file

@ -1,5 +1,6 @@
#![allow(clippy::get_first)] #![allow(clippy::get_first)]
#![feature(trait_upcasting)] #![feature(trait_upcasting)]
#![feature(mapped_lock_guards)]
pub mod backends; pub mod backends;
pub mod constants; pub mod constants;
@ -8,3 +9,30 @@ pub mod middleware;
#[cfg(test)] #[cfg(test)]
pub mod examples; pub mod examples;
#[cfg(feature = "time")]
pub mod time_macros {
#[macro_export]
macro_rules! timed {
($ctx:expr, $exp:expr) => {{
let start = std::time::Instant::now();
let res = $exp;
println!(
"timed \"{}\": {:?}",
$ctx,
std::time::Instant::now() - start
);
res
}};
}
}
#[cfg(not(feature = "time"))]
pub mod time_macros {
#[macro_export]
macro_rules! timed {
($ctx:expr, $exp:expr) => {{
$exp
}};
}
}

View file

@ -1,45 +1,30 @@
// TODO: Update this doc //! This file exposes the imported backend dependent basetypes as middleware types, taking them
//! This file exposes the backend dependent basetypes as middleware types, //! from the feature-enabled backend.
//! taking them from the feature-enabled backend.
//! //!
//! This is done in order to avoid inconsistencies where a type or parameter is //! This is done in order to avoid inconsistencies where a type or parameter is defined in the
//! defined in the middleware to have certain carachteristic and later in the //! middleware to have certain carachteristic and later in the backend it gets used differently.
//! backend it gets used differently. The idea is that those types and //! The idea is that those types and parameters (eg. lengths) have a single source of truth in the
//! parameters (eg. lengths) have a single source of truth in the code; and in //! code; and in the case of the "base types" this is determined by the backend being used under
//! the case of the "base types" this is determined by the backend being used //! the hood, not by a choice of the middleware parameters.
//! under the hood, not by a choice of the middleware parameters.
//! //!
//! The idea with this approach, is that the frontend & middleware should not //! The idea with this approach, is that the frontend & middleware should not need to import the
//! need to import the proving library used by the backend (eg. plonky2, //! proving library used by the backend (eg. plonky2, plonky3, etc).
//! plonky3, etc).
//! //!
//! For example, the `Hash` and `Value` types are types belonging at the //! For example, the `Hash` and `Value` types are types belonging at the middleware, and is the
//! middleware, and is the middleware who reasons about them, but depending on //! middleware who reasons about them, but depending on the backend being used, the `Hash` and
//! the backend being used, the `Hash` and `Value` types will have different //! `Value` types will have different sizes. So it's the backend being used who actually defines
//! sizes. So it's the backend being used who actually defines their nature //! their nature under the hood. For example with a plonky2 backend, these types will have a length
//! under the hood. For example with a plonky2 backend, these types will have a //! of 4 field elements, whereas with a plonky3 backend they will have a length of 8 field
//! length of 4 field elements, whereas with a plonky3 backend they will have a //! eleements.
//! length of 8 field eleements.
//! //!
//! Note that his approach does not introduce new traits or abstract code, //! Note that his approach does not introduce new traits or abstract code, just makes use of rust
//! just makes use of rust features to define 'base types' that are being used //! features to define 'base types' that are being used in the middleware.
//! in the middleware.
//! //!
//! //!
//! NOTE (TMP): current implementation still uses plonky2 in the middleware for //! NOTE (TMP): current implementation still uses plonky2 in the middleware for u64/i64 to F
//! u64/i64 to F conversion. Eventually we will do those conversions through the //! conversion. Eventually we will do those conversions through the approach described in this
//! approach described in this file, removing the imports of plonky2 in the //! file, removing the imports of plonky2 in the middleware.
//! middleware.
//! TODO: Update this doc
/// Value, Hash and F are imported based on 'features'. For example by default
/// we use the 'plonky2' feature, but it could be used a 'plonky3' feature, so
/// then the Value, Hash and F types would come from the plonky3 backend.
// #[cfg(feature = "backend_plonky2")]
// pub use crate::backends::plonky2::basetypes::{
// hash_fields, hash_str, hash_value, Hash, RawValue, EMPTY_HASH, EMPTY_VALUE, F, HASH_SIZE,
// SELF_ID_HASH, VALUE_SIZE,
// };
use std::{ use std::{
cmp::{Ord, Ordering}, cmp::{Ord, Ordering},
fmt, fmt,
@ -47,10 +32,7 @@ use std::{
use hex::{FromHex, FromHexError}; use hex::{FromHex, FromHexError};
use plonky2::{ use plonky2::{
field::{ field::types::{Field, PrimeField64},
goldilocks_field::GoldilocksField,
types::{Field, PrimeField64},
},
hash::poseidon::PoseidonHash, hash::poseidon::PoseidonHash,
plonk::config::Hasher, plonk::config::Hasher,
}; };
@ -58,11 +40,14 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::serialization::*; use super::serialization::*;
// Plonky2 specific types.
// Value, Hash, F and other types are imported based on 'features'. For example by default we use
// theg'plonky2' feature, but it could be used a 'plonky3' feature, so then the Value, Hash and F
// types would come from the plonky3 backend.
#[cfg(feature = "backend_plonky2")]
pub use crate::backends::plonky2::basetypes::*;
use crate::middleware::{Params, ToFields, Value}; use crate::middleware::{Params, ToFields, Value};
/// F is the native field we use everywhere. Currently it's Goldilocks from plonky2
pub type F = GoldilocksField;
pub const HASH_SIZE: usize = 4; pub const HASH_SIZE: usize = 4;
pub const VALUE_SIZE: usize = 4; pub const VALUE_SIZE: usize = 4;

View file

@ -558,8 +558,10 @@ pub enum PodType {
None = 0, None = 0,
MockSigned = 1, MockSigned = 1,
MockMain = 2, MockMain = 2,
Signed = 3, MockEmpty = 3,
Main = 4, Signed = 4,
Main = 5,
Empty = 6,
} }
impl fmt::Display for PodType { impl fmt::Display for PodType {
@ -568,45 +570,56 @@ impl fmt::Display for PodType {
PodType::None => write!(f, "None"), PodType::None => write!(f, "None"),
PodType::MockSigned => write!(f, "MockSigned"), PodType::MockSigned => write!(f, "MockSigned"),
PodType::MockMain => write!(f, "MockMain"), PodType::MockMain => write!(f, "MockMain"),
PodType::MockEmpty => write!(f, "MockEmpty"),
PodType::Signed => write!(f, "Signed"), PodType::Signed => write!(f, "Signed"),
PodType::Main => write!(f, "Main"), PodType::Main => write!(f, "Main"),
PodType::Empty => write!(f, "Empty"),
} }
} }
} }
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Hash)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Params { pub struct Params {
pub max_input_signed_pods: usize, pub max_input_signed_pods: usize,
pub max_input_main_pods: usize, pub max_input_recursive_pods: usize,
pub max_input_pods_public_statements: usize,
pub max_statements: usize, pub max_statements: usize,
pub max_signed_pod_values: usize, pub max_signed_pod_values: usize,
pub max_public_statements: usize, pub max_public_statements: usize,
// 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,
pub max_statement_args: usize,
pub max_operation_args: usize, pub max_operation_args: usize,
// max number of custom predicates batches that a MainPod can use // max number of custom predicates batches that a MainPod can use
pub max_custom_predicate_batches: usize, pub max_custom_predicate_batches: usize,
// max number of operations using custom predicates that can be verified in the MainPod // max number of operations using custom predicates that can be verified in the MainPod
pub max_custom_predicate_verifications: usize, pub max_custom_predicate_verifications: usize,
// max number of statements that can be ANDed or ORed together
// in a custom predicate
pub max_custom_predicate_arity: usize,
pub max_custom_predicate_wildcards: usize, pub max_custom_predicate_wildcards: usize,
pub max_custom_batch_size: usize,
// maximum number of merkle proofs // maximum number of merkle proofs
pub max_merkle_proofs: usize, pub max_merkle_proofs: usize,
// maximum depth for merkle tree gadget // maximum depth for merkle tree gadget
pub max_depth_mt_gadget: usize, pub max_depth_mt_gadget: 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,
pub max_statement_args: usize,
//
// The following parameters define how a custom predicate batch id is calculated.
//
// max number of statements that can be ANDed or ORed together
// in a custom predicate
pub max_custom_predicate_arity: usize,
pub max_custom_batch_size: usize,
} }
impl Default for Params { impl Default for Params {
fn default() -> Self { fn default() -> Self {
Self { Self {
max_input_signed_pods: 3, max_input_signed_pods: 3,
max_input_main_pods: 3, max_input_recursive_pods: 2,
max_input_pods_public_statements: 10,
max_statements: 20, max_statements: 20,
max_signed_pod_values: 8, max_signed_pod_values: 8,
max_public_statements: 10, max_public_statements: 10,
@ -661,6 +674,16 @@ impl Params {
self.max_custom_batch_size * self.custom_predicate_size() self.max_custom_batch_size * self.custom_predicate_size()
} }
/// Parameters that define how the id is calculated
pub fn id_params(&self) -> Vec<usize> {
vec![
self.num_public_statements_id,
self.max_statement_args,
self.max_custom_predicate_arity,
self.max_custom_batch_size,
]
}
pub fn print_serialized_sizes(&self) { pub fn print_serialized_sizes(&self) {
println!("Parameter sizes:"); println!("Parameter sizes:");
println!( println!(
@ -678,12 +701,39 @@ impl Params {
} }
} }
/// Replace references to SELF by `self_id` in anchored keys of the statement.
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()))
}
_ => sa.clone(),
})
.collect();
Statement::from_args(predicate, args).expect("statement was valid before normalization")
}
pub type DynError = dyn std::error::Error + Send + Sync; pub type DynError = dyn std::error::Error + Send + Sync;
pub trait Pod: fmt::Debug + DynClone + Any { pub trait Pod: fmt::Debug + DynClone + Any {
fn params(&self) -> &Params;
fn verify(&self) -> Result<(), Box<DynError>>; fn verify(&self) -> Result<(), Box<DynError>>;
fn id(&self) -> PodId; fn id(&self) -> PodId;
fn pub_statements(&self) -> Vec<Statement>; /// Statements as internally generated, where self-referencing arguments use SELF in the
/// anchored key. The serialization of these statements is used to calculate the id.
fn pub_self_statements(&self) -> Vec<Statement>;
/// Normalized statements, where self-referencing arguments use the pod id instead of SELF in
/// the anchored key.
fn pub_statements(&self) -> Vec<Statement> {
self.pub_self_statements()
.into_iter()
.map(|statement| normalize_statement(&statement, self.id()))
.collect()
}
/// Extract key-values from ValueOf public statements /// Extract key-values from ValueOf public statements
fn kvs(&self) -> HashMap<AnchoredKey, Value> { fn kvs(&self) -> HashMap<AnchoredKey, Value> {
self.pub_statements() self.pub_statements()
@ -708,9 +758,21 @@ pub trait Pod: fmt::Debug + DynClone + Any {
fn serialized_proof(&self) -> String; fn serialized_proof(&self) -> String;
} }
// impl Clone for Box<dyn SignedPod> // impl Clone for Box<dyn Pod>
dyn_clone::clone_trait_object!(Pod); dyn_clone::clone_trait_object!(Pod);
/// Trait for pods that are generated with a plonky2 circuit and that can be verified by a
/// 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;
fn proof(&self) -> Proof;
fn vds_root(&self) -> Hash;
}
// impl Clone for Box<dyn RecursivePod>
dyn_clone::clone_trait_object!(RecursivePod);
pub trait PodSigner { pub trait PodSigner {
fn sign( fn sign(
&mut self, &mut self,
@ -719,19 +781,24 @@ pub trait PodSigner {
) -> Result<Box<dyn Pod>, Box<DynError>>; ) -> Result<Box<dyn Pod>, Box<DynError>>;
} }
// TODO: Delete once we have a fully working EmptyPod and a dumb SignedPod
// https://github.com/0xPARC/pod2/issues/246
/// This is a filler type that fulfills the Pod trait and always verifies. It's empty. This /// This is a filler type that fulfills the Pod trait and always verifies. It's empty. This
/// can be used to simulate padding in a circuit. /// can be used to simulate padding in a circuit.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct NonePod {} pub struct NonePod {}
impl Pod for NonePod { impl Pod for NonePod {
fn params(&self) -> &Params {
panic!("NonePod doesn't have params");
}
fn verify(&self) -> Result<(), Box<DynError>> { fn verify(&self) -> Result<(), Box<DynError>> {
Ok(()) Ok(())
} }
fn id(&self) -> PodId { fn id(&self) -> PodId {
PodId(EMPTY_HASH) PodId(EMPTY_HASH)
} }
fn pub_statements(&self) -> Vec<Statement> { fn pub_self_statements(&self) -> Vec<Statement> {
Vec::new() Vec::new()
} }
fn serialized_proof(&self) -> String { fn serialized_proof(&self) -> String {
@ -742,20 +809,21 @@ impl Pod for NonePod {
#[derive(Debug)] #[derive(Debug)]
pub struct MainPodInputs<'a> { pub struct MainPodInputs<'a> {
pub signed_pods: &'a [&'a dyn Pod], pub signed_pods: &'a [&'a dyn Pod],
pub main_pods: &'a [&'a dyn Pod], pub recursive_pods: &'a [&'a dyn RecursivePod],
pub statements: &'a [Statement], pub statements: &'a [Statement],
pub operations: &'a [Operation], pub operations: &'a [Operation],
/// Statements that need to be made public (they can come from input pods or input /// Statements that need to be made public (they can come from input pods or input
/// statements) /// statements)
pub public_statements: &'a [Statement], pub public_statements: &'a [Statement],
pub vds_root: Hash, // TODO: Figure out if we use Hash or a Map here https://github.com/0xPARC/pod2/issues/249
} }
pub trait PodProver { pub trait PodProver {
fn prove( fn prove(
&mut self, &self,
params: &Params, params: &Params,
inputs: MainPodInputs, inputs: MainPodInputs,
) -> Result<Box<dyn Pod>, Box<DynError>>; ) -> Result<Box<dyn RecursivePod>, Box<DynError>>;
} }
pub trait ToFields { pub trait ToFields {