Support mixed depth container merkle proofs (#508)
Some checks failed
Rust Build with features / Rust tests (push) Has been cancelled
Clippy Check / Rust formatting (push) Has been cancelled
Publish MainPod circuit info / Update Wiki with new MainPod circuit info (push) Has been cancelled
Check mdbook compilation / compile (push) Has been cancelled
Publish mdbook / build (push) Has been cancelled
Rustfmt Check / Rust formatting (push) Has been cancelled
Rust Tests / Rust tests (push) Has been cancelled
typos / Spell Check with Typos (push) Has been cancelled
Publish mdbook / deploy (push) Has been cancelled

* remove enabled flag from merkle tree proofs

* add small existence mpt proofs in MainPod

* refactor params, add small transition proofs

* complete

* fix edge case in vdset

* fix: use existence only proof for vdset

* use consistent order for aux table
This commit is contained in:
Eduard S. 2026-05-06 12:39:27 +02:00 committed by GitHub
parent 111b132a00
commit 5e3ac9a101
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 2366 additions and 2189 deletions

View file

@ -51,7 +51,7 @@ use crate::{
mainpod::cache_get_rec_main_pod_verifier_circuit_data,
primitives::merkletree::MerkleClaimAndProof,
},
middleware::{containers::Array, Hash, Params, RawValue, Result, Value},
middleware::{containers::Array, Hash, Params, RawValue, Result, Value, EMPTY_HASH},
};
pub static DEFAULT_VD_LIST: LazyLock<Vec<VerifierOnlyCircuitData>> = LazyLock::new(|| {
@ -95,6 +95,12 @@ impl Eq for VDSet {}
impl VDSet {
fn new_from_vds_hashes(mut vds_hashes: Vec<Hash>) -> Self {
// If vds_hashes is empty we add an zero entry to be used as padding when verifying merkle
// proofs of inclusion in the vds set. This zero entry can't be abused because no circuit
// exists with a vds_hash = 0.
if vds_hashes.is_empty() {
vds_hashes.push(EMPTY_HASH);
}
// before using the hash values, sort them, so that each set of
// verifier_datas gets the same VDSet root
vds_hashes.sort();
@ -150,6 +156,9 @@ impl VDSet {
))?
.clone())
}
pub fn get_vds_proof_0(&self) -> MerkleClaimAndProof {
self.proofs_map[&self.vds_hashes[0]].clone()
}
/// Returns true if the `verifier_data_hash` is in the set
pub fn contains(&self, verifier_data_hash: HashOut) -> bool {
self.proofs_map

View file

@ -30,7 +30,7 @@ use crate::{
mainpod::{Operation, OperationArg, OperationAux, Statement},
primitives::merkletree::{
verify_merkle_proof_circuit, MerkleClaimAndProof, MerkleClaimAndProofTarget,
MerkleProof, MerkleTreeStateTransitionProofTarget,
MerkleProof, MerkleProofExistenceTarget, MerkleTreeStateTransitionProofTarget,
},
},
middleware::{
@ -725,7 +725,6 @@ impl CustomPredicateInBatchTarget {
let mtp =
MerkleClaimAndProofTarget::new_virtual(Params::max_depth_custom_batch_mt(), builder);
let _true = builder._true();
builder.connect(_true.target, mtp.enabled.target);
builder.connect(_true.target, mtp.existence.target);
let zero = builder.constant(F(0));
let key = ValueTarget {
@ -763,7 +762,7 @@ impl CustomPredicateInBatchTarget {
value: RawValue::from(hash_fields(&predicate.to_fields())),
proof: mtp.clone(),
};
self.mtp.set_targets(pw, true, &mtp_claim)?;
self.mtp.set_targets(pw, &mtp_claim)?;
Ok(())
}
}
@ -987,7 +986,6 @@ pub trait Flattenable {
/// elsewhere.
#[derive(Copy, Clone)]
pub struct MerkleClaimTarget {
pub(crate) enabled: BoolTarget,
pub(crate) root: HashOutTarget,
pub(crate) key: ValueTarget,
pub(crate) value: ValueTarget,
@ -997,7 +995,6 @@ pub struct MerkleClaimTarget {
impl From<MerkleClaimAndProofTarget> for MerkleClaimTarget {
fn from(pf: MerkleClaimAndProofTarget) -> Self {
Self {
enabled: pf.enabled,
root: pf.root,
key: pf.key,
value: pf.value,
@ -1006,12 +1003,25 @@ impl From<MerkleClaimAndProofTarget> for MerkleClaimTarget {
}
}
impl MerkleClaimTarget {
pub fn from_proof_existence(
builder: &mut CircuitBuilder,
pf: MerkleProofExistenceTarget,
) -> Self {
Self {
root: pf.root,
key: pf.key,
value: pf.value,
existence: builder._true(),
}
}
}
/// For the purpose of op verification, we need only look up the
/// Merkle state transition claim rather than the Merkle state
/// transition proof since it is verified elsewhere.
#[derive(Copy, Clone)]
pub struct MerkleTreeStateTransitionClaimTarget {
pub(crate) enabled: BoolTarget,
pub(crate) op: Target,
pub(crate) old_root: HashOutTarget,
pub(crate) new_root: HashOutTarget,
@ -1022,7 +1032,6 @@ pub struct MerkleTreeStateTransitionClaimTarget {
impl From<MerkleTreeStateTransitionProofTarget> for MerkleTreeStateTransitionClaimTarget {
fn from(pf: MerkleTreeStateTransitionProofTarget) -> Self {
Self {
enabled: pf.enabled,
op: pf.op,
old_root: pf.old_root,
new_root: pf.new_root,
@ -1063,7 +1072,6 @@ impl Flattenable for ValueTarget {
impl Flattenable for MerkleClaimTarget {
fn flatten(&self) -> Vec<Target> {
[
vec![self.enabled.target],
self.root.elements.to_vec(),
self.key.elements.to_vec(),
self.value.elements.to_vec(),
@ -1075,31 +1083,28 @@ impl Flattenable for MerkleClaimTarget {
fn from_flattened(params: &Params, vs: &[Target]) -> Self {
assert_eq!(vs.len(), Self::size(params));
Self {
enabled: BoolTarget::new_unsafe(vs[0]),
root: HashOutTarget::from_vec(vs[1..1 + NUM_HASH_OUT_ELTS].to_vec()),
key: ValueTarget::from_slice(
&vs[1 + NUM_HASH_OUT_ELTS..1 + NUM_HASH_OUT_ELTS + VALUE_SIZE],
),
root: HashOutTarget::from_vec(vs[0..NUM_HASH_OUT_ELTS].to_vec()),
key: ValueTarget::from_slice(&vs[NUM_HASH_OUT_ELTS..NUM_HASH_OUT_ELTS + VALUE_SIZE]),
value: ValueTarget::from_slice(
&vs[1 + NUM_HASH_OUT_ELTS + VALUE_SIZE..1 + NUM_HASH_OUT_ELTS + 2 * VALUE_SIZE],
&vs[NUM_HASH_OUT_ELTS + VALUE_SIZE..NUM_HASH_OUT_ELTS + 2 * VALUE_SIZE],
),
existence: BoolTarget::new_unsafe(vs[1 + NUM_HASH_OUT_ELTS + 2 * VALUE_SIZE]),
existence: BoolTarget::new_unsafe(vs[NUM_HASH_OUT_ELTS + 2 * VALUE_SIZE]),
}
}
fn size(params: &Params) -> usize {
2 + HashOutTarget::size(params) + 2 * ValueTarget::size(params)
HashOutTarget::size(params) + 2 * ValueTarget::size(params) + 1
}
}
impl Flattenable for MerkleTreeStateTransitionClaimTarget {
fn flatten(&self) -> Vec<Target> {
[
vec![self.enabled.target, self.op],
self.old_root.elements.to_vec(),
self.new_root.elements.to_vec(),
self.op_key.elements.to_vec(),
self.op_value.elements.to_vec(),
vec![self.op],
]
.concat()
}
@ -1107,24 +1112,22 @@ impl Flattenable for MerkleTreeStateTransitionClaimTarget {
fn from_flattened(params: &Params, vs: &[Target]) -> Self {
assert_eq!(vs.len(), Self::size(params));
Self {
enabled: BoolTarget::new_unsafe(vs[0]),
op: vs[1],
old_root: HashOutTarget::from_vec(vs[2..2 + NUM_HASH_OUT_ELTS].to_vec()),
old_root: HashOutTarget::from_vec(vs[0..NUM_HASH_OUT_ELTS].to_vec()),
new_root: HashOutTarget::from_vec(
vs[2 + NUM_HASH_OUT_ELTS..2 * (1 + NUM_HASH_OUT_ELTS)].to_vec(),
vs[NUM_HASH_OUT_ELTS..2 * NUM_HASH_OUT_ELTS].to_vec(),
),
op_key: ValueTarget::from_slice(
&vs[2 * (1 + NUM_HASH_OUT_ELTS)..2 * (1 + NUM_HASH_OUT_ELTS) + VALUE_SIZE],
&vs[2 * NUM_HASH_OUT_ELTS..2 * NUM_HASH_OUT_ELTS + VALUE_SIZE],
),
op_value: ValueTarget::from_slice(
&vs[2 * (1 + NUM_HASH_OUT_ELTS) + VALUE_SIZE
..2 * (1 + NUM_HASH_OUT_ELTS) + 2 * VALUE_SIZE],
&vs[2 * NUM_HASH_OUT_ELTS + VALUE_SIZE..2 * NUM_HASH_OUT_ELTS + 2 * VALUE_SIZE],
),
op: vs[2 * NUM_HASH_OUT_ELTS + 2 * VALUE_SIZE],
}
}
fn size(params: &Params) -> usize {
2 * (1 + HashOutTarget::size(params)) + 2 * ValueTarget::size(params)
2 * HashOutTarget::size(params) + 2 * ValueTarget::size(params) + 1
}
}

File diff suppressed because it is too large Load diff

View file

@ -148,14 +148,20 @@ pub(crate) fn extract_custom_predicate_verifications(
Ok(table)
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MerkleProofs {
pub(crate) medium: Vec<MerkleClaimAndProof>,
pub(crate) small: Vec<MerkleClaimAndProof>,
}
/// Extracts Merkle proofs from Contains/NotContains ops.
pub(crate) fn extract_merkle_proofs(
params: &Params,
aux_list: &mut [OperationAux],
operations: &[middleware::Operation],
statements: &[middleware::Statement],
) -> Result<Vec<MerkleClaimAndProof>> {
let mut table = Vec::new();
) -> Result<MerkleProofs> {
let mut tables = MerkleProofs::default();
for (i, (op, st)) in operations.iter().zip(statements.iter()).enumerate() {
let deduction_err = || MiddlewareError::invalid_deduction(op.clone(), st.clone());
let (root, key, value, pf) = match (op, st) {
@ -178,31 +184,42 @@ pub(crate) fn extract_merkle_proofs(
}
_ => continue,
};
aux_list[i] = OperationAux::MerkleProofIndex(table.len());
table.push(MerkleClaimAndProof::new(
Hash::from(root),
key,
value,
pf.clone(),
));
let claim_proof = MerkleClaimAndProof::new(Hash::from(root), key, value, pf.clone());
if pf.existence
// TODO: Make sure there's no off-by-one error here
&& pf.siblings.len() <= params.containers.max_depth_small
&& tables.small.len() < params.containers.state.max_small
{
aux_list[i] = OperationAux::MerkleProofIndex(Size::Small, tables.small.len());
tables.small.push(claim_proof);
} else {
aux_list[i] = OperationAux::MerkleProofIndex(Size::Medium, tables.medium.len());
tables.medium.push(claim_proof);
}
if table.len() > params.max_merkle_proofs_containers {
}
if tables.medium.len() > params.containers.state.max_medium {
return Err(Error::custom(format!(
"The number of required Merkle proofs ({}) exceeds the maximum number ({}).",
table.len(),
params.max_merkle_proofs_containers
tables.medium.len(),
params.containers.state.max_medium
)));
}
Ok(table)
Ok(tables)
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MerkleTransitionProofs {
pub(crate) medium: Vec<MerkleTreeStateTransitionProof>,
pub(crate) small: Vec<MerkleTreeStateTransitionProof>,
}
/// Extracts Merkle state transition proofs from container update ops.
pub(crate) fn extract_merkle_tree_state_transition_proofs(
pub(crate) fn extract_merkle_transition_proofs(
params: &Params,
aux_list: &mut [OperationAux],
operations: &[middleware::Operation],
) -> Result<Vec<MerkleTreeStateTransitionProof>> {
let mut table = Vec::new();
) -> Result<MerkleTransitionProofs> {
let mut tables = MerkleTransitionProofs::default();
for (i, op) in operations.iter().enumerate() {
let pf = match op {
middleware::Operation::ContainerInsertFromEntries(_, _, _, _, pf)
@ -210,17 +227,27 @@ pub(crate) fn extract_merkle_tree_state_transition_proofs(
| middleware::Operation::ContainerDeleteFromEntries(_, _, _, pf) => pf.clone(),
_ => continue,
};
aux_list[i] = OperationAux::MerkleTreeStateTransitionProofIndex(table.len());
table.push(pf);
if pf.op_proof.existence
// TODO: Make sure there's no off-by-one error here
&& pf.siblings.len() <= params.containers.max_depth_small
&& tables.small.len() < params.containers.transition.max_small
{
aux_list[i] = OperationAux::MerkleTransitionProofIndex(Size::Small, tables.small.len());
tables.small.push(pf);
} else {
aux_list[i] =
OperationAux::MerkleTransitionProofIndex(Size::Medium, tables.medium.len());
tables.medium.push(pf);
}
if table.len() > params.max_merkle_tree_state_transition_proofs_containers {
}
if tables.medium.len() > params.containers.transition.max_medium {
return Err(Error::custom(format!(
"The number of required Merkle proofs ({}) exceeds the maximum number ({}).",
table.len(),
params.max_merkle_tree_state_transition_proofs_containers
tables.medium.len(),
params.containers.transition.max_medium
)));
}
Ok(table)
Ok(tables)
}
pub(crate) fn extract_public_key_of(
@ -513,6 +540,8 @@ impl MainPodProver for Prover {
let mut aux_list = vec![OperationAux::None; params.max_priv_statements()];
let merkle_proofs =
extract_merkle_proofs(params, &mut aux_list, inputs.operations, inputs.statements)?;
let merkle_transition_proofs =
extract_merkle_transition_proofs(params, &mut aux_list, inputs.operations)?;
let custom_predicates = extract_custom_predicates(params, inputs.operations)?;
let custom_predicate_verifications = extract_custom_predicate_verifications(
params,
@ -537,9 +566,6 @@ impl MainPodProver for Prover {
let signed_bys =
extract_signatures(params, &mut aux_list, inputs.operations, inputs.statements)?;
let merkle_tree_state_transition_proofs =
extract_merkle_tree_state_transition_proofs(params, &mut aux_list, inputs.operations)?;
let (statements, public_statements) = layout_statements(params, false, &inputs)?;
let operations = process_private_statements_operations(
params,
@ -572,20 +598,15 @@ impl MainPodProver for Prover {
.collect_vec();
let mut vd_mt_proofs = Vec::with_capacity(inputs.pods.len());
let pad_vd_mt_proof = inputs.vd_set.get_vds_proof_0();
for (pod, vd) in inputs.pods.iter().zip(&verifier_datas) {
vd_mt_proofs.push(if pod.is_main() {
(true, inputs.vd_set.get_vds_proof(vd)?)
inputs.vd_set.get_vds_proof(vd)?
} else {
// For intro pods we don't verify inclusion of their vk into the vd set, so we
// generate a dummy mt proof with expected root and value to pass some constraints
(
false,
MerkleClaimAndProof {
root: inputs.vd_set.root(),
value: RawValue::from(pod.verifier_data_hash()),
..MerkleClaimAndProof::empty()
},
)
// use a valid vds proof that matches the expected root but not the value to pass
// the constraints
pad_vd_mt_proof.clone()
});
}
@ -598,7 +619,7 @@ impl MainPodProver for Prover {
merkle_proofs,
public_key_of_sks,
signed_bys,
merkle_tree_state_transition_proofs,
merkle_transition_proofs,
custom_predicates_with_mpt_proofs,
custom_predicate_verifications,
};
@ -985,7 +1006,18 @@ pub mod tests {
max_statements: 2,
max_public_statements: 1,
max_input_pods_public_statements: 0,
max_merkle_proofs_containers: 0,
containers: middleware::ParamsContainers {
state: middleware::ParamsMerkleProofs {
max_small: 0,
max_medium: 0,
},
transition: middleware::ParamsMerkleProofs {
max_small: 0,
max_medium: 0,
},
max_depth_small: 8,
max_depth_medium: 32,
},
max_public_key_of: 0,
max_custom_predicate_verifications: 0,
max_custom_predicates: 0,
@ -1024,11 +1056,20 @@ pub mod tests {
max_custom_predicates: 2,
max_custom_predicate_verifications: 2,
max_custom_predicate_wildcards: 3,
max_merkle_proofs_containers: 2,
max_merkle_tree_state_transition_proofs_containers: 2,
max_public_key_of: 2,
max_depth_mt_containers: 4,
max_depth_mt_vds: 6,
containers: middleware::ParamsContainers {
state: middleware::ParamsMerkleProofs {
max_small: 2,
max_medium: 2,
},
transition: middleware::ParamsMerkleProofs {
max_small: 2,
max_medium: 2,
},
max_depth_small: 2,
max_depth_medium: 4,
},
};
let mut vds = DEFAULT_VD_LIST.clone();
vds.push(rec_main_pod_circuit_data(&params).1.verifier_only.clone());
@ -1087,8 +1128,18 @@ pub mod tests {
max_public_statements: 4,
max_custom_predicate_wildcards: 4,
max_custom_predicate_verifications: 2,
max_merkle_proofs_containers: 3,
max_merkle_tree_state_transition_proofs_containers: 0,
containers: middleware::ParamsContainers {
state: middleware::ParamsMerkleProofs {
max_small: 0,
max_medium: 3,
},
transition: middleware::ParamsMerkleProofs {
max_small: 0,
max_medium: 0,
},
max_depth_small: 8,
max_depth_medium: 32,
},
..Default::default()
};
println!("{:#?}", params);
@ -1156,8 +1207,18 @@ pub mod tests {
max_public_statements: 2,
max_custom_predicate_wildcards: 4,
max_custom_predicate_verifications: 2,
max_merkle_proofs_containers: 0,
max_merkle_tree_state_transition_proofs_containers: 0,
containers: middleware::ParamsContainers {
state: middleware::ParamsMerkleProofs {
max_small: 0,
max_medium: 0,
},
transition: middleware::ParamsMerkleProofs {
max_small: 0,
max_medium: 0,
},
max_depth_small: 8,
max_depth_medium: 32,
},
..Default::default()
};
let mut vds = DEFAULT_VD_LIST.clone();

View file

@ -5,8 +5,7 @@ use serde::{Deserialize, Serialize};
use crate::{
backends::plonky2::{
error::{Error, Result},
mainpod::{SignedBy, Statement},
primitives::merkletree::{MerkleClaimAndProof, MerkleTreeStateTransitionProof},
mainpod::{MerkleProofs, MerkleTransitionProofs, SignedBy, Statement},
},
middleware::{self, OperationType, Params},
};
@ -30,50 +29,89 @@ impl OperationArg {
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub enum Size {
Small,
Medium,
}
impl fmt::Display for Size {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Small => write!(f, "small"),
Self::Medium => write!(f, "medium"),
}
}
}
impl Size {
pub const fn min() -> Self {
Self::Small
}
pub const fn max() -> Self {
Self::Medium
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub enum OperationAux {
None,
MerkleProofIndex(usize),
MerkleProofIndex(Size, usize),
MerkleTransitionProofIndex(Size, usize),
PublicKeyOfIndex(usize),
SignedByIndex(usize),
MerkleTreeStateTransitionProofIndex(usize),
CustomPredVerifyIndex(usize),
}
impl OperationAux {
fn table_offset_merkle_proof(_params: &Params) -> usize {
fn table_offset_merkle_proof(params: &Params, size: Size) -> usize {
match size {
// At index 0 we store a zero entry
1
Size::Small => 1,
Size::Medium => {
Self::table_offset_merkle_proof(params, Size::Small)
+ params.containers.state.max_small
}
}
}
fn table_offset_merkle_transition_proof(params: &Params, size: Size) -> usize {
match size {
Size::Small => {
Self::table_offset_merkle_proof(params, Size::min())
+ params.containers.state.max_total()
}
Size::Medium => {
Self::table_offset_merkle_transition_proof(params, Size::Small)
+ params.containers.transition.max_small
}
}
}
fn table_offset_custom_pred_verify(params: &Params) -> usize {
Self::table_offset_merkle_transition_proof(params, Size::min())
+ params.containers.transition.max_total()
}
fn table_offset_public_key_of(params: &Params) -> usize {
Self::table_offset_merkle_proof(params) + params.max_merkle_proofs_containers
Self::table_offset_custom_pred_verify(params) + params.max_custom_predicate_verifications
}
fn table_offset_signed_by(params: &Params) -> usize {
Self::table_offset_public_key_of(params) + params.max_public_key_of
}
fn table_offset_merkle_tree_state_transition_proof(params: &Params) -> usize {
Self::table_offset_signed_by(params) + params.max_signed_by
}
fn table_offset_custom_pred_verify(params: &Params) -> usize {
Self::table_offset_merkle_tree_state_transition_proof(params)
+ params.max_merkle_tree_state_transition_proofs_containers
}
pub(crate) fn table_size(params: &Params) -> usize {
1 + params.max_merkle_proofs_containers
1 + params.containers.state.max_total()
+ params.containers.transition.max_total()
+ params.max_custom_predicate_verifications
+ params.max_public_key_of
+ params.max_signed_by
+ params.max_merkle_tree_state_transition_proofs_containers
+ params.max_custom_predicate_verifications
}
pub fn table_index(&self, params: &Params) -> usize {
match self {
Self::None => 0,
Self::MerkleProofIndex(i) => Self::table_offset_merkle_proof(params) + *i,
Self::MerkleProofIndex(size, i) => Self::table_offset_merkle_proof(params, *size) + *i,
Self::MerkleTransitionProofIndex(size, i) => {
Self::table_offset_merkle_transition_proof(params, *size) + *i
}
Self::PublicKeyOfIndex(i) => Self::table_offset_public_key_of(params) + *i,
Self::SignedByIndex(i) => Self::table_offset_signed_by(params) + *i,
Self::MerkleTreeStateTransitionProofIndex(i) => {
Self::table_offset_merkle_tree_state_transition_proof(params) + *i
}
Self::CustomPredVerifyIndex(i) => Self::table_offset_custom_pred_verify(params) + *i,
}
}
@ -96,8 +134,8 @@ impl Operation {
&self,
statements: &[Statement],
signatures: &[SignedBy],
merkle_proofs: &[MerkleClaimAndProof],
merkle_tree_state_transition_proofs: &[MerkleTreeStateTransitionProof],
merkle_proofs: &MerkleProofs,
merkle_transition_proofs: &MerkleTransitionProofs,
) -> Result<crate::middleware::Operation> {
let deref_args = self
.1
@ -113,17 +151,26 @@ impl Operation {
.collect::<Result<Vec<_>>>()?;
let deref_aux = match self.2 {
OperationAux::None => crate::middleware::OperationAux::None,
OperationAux::CustomPredVerifyIndex(_) => crate::middleware::OperationAux::None,
OperationAux::MerkleProofIndex(i) => crate::middleware::OperationAux::MerkleProof(
merkle_proofs
OperationAux::MerkleProofIndex(size, i) => {
let table = match size {
Size::Small => &merkle_proofs.small,
Size::Medium => &merkle_proofs.medium,
};
crate::middleware::OperationAux::MerkleProof(
table
.get(i)
.ok_or(Error::custom(format!("Missing Merkle proof index {}", i)))?
.proof
.clone(),
),
OperationAux::MerkleTreeStateTransitionProofIndex(i) => {
)
}
OperationAux::MerkleTransitionProofIndex(size, i) => {
let table = match size {
Size::Small => &merkle_transition_proofs.small,
Size::Medium => &merkle_transition_proofs.medium,
};
crate::middleware::OperationAux::MerkleTreeStateTransitionProof(
merkle_tree_state_transition_proofs
table
.get(i)
.ok_or(Error::custom(format!(
"Missing Merkle state transition proof index {}",
@ -132,6 +179,7 @@ impl Operation {
.clone(),
)
}
OperationAux::CustomPredVerifyIndex(_) => crate::middleware::OperationAux::None,
OperationAux::SignedByIndex(i) => crate::middleware::OperationAux::Signature(
signatures
.get(i)
@ -165,12 +213,14 @@ impl fmt::Display for Operation {
}
match self.2 {
OperationAux::None => (),
OperationAux::MerkleProofIndex(i) => write!(f, " merkle_proof_{:02}", i)?,
OperationAux::MerkleProofIndex(size, i) => {
write!(f, " {}_merkle_proof_{:02}", size, i)?
}
OperationAux::CustomPredVerifyIndex(i) => write!(f, " custom_pred_verify_{:02}", i)?,
OperationAux::PublicKeyOfIndex(i) => write!(f, " public_key_of_{:02}", i)?,
OperationAux::SignedByIndex(i) => write!(f, " signed_by_{:02}", i)?,
OperationAux::MerkleTreeStateTransitionProofIndex(i) => {
write!(f, " merkle_tree_state_transition_proof_{:02}", i)?
OperationAux::MerkleTransitionProofIndex(size, i) => {
write!(f, " {}_merkle_transition_proof_{:02}", size, i)?
}
}
Ok(())

View file

@ -11,13 +11,12 @@ use crate::{
basetypes::{Proof, VerifierOnlyCircuitData},
error::{Error, Result},
mainpod::{
calculate_statements_hash, extract_merkle_proofs,
extract_merkle_tree_state_transition_proofs, extract_signatures, layout_statements,
process_private_statements_operations, process_public_statements_operations, Operation,
calculate_statements_hash, extract_merkle_proofs, extract_merkle_transition_proofs,
extract_signatures, layout_statements, process_private_statements_operations,
process_public_statements_operations, MerkleProofs, MerkleTransitionProofs, Operation,
OperationAux, SignedBy, Statement,
},
mock::emptypod::MockEmptyPod,
primitives::merkletree::{MerkleClaimAndProof, MerkleTreeStateTransitionProof},
recursion::hash_verifier_data,
},
middleware::{
@ -45,10 +44,10 @@ pub struct MockMainPod {
operations: Vec<Operation>,
// public subset of the `statements` vector
public_statements: Vec<Statement>,
// All Merkle proofs
merkle_proofs_containers: Vec<MerkleClaimAndProof>,
// All Merkle tree state transition proofs
merkle_tree_state_transition_proofs_containers: Vec<MerkleTreeStateTransitionProof>,
// All Merkle proofs for containers
merkle_proofs: MerkleProofs,
// All Merkle tree state transition proofs for containers
merkle_transition_proofs: MerkleTransitionProofs,
// All verified signatures
signatures: Vec<SignedBy>,
}
@ -124,8 +123,8 @@ struct Data {
public_statements: Vec<Statement>,
operations: Vec<Operation>,
statements: Vec<Statement>,
merkle_proofs: Vec<MerkleClaimAndProof>,
merkle_tree_state_transition_proofs: Vec<MerkleTreeStateTransitionProof>,
merkle_proofs: MerkleProofs,
merkle_transition_proofs: MerkleTransitionProofs,
signatures: Vec<SignedBy>,
input_pods: Vec<(usize, Params, Hash, VDSet, serde_json::Value)>,
}
@ -153,8 +152,8 @@ impl MockMainPod {
let merkle_proofs =
extract_merkle_proofs(params, &mut aux_list, inputs.operations, inputs.statements)?;
// Similarly for Merkle state transition proofs.
let merkle_tree_state_transition_proofs =
extract_merkle_tree_state_transition_proofs(params, &mut aux_list, inputs.operations)?;
let merkle_transition_proofs =
extract_merkle_transition_proofs(params, &mut aux_list, inputs.operations)?;
let signatures =
extract_signatures(params, &mut aux_list, inputs.operations, inputs.statements)?;
@ -185,8 +184,8 @@ impl MockMainPod {
public_statements,
statements,
operations,
merkle_proofs_containers: merkle_proofs,
merkle_tree_state_transition_proofs_containers: merkle_tree_state_transition_proofs,
merkle_proofs,
merkle_transition_proofs,
signatures,
})
}
@ -260,8 +259,8 @@ impl Pod for MockMainPod {
.deref(
&self.statements[..input_statement_offset + i],
&self.signatures,
&self.merkle_proofs_containers,
&self.merkle_tree_state_transition_proofs_containers,
&self.merkle_proofs,
&self.merkle_transition_proofs,
)?
.check_and_log(&self.params, &s.clone().try_into()?)
.map_err(|e| e.into())
@ -321,10 +320,8 @@ impl Pod for MockMainPod {
public_statements: self.public_statements.clone(),
operations: self.operations.clone(),
statements: self.statements.clone(),
merkle_proofs: self.merkle_proofs_containers.clone(),
merkle_tree_state_transition_proofs: self
.merkle_tree_state_transition_proofs_containers
.clone(),
merkle_proofs: self.merkle_proofs.clone(),
merkle_transition_proofs: self.merkle_transition_proofs.clone(),
signatures: self.signatures.clone(),
input_pods,
})
@ -344,7 +341,7 @@ impl Pod for MockMainPod {
operations,
statements,
merkle_proofs,
merkle_tree_state_transition_proofs,
merkle_transition_proofs,
signatures,
input_pods,
} = serde_json::from_value(data)?;
@ -362,8 +359,8 @@ impl Pod for MockMainPod {
public_statements,
operations,
statements,
merkle_proofs_containers: merkle_proofs,
merkle_tree_state_transition_proofs_containers: merkle_tree_state_transition_proofs,
merkle_proofs,
merkle_transition_proofs,
signatures,
})
}

View file

@ -42,8 +42,6 @@ use crate::{
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MerkleClaimAndProofTarget {
pub(crate) max_depth: usize,
// `enabled` determines if the merkleproof verification is enabled
pub(crate) enabled: BoolTarget,
pub(crate) root: HashOutTarget,
pub(crate) key: ValueTarget,
pub(crate) value: ValueTarget,
@ -121,16 +119,9 @@ pub fn verify_merkle_proof_circuit(
let obtained_root =
compute_root_from_leaf(max_depth, builder, &path, &leaf_hash, &proof.siblings);
// check that obtained_root==root (from inputs), when enabled==true
let zero = builder.zero();
let expected_root: Vec<Target> = (0..HASH_SIZE)
.map(|j| builder.select(proof.enabled, proof.root.elements[j], zero))
.collect();
let computed_root: Vec<Target> = (0..HASH_SIZE)
.map(|j| builder.select(proof.enabled, obtained_root.elements[j], zero))
.collect();
// check that obtained_root==root (from inputs)
for j in 0..HASH_SIZE {
builder.connect(computed_root[j], expected_root[j]);
builder.connect(obtained_root.elements[j], proof.root.elements[j]);
}
measure_gates_end!(builder, measure);
}
@ -139,7 +130,6 @@ impl MerkleClaimAndProofTarget {
pub fn new_virtual(max_depth: usize, builder: &mut CircuitBuilder<F, D>) -> Self {
MerkleClaimAndProofTarget {
max_depth,
enabled: builder.add_virtual_bool_target_safe(),
root: builder.add_virtual_hash(),
key: builder.add_virtual_value(),
value: builder.add_virtual_value(),
@ -154,12 +144,7 @@ impl MerkleClaimAndProofTarget {
}
/// assigns the given values to the targets
#[allow(clippy::too_many_arguments)]
pub fn set_targets(
&self,
pw: &mut PartialWitness<F>,
enabled: bool,
mp: &MerkleClaimAndProof,
) -> Result<()> {
pub fn set_targets(&self, pw: &mut PartialWitness<F>, mp: &MerkleClaimAndProof) -> Result<()> {
if mp.proof.siblings.len() > self.max_depth {
return Err(Error::Tree(TreeError::circuit_depth_too_small(
self.max_depth,
@ -167,7 +152,6 @@ impl MerkleClaimAndProofTarget {
)));
}
pw.set_bool_target(self.enabled, enabled)?;
pw.set_hash_target(self.root, HashOut::from_vec(mp.root.0.to_vec()))?;
pw.set_target_arr(&self.key.elements, &mp.key.0)?;
pw.set_target_arr(&self.value.elements, &mp.value.0)?;
@ -207,8 +191,6 @@ impl MerkleClaimAndProofTarget {
#[derive(Clone, Serialize, Deserialize)]
pub struct MerkleProofExistenceTarget {
max_depth: usize,
// `enabled` determines if the merkleproof verification is enabled
pub(crate) enabled: BoolTarget,
pub(crate) root: HashOutTarget,
pub(crate) key: ValueTarget,
pub(crate) value: ValueTarget,
@ -236,16 +218,9 @@ pub fn verify_merkle_proof_existence_circuit(
let obtained_root =
compute_root_from_leaf(max_depth, builder, &path, &leaf_hash, &proof.siblings);
// check that obtained_root==root (from inputs), when enabled==true
let zero = builder.zero();
let expected_root: Vec<Target> = (0..HASH_SIZE)
.map(|j| builder.select(proof.enabled, proof.root.elements[j], zero))
.collect();
let computed_root: Vec<Target> = (0..HASH_SIZE)
.map(|j| builder.select(proof.enabled, obtained_root.elements[j], zero))
.collect();
// check that obtained_root==root (from inputs)
for j in 0..HASH_SIZE {
builder.connect(computed_root[j], expected_root[j]);
builder.connect(obtained_root.elements[j], proof.root.elements[j]);
}
measure_gates_end!(builder, measure);
@ -256,7 +231,6 @@ impl MerkleProofExistenceTarget {
pub fn new_virtual(max_depth: usize, builder: &mut CircuitBuilder<F, D>) -> Self {
MerkleProofExistenceTarget {
max_depth,
enabled: builder.add_virtual_bool_target_safe(),
root: builder.add_virtual_hash(),
key: builder.add_virtual_value(),
value: builder.add_virtual_value(),
@ -265,12 +239,7 @@ impl MerkleProofExistenceTarget {
}
}
/// assigns the given values to the targets
pub fn set_targets(
&self,
pw: &mut PartialWitness<F>,
enabled: bool,
mp: &MerkleClaimAndProof,
) -> Result<()> {
pub fn set_targets(&self, pw: &mut PartialWitness<F>, mp: &MerkleClaimAndProof) -> Result<()> {
assert!(mp.proof.existence); // sanity check
if mp.proof.siblings.len() > self.max_depth {
return Err(Error::Tree(TreeError::circuit_depth_too_small(
@ -279,7 +248,6 @@ impl MerkleProofExistenceTarget {
)));
}
pw.set_bool_target(self.enabled, enabled)?;
pw.set_hash_target(self.root, HashOut::from_vec(mp.root.0.to_vec()))?;
pw.set_target_arr(&self.key.elements, &mp.key.0)?;
pw.set_target_arr(&self.value.elements, &mp.value.0)?;
@ -456,8 +424,6 @@ fn hash_with_flag_target<H: AlgebraicHasher<F>>(
#[derive(Clone, Serialize, Deserialize)]
pub struct MerkleTreeStateTransitionProofTarget {
pub(crate) max_depth: usize,
// `enabled` determines if the merkleproof state transition verification is enabled
pub(crate) enabled: BoolTarget,
pub(crate) op: Target,
pub(crate) old_root: HashOutTarget,
pub(crate) op_proof: MerkleClaimAndProofTarget,
@ -511,7 +477,6 @@ pub fn verify_merkle_state_transition_circuit(
};
let new_key_proof = MerkleProofExistenceTarget {
max_depth: proof.max_depth,
enabled: proof.enabled,
root,
key: proof.op_key,
value: proof.op_value,
@ -523,13 +488,7 @@ pub fn verify_merkle_state_transition_circuit(
// Insert/Delete: Non-existence
// Update: Existence
let proof_type = is_update;
builder.conditional_assert_eq(
proof.enabled.target,
proof.op_proof.existence.target,
proof_type.target,
);
// 3.2) assert that proof.enabled matches with op_proof.enabled
builder.connect(proof.op_proof.enabled.target, proof.enabled.target);
builder.connect(proof.op_proof.existence.target, proof_type.target);
// 4) assert proof_non_existence.root corresponds to the root
// specified by the op (old_root for Insert/Update and new_root
@ -545,17 +504,9 @@ pub fn verify_merkle_state_transition_circuit(
};
for j in 0..HASH_SIZE {
// 4.1) assert that proof.proof_non_existence.root == proof.old_root
builder.conditional_assert_eq(
proof.enabled.target,
proof.op_proof.root.elements[j],
claim_root.elements[j],
);
builder.connect(proof.op_proof.root.elements[j], claim_root.elements[j]);
// 4.2) assert that the non-existence proof uses the op_key (value not needed).
builder.conditional_assert_eq(
proof.enabled.target,
proof.op_proof.key.elements[j],
proof.op_key.elements[j],
);
builder.connect(proof.op_proof.key.elements[j], proof.op_key.elements[j]);
}
// prepare value for check 5.2)
@ -593,7 +544,7 @@ pub fn verify_merkle_state_transition_circuit(
.map(|j| builder.select(is_divergence_level, zero, new_siblings[i].elements[j]))
.collect();
for j in 0..HASH_SIZE {
builder.conditional_assert_eq(proof.enabled.target, old_sibling_i[j], new_sibling_i[j]);
builder.connect(old_sibling_i[j], new_sibling_i[j]);
}
// 5.2) when i==d && if old_siblings[i] != new_siblings[i], check that:
@ -611,7 +562,7 @@ pub fn verify_merkle_state_transition_circuit(
let in_case_5_2 = builder.and(old_is_noteq_new, is_divergence_level);
// do the case2's checks
let sel = builder.and(proof.enabled, in_case_5_2);
let sel = in_case_5_2;
for j in 0..HASH_SIZE {
builder.conditional_assert_eq(sel.target, old_siblings[i].elements[j], zero);
builder.conditional_assert_eq(
@ -641,7 +592,6 @@ impl MerkleTreeStateTransitionProofTarget {
pub fn new_virtual(max_depth: usize, builder: &mut CircuitBuilder<F, D>) -> Self {
Self {
max_depth,
enabled: builder.add_virtual_bool_target_safe(),
op: builder.add_virtual_target(),
old_root: builder.add_virtual_hash(),
@ -661,7 +611,6 @@ impl MerkleTreeStateTransitionProofTarget {
pub fn set_targets(
&self,
pw: &mut PartialWitness<F>,
enabled: bool,
mp: &MerkleTreeStateTransitionProof,
) -> Result<()> {
let new_siblings = mp.siblings.clone();
@ -672,13 +621,11 @@ impl MerkleTreeStateTransitionProofTarget {
)));
}
pw.set_bool_target(self.enabled, enabled)?;
pw.set_target(self.op, F::from_canonical_u8(mp.op as u8))?;
pw.set_hash_target(self.old_root, HashOut::from_vec(mp.old_root.0.to_vec()))?;
self.op_proof.set_targets(
pw,
enabled,
&MerkleClaimAndProof {
root: if mp.op == MerkleTreeOp::Delete {
mp.new_root
@ -859,7 +806,6 @@ pub mod tests {
verify_merkle_proof_circuit(&mut builder, &targets);
targets.set_targets(
&mut pw,
true,
&MerkleClaimAndProof::new(tree.root(), key, Some(value), proof),
)?;
@ -871,6 +817,42 @@ pub mod tests {
Ok(())
}
#[test]
fn test_merkleproof_pad_valid() -> Result<()> {
// circuit
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let mut pw = PartialWitness::<F>::new();
let targets = MerkleClaimAndProofTarget::new_virtual(32, &mut builder);
verify_merkle_proof_circuit(&mut builder, &targets);
targets.set_targets(&mut pw, &MerkleClaimAndProof::pad())?;
// generate & verify proof
let data = builder.build::<C>();
let proof = data.prove(pw)?;
data.verify(proof)?;
Ok(())
}
#[test]
fn test_merkleproof_transition_pad_valid() -> Result<()> {
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let mut pw = PartialWitness::<F>::new();
let targets = MerkleTreeStateTransitionProofTarget::new_virtual(32, &mut builder);
verify_merkle_state_transition_circuit(&mut builder, &targets);
targets.set_targets(&mut pw, &MerkleTreeStateTransitionProof::pad())?;
// generate & verify proof
let data = builder.build::<C>();
let proof = data.prove(pw)?;
data.verify(proof)?;
Ok(())
}
#[test]
fn test_merkleproof_only_existence_verify() -> Result<()> {
for max_depth in [10, 16, 32, 40, 64, 128, 130, 250, 256] {
@ -906,7 +888,6 @@ pub mod tests {
verify_merkle_proof_circuit(&mut builder, &targets);
targets.set_targets(
&mut pw,
true,
&MerkleClaimAndProof::new(tree.root(), key, Some(value), proof),
)?;
@ -982,7 +963,6 @@ pub mod tests {
verify_merkle_proof_circuit(&mut builder, &targets);
targets.set_targets(
&mut pw,
true,
&MerkleClaimAndProof::new(tree.root(), key, Some(value), proof),
)?;
@ -1028,32 +1008,15 @@ pub mod tests {
let targets = MerkleClaimAndProofTarget::new_virtual(max_depth, &mut builder);
verify_merkle_proof_circuit(&mut builder, &targets);
// verification enabled & proof of existence
// proof of existence
let mp = MerkleClaimAndProof::new(tree2.root(), key, Some(value), proof);
targets.set_targets(&mut pw, true, &mp)?;
targets.set_targets(&mut pw, &mp)?;
// generate proof, expecting it to fail (since we're using the wrong
// root)
let data = builder.build::<C>();
assert!(data.prove(pw).is_err());
// Now generate a new proof, using `enabled=false`, which should pass the verification
// despite containing 'wrong' witness.
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let mut pw = PartialWitness::<F>::new();
let targets = MerkleClaimAndProofTarget::new_virtual(max_depth, &mut builder);
verify_merkle_proof_circuit(&mut builder, &targets);
// verification disabled & proof of existence
targets.set_targets(&mut pw, false, &mp)?;
// generate proof, should pass despite using wrong witness, since the
// `enabled=false`
let data = builder.build::<C>();
let proof = data.prove(pw)?;
data.verify(proof)?;
Ok(())
}
@ -1076,7 +1039,7 @@ pub mod tests {
let targets = MerkleTreeStateTransitionProofTarget::new_virtual(max_depth, &mut builder);
verify_merkle_state_transition_circuit(&mut builder, &targets);
targets.set_targets(&mut pw, true, state_transition_proof)?;
targets.set_targets(&mut pw, state_transition_proof)?;
// generate & verify proof
let data = builder.build::<C>();
@ -1273,71 +1236,4 @@ pub mod tests {
assert_ne!(state_transition_proof.new_root, tree.root()); // Tamper check
Ok(())
}
#[test]
fn test_state_transition_gadget_disabled() -> Result<()> {
let max_depth: usize = 32;
let mut kvs = HashMap::new();
for i in 0..8 {
kvs.insert(RawValue::from(i), RawValue::from(1000 + i));
}
let mut tree = MerkleTree::new(&kvs);
let key = RawValue::from(37);
let value = RawValue::from(1037);
let _ = tree.insert(&key, &value)?;
let key = RawValue::from(21);
let value = RawValue::from(1021);
let original_state_transition_proof = tree.insert(&key, &value)?;
let mut state_transition_proof = original_state_transition_proof.clone();
// modify the proof, so that it should fail when `enabled=true`, by
// changing the new_root
state_transition_proof.new_root = state_transition_proof.old_root;
run_circuit_disabled(max_depth, &state_transition_proof)?;
// modify the proof, so that it should fail when `enabled=true`, by
// changing the new_sibling at the divergence level, which should not
// pass the verification in the case where we're inserting key=21
let mut state_transition_proof = original_state_transition_proof.clone();
state_transition_proof.siblings[4] = EMPTY_HASH;
run_circuit_disabled(max_depth, &state_transition_proof)?;
Ok(())
}
fn run_circuit_disabled(
max_depth: usize,
state_transition_proof: &MerkleTreeStateTransitionProof,
) -> Result<()> {
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let mut pw = PartialWitness::<F>::new();
let targets = MerkleTreeStateTransitionProofTarget::new_virtual(max_depth, &mut builder);
verify_merkle_state_transition_circuit(&mut builder, &targets);
targets.set_targets(&mut pw, true, state_transition_proof)?;
// generate proof, and expect it to fail
let data = builder.build::<C>();
assert!(data.prove(pw).is_err()); // expect prove to fail
let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config);
let mut pw = PartialWitness::<F>::new();
let targets = MerkleTreeStateTransitionProofTarget::new_virtual(max_depth, &mut builder);
verify_merkle_state_transition_circuit(&mut builder, &targets);
targets.set_targets(&mut pw, false, state_transition_proof)?;
// generate and expect it to pass
let data = builder.build::<C>();
let proof = data.prove(pw)?;
data.verify(proof)?;
Ok(())
}
}

View file

@ -921,6 +921,21 @@ impl MerkleClaimAndProof {
},
}
}
/// Value used for padding. This is a valid merkle proof.
pub fn pad() -> Self {
let [key, value] = [EMPTY_VALUE, EMPTY_VALUE];
let root = kv_hash(&key, Some(value));
Self {
root,
key,
value,
proof: MerkleProof {
existence: true,
siblings: vec![],
other_leaf: None,
},
}
}
pub fn new(root: Hash, key: RawValue, value: Option<RawValue>, proof: MerkleProof) -> Self {
Self {
root,
@ -974,7 +989,6 @@ pub struct MerkleTreeStateTransitionProof {
}
impl MerkleTreeStateTransitionProof {
/// Value used for padding.
pub fn empty() -> Self {
let empty_proof_and_claim = MerkleClaimAndProof::empty();
Self {
@ -988,6 +1002,20 @@ impl MerkleTreeStateTransitionProof {
siblings: vec![],
}
}
/// Value used for padding. This is a valid transition proof.
pub fn pad() -> Self {
let pad_proof_and_claim = MerkleClaimAndProof::pad();
Self {
op: MerkleTreeOp::Update,
old_root: pad_proof_and_claim.root,
op_proof: pad_proof_and_claim.proof,
new_root: pad_proof_and_claim.root,
op_key: pad_proof_and_claim.key,
op_value: pad_proof_and_claim.value,
value: Some(pad_proof_and_claim.value),
siblings: vec![],
}
}
}
// NOTE: currently we use automatic serialization/deserialization, which is
@ -1165,6 +1193,15 @@ pub mod tests {
Ok(())
}
#[test]
fn test_merkletree_pad() {
let claim = MerkleClaimAndProof::pad();
MerkleTree::verify(claim.root, &claim.proof, &claim.key, &claim.value).unwrap();
let proof = MerkleTreeStateTransitionProof::pad();
MerkleTree::verify_state_transition(&proof).unwrap();
}
#[test]
fn test_key_not_found() -> Result<()> {
let db = Box::new(db::MemDB::new());

View file

@ -78,12 +78,12 @@ fn aggregate_rows<'a>(
UtilizationRow {
name: "merkle proofs",
used: merkle_proofs,
limit: params.max_merkle_proofs_containers,
limit: params.containers.state.max_medium,
},
UtilizationRow {
name: "merkle state transitions",
used: merkle_state_transitions,
limit: params.max_merkle_tree_state_transition_proofs_containers,
limit: params.containers.transition.max_medium,
},
UtilizationRow {
name: "custom pred verifications",
@ -278,15 +278,24 @@ mod tests {
use super::*;
use crate::{
frontend::multi_pod::cost::CustomPredicateId,
middleware::{Hash, RawValue},
middleware::{Hash, ParamsContainers, ParamsMerkleProofs, RawValue},
};
fn default_params() -> Params {
Params {
max_statements: 48,
max_public_statements: 8,
max_merkle_proofs_containers: 8,
max_merkle_tree_state_transition_proofs_containers: 4,
containers: ParamsContainers {
state: ParamsMerkleProofs {
max_small: 0,
max_medium: 8,
},
transition: ParamsMerkleProofs {
max_small: 0,
max_medium: 4,
},
..Default::default()
},
max_custom_predicate_verifications: 10,
max_custom_predicates: 2,
max_signed_by: 4,

View file

@ -395,13 +395,11 @@ pub fn solve(input: &SolverInput) -> Result<MultiPodSolution> {
let lb_statement_groups = lower_bound_from_total(input.num_statements, max_stmts_per_pod);
let lb_merkle = lower_bound_from_total(
resource_totals.merkle_proofs,
input.params.max_merkle_proofs_containers,
input.params.containers.state.max_medium,
);
let lb_merkle_transitions = lower_bound_from_total(
resource_totals.merkle_state_transitions,
input
.params
.max_merkle_tree_state_transition_proofs_containers,
input.params.containers.transition.max_medium,
);
let lb_custom_pred_verifications = lower_bound_from_total(
resource_totals.custom_pred_verifications,
@ -753,7 +751,7 @@ fn try_solve_with_pods(
.map(|s| (input.costs[s].merkle_proofs as f64) * prove[s][p])
.sum();
model.add_constraint(constraint!(
merkle_sum <= (input.params.max_merkle_proofs_containers as f64) * pod_used[p]
merkle_sum <= (input.params.containers.state.max_medium as f64) * pod_used[p]
));
// 6d: Merkle state transitions
@ -761,11 +759,7 @@ fn try_solve_with_pods(
.map(|s| (input.costs[s].merkle_state_transitions as f64) * prove[s][p])
.sum();
model.add_constraint(constraint!(
mst_sum
<= (input
.params
.max_merkle_tree_state_transition_proofs_containers as f64)
* pod_used[p]
mst_sum <= (input.params.containers.transition.max_medium as f64) * pod_used[p]
));
// 6e: Custom predicate verifications

View file

@ -780,6 +780,50 @@ pub const BASE_PARAMS: BaseParams = BaseParams {
max_operation_args: 5 + 1,
};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Hash)]
#[serde(rename_all = "camelCase")]
pub struct ParamsMerkleProofs {
pub max_small: usize,
pub max_medium: usize,
}
impl ParamsMerkleProofs {
pub fn max_total(&self) -> usize {
self.max_small + self.max_medium
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Hash)]
#[serde(rename_all = "camelCase")]
pub struct ParamsContainers {
// Parameters for exists/nonexists container operations. The small set only supports exists
pub state: ParamsMerkleProofs,
// Parameters for transition container operations (insert, delete, update). The small set only
// supports update.
pub transition: ParamsMerkleProofs,
// Max depth of small proofs
pub max_depth_small: usize,
// Max depth of medium proofs
pub max_depth_medium: usize,
}
impl Default for ParamsContainers {
fn default() -> Self {
Self {
state: ParamsMerkleProofs {
max_small: 22,
max_medium: 8,
},
transition: ParamsMerkleProofs {
max_small: 12,
max_medium: 6,
},
max_depth_small: 8,
max_depth_medium: 32,
}
}
}
/// Params: non dynamic parameters that define the circuit.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Hash)]
#[serde(rename_all = "camelCase")]
@ -793,12 +837,7 @@ pub struct Params {
// max number of operations using custom predicates that can be verified in the MainPod
pub max_custom_predicate_verifications: usize,
pub max_custom_predicate_wildcards: usize,
// maximum number of merkle proofs used for container operations
pub max_merkle_proofs_containers: usize,
// maximum number of merkle tree state transition proofs used for container update operations
pub max_merkle_tree_state_transition_proofs_containers: usize,
// maximum depth for merkle tree gadget used for container operations
pub max_depth_mt_containers: usize,
pub containers: ParamsContainers,
// maximum depth of the merkle tree gadget used for verifier_data membership
// check. This allows creating verifying sets of pod circuits of size
// 2^max_depth_mt_vds. Limits the number of container operations of the type Contains,
@ -820,9 +859,7 @@ impl Default for Params {
max_custom_predicates: 8,
max_custom_predicate_verifications: 8,
max_custom_predicate_wildcards: 8,
max_merkle_proofs_containers: 20,
max_merkle_tree_state_transition_proofs_containers: 6,
max_depth_mt_containers: 32,
containers: ParamsContainers::default(),
max_depth_mt_vds: 6, // up to 64 (2^6) different pod circuits
max_public_key_of: 2,
max_signed_by: 4,