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

@ -54,10 +54,10 @@ impl ToFields for OperationType {
/// Encoding:
/// - Native(native_op) => `[1, [native_op], 0, 0, 0, 0]`
/// - Custom(batch, index) => `[3, [batch.id], index]`
fn to_fields(&self, params: &Params) -> Vec<F> {
fn to_fields(&self) -> Vec<F> {
let mut fields: Vec<F> = match self {
Self::Native(p) => iter::once(F::from_canonical_u64(1))
.chain(p.to_fields(params))
.chain(p.to_fields())
.collect(),
Self::Custom(CustomPredicateRef { batch, index }) => {
iter::once(F::from_canonical_u64(3))
@ -118,7 +118,7 @@ impl NativeOperation {
}
impl ToFields for NativeOperation {
fn to_fields(&self, _params: &Params) -> Vec<F> {
fn to_fields(&self) -> Vec<F> {
vec![F::from_canonical_u64(*self as u64)]
}
}
@ -605,14 +605,25 @@ pub fn check_st_tmpl(
}
pub fn fill_wildcard_values(
params: &Params,
pred: &CustomPredicate,
args: &[Statement],
wildcard_map: &mut [Option<Value>],
) -> Result<()> {
for (st_tmpl, st) in pred.statements.iter().zip(args) {
if let PredicateOrWildcard::Wildcard(wc) = &st_tmpl.pred_or_wc {
wc_check_or_set(Value::from(st.predicate().hash(params)), wc, wildcard_map)?;
wc_check_or_set(Value::from(st.predicate().hash()), wc, wildcard_map)?;
}
let st_args = st.args();
for (st_tmpl_arg, st_arg) in st_tmpl.args.iter().zip(&st_args) {
if let Err(st_tmpl_check_error) = check_st_tmpl(st_tmpl_arg, st_arg, wildcard_map) {
return Err(Error::statements_dont_match(
st.clone(),
st_tmpl.clone(),
wildcard_map.to_vec(),
st_tmpl_check_error,
));
}
}
let st_args = st.args();
@ -642,7 +653,7 @@ pub fn wildcard_values_from_op_st(
.chain(core::iter::repeat(None))
.take(params.max_custom_predicate_wildcards)
.collect_vec();
fill_wildcard_values(params, pred, op_args, &mut wildcard_map)?;
fill_wildcard_values(pred, op_args, &mut wildcard_map)?;
// NOTE: We set unresolved wildcard slots with an empty value. They can be unresolved because
// they are beyond the number of used wildcards in this custom predicate, or they could be
// private arguments that are unused in a particular disjunction.
@ -653,7 +664,6 @@ pub fn wildcard_values_from_op_st(
}
fn check_custom_pred_argument(
params: &Params,
custom_pred_ref: &CustomPredicateRef,
template: &StatementTmpl,
statement: &Statement,
@ -676,11 +686,11 @@ fn check_custom_pred_argument(
}
}
PredicateOrWildcard::Wildcard(wc) => {
let pred_hash = Value::from(statement.predicate().hash(params));
if wc_values[wc.index] != pred_hash {
let pred_value = Value::from(statement.predicate());
if wc_values[wc.index] != pred_value {
return Err(Error::mismatched_statement_wc_pred(
wc_values[wc.index].clone(),
pred_hash,
pred_value,
statement.predicate(),
));
}
@ -748,7 +758,7 @@ pub(crate) fn check_custom_pred(
if !pred.conjunction && matches!(st, Statement::None) {
continue;
}
check_custom_pred_argument(params, custom_pred_ref, st_tmpl, st, &wc_values)?;
check_custom_pred_argument(custom_pred_ref, st_tmpl, st, &wc_values)?;
match_exists = true;
}
@ -761,7 +771,7 @@ pub(crate) fn check_custom_pred(
}
impl ToFields for Operation {
fn to_fields(&self, _params: &Params) -> Vec<F> {
fn to_fields(&self) -> Vec<F> {
todo!()
}
}