Split Params into base and developer-defined (#458)

I thought it would be nice to have a Predicate for the typed value so that the developer can work with predicates as values comfortably.  Then I noticed that hashing a predicate required `Params` which would have been annoying for converting a `TypedValue::Predicate` to `RawValue` and this led to a small refactor over how `Params` work.

We already had some fields in the `Params` struct that determine compatibility between encoded data.  They can be seen as determining a kind of ABI compatibility.  In general it's better if those parameters don't change so that different circuit configurations can still verify proofs from each other.  So I decided to force those parameters to be constant in the code base and not allow the user of our library to change them.  Many field element serialization/deserialization functions in our code depended on those parameters, and since now they are constant many functions get rid of the `Params` argument, which simplifies the code.  This includes the serialization of a `Predicate` which was required to calculate its hash.
This commit is contained in:
Eduard S. 2026-02-02 16:23:32 +01:00 committed by GitHub
parent 498e946612
commit a7a30176a7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 376 additions and 468 deletions

View file

@ -50,21 +50,20 @@ use crate::{
/// circuits with a small `max_public_statements` only pay for `max_public_statements` by starting
/// the poseidon state with a precomputed constant corresponding to the front-padding part: `id =
/// hash(serialize(reverse(statements || none-statements)))`
pub fn calculate_statements_hash(statements: &[Statement], params: &Params) -> middleware::Hash {
assert!(statements.len() <= params.num_public_statements_hash);
assert!(params.max_public_statements <= params.num_public_statements_hash);
pub fn calculate_statements_hash(statements: &[Statement]) -> middleware::Hash {
assert!(statements.len() <= Params::num_public_statements_hash());
let mut none_st: Statement = middleware::Statement::None.into();
pad_statement(params, &mut none_st);
pad_statement(&mut none_st);
let statements_back_padded = statements
.iter()
.chain(iter::repeat(&none_st))
.take(params.num_public_statements_hash)
.take(Params::num_public_statements_hash())
.collect_vec();
let field_elems = statements_back_padded
.iter()
.rev()
.flat_map(|statement| statement.to_fields(params))
.flat_map(|statement| statement.to_fields())
.collect::<Vec<_>>();
Hash(PoseidonHash::hash_no_pad(&field_elems).elements)
}
@ -115,7 +114,7 @@ pub(crate) fn extract_custom_predicate_verifications(
.find_map(|(i, cpb)| (cpb.id() == cpr.batch.id()).then_some(i))
.expect("find the custom predicate from the extracted unique list");
let custom_predicate_table_index =
batch_index * params.max_custom_batch_size + cpr.index;
batch_index * Params::max_custom_batch_size() + cpr.index;
aux_list[i] = OperationAux::CustomPredVerifyIndex(table.len());
table.push(CustomPredicateVerification {
custom_predicate_table_index,
@ -326,8 +325,8 @@ fn fill_pad<T: Clone>(v: &mut Vec<T>, pad_value: T, len: usize) {
}
}
pub fn pad_statement(params: &Params, s: &mut Statement) {
fill_pad(&mut s.1, StatementArg::None, params.max_statement_args)
pub fn pad_statement(s: &mut Statement) {
fill_pad(&mut s.1, StatementArg::None, Params::max_statement_args())
}
fn pad_operation_args(params: &Params, args: &mut Vec<OperationArg>) {
@ -353,7 +352,7 @@ pub(crate) fn layout_statements(
// We mocking or we don't need padding so we skip creating an EmptyPod
MockEmptyPod::new_boxed(params, inputs.vd_set.clone())
} else {
EmptyPod::new_boxed(params, inputs.vd_set.clone())
EmptyPod::new_boxed(inputs.vd_set.clone())
};
let empty_pod = empty_pod_box.as_ref();
assert!(inputs.pods.len() <= params.max_input_pods);
@ -367,7 +366,7 @@ pub(crate) fn layout_statements(
.unwrap_or(&middleware::Statement::None)
.clone()
.into();
pad_statement(params, &mut st);
pad_statement(&mut st);
statements.push(st);
}
}
@ -386,7 +385,7 @@ pub(crate) fn layout_statements(
.unwrap_or(&middleware::Statement::None)
.clone()
.into();
pad_statement(params, &mut st);
pad_statement(&mut st);
statements.push(st);
}
@ -399,7 +398,7 @@ pub(crate) fn layout_statements(
.unwrap_or(&middleware::Statement::None)
.clone()
.into();
pad_statement(params, &mut st);
pad_statement(&mut st);
statements.push(st);
}
@ -475,7 +474,7 @@ impl MainPodProver for Prover {
// We don't need padding so we skip creating an EmptyPod
MockEmptyPod::new_boxed(params, inputs.vd_set.clone())
} else {
EmptyPod::new_boxed(params, inputs.vd_set.clone())
EmptyPod::new_boxed(inputs.vd_set.clone())
};
let inputs = MainPodInputs {
pods: &inputs
@ -491,10 +490,7 @@ impl MainPodProver for Prover {
let input_pods_pub_self_statements = inputs
.pods
.iter()
.map(|pod| {
assert_eq!(params.id_params(), pod.params().id_params());
pod.pub_self_statements()
})
.map(|pod| pod.pub_self_statements())
.collect_vec();
// Aux values for backend::Operation
@ -527,7 +523,7 @@ impl MainPodProver for Prover {
let operations = process_public_statements_operations(params, &statements, operations)?;
// get the id out of the public statements
let sts_hash = calculate_statements_hash(&public_statements, params);
let sts_hash = calculate_statements_hash(&public_statements);
let common_hash: String = cache_get_rec_main_pod_common_hash(params).clone();
let proofs = inputs
@ -718,7 +714,7 @@ impl Pod for MainPod {
)));
}
// 2. get the id out of the public statements
let sts_hash = calculate_statements_hash(&self.public_statements, &self.params);
let sts_hash = calculate_statements_hash(&self.public_statements);
if sts_hash != self.sts_hash {
return Err(Error::statements_hash_not_equal(self.sts_hash, sts_hash));
}
@ -738,7 +734,7 @@ impl Pod for MainPod {
let rec_main_pod_verifier_circuit_data =
&*cache_get_rec_main_pod_verifier_circuit_data(&self.params);
let public_inputs = sts_hash
.to_fields(&self.params)
.to_fields()
.iter()
.chain(self.vd_set.root().0.iter())
.cloned()
@ -998,14 +994,10 @@ pub mod tests {
max_input_pods_public_statements: 2,
max_statements: 5,
max_public_statements: 2,
num_public_statements_hash: 4,
max_statement_args: 4,
max_operation_args: 4,
max_operation_args: 5,
max_custom_predicate_batches: 2,
max_custom_predicate_verifications: 2,
max_custom_predicate_arity: 2,
max_custom_predicate_wildcards: 3,
max_custom_batch_size: 2,
max_merkle_proofs_containers: 2,
max_merkle_tree_state_transition_proofs_containers: 2,
max_public_key_of: 2,
@ -1067,10 +1059,7 @@ pub mod tests {
max_input_pods: 0,
max_statements: 9,
max_public_statements: 4,
max_statement_args: 4,
max_operation_args: 4,
max_custom_predicate_arity: 3,
max_custom_batch_size: 3,
max_operation_args: 5,
max_custom_predicate_wildcards: 4,
max_custom_predicate_verifications: 2,
max_merkle_proofs_containers: 3,

View file

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use crate::{
backends::plonky2::error::{Error, Result},
middleware::{self, NativePredicate, Params, Predicate, StatementArg, ToFields, Value},
middleware::{self, NativePredicate, Predicate, StatementArg, ToFields, Value, BASE_PARAMS},
};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -30,14 +30,14 @@ impl Statement {
}
impl ToFields for Statement {
fn to_fields(&self, params: &Params) -> Vec<middleware::F> {
let mut fields = self.0.hash(params).to_fields(params);
fn to_fields(&self) -> Vec<middleware::F> {
let mut fields = self.0.hash().to_fields();
fields.extend(
self.1
.iter()
.chain(iter::repeat(&StatementArg::None))
.take(params.max_statement_args)
.flat_map(|arg| arg.to_fields(params)),
.take(BASE_PARAMS.max_statement_args)
.flat_map(|arg| arg.to_fields()),
);
fields
}