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

@ -76,18 +76,15 @@ struct WildcardUsage {
}
/// Early validation: Check if predicate is fundamentally splittable
pub fn validate_predicate_is_splittable(
pred: &CustomPredicateDef,
params: &Params,
) -> Result<(), SplittingError> {
pub fn validate_predicate_is_splittable(pred: &CustomPredicateDef) -> Result<(), SplittingError> {
let public_args = pred.args.public_args.len();
// Check: public args must fit in operation arg limit
if public_args > params.max_statement_args {
if public_args > Params::max_statement_args() {
return Err(SplittingError::TooManyPublicArgs {
predicate: pred.name.name.clone(),
count: public_args,
max_allowed: params.max_statement_args,
max_allowed: Params::max_statement_args(),
message: "Public arguments exceed max operation args - cannot call this predicate"
.to_string(),
});
@ -102,10 +99,10 @@ pub fn split_predicate_if_needed(
params: &Params,
) -> Result<SplitResult, SplittingError> {
// Early validation
validate_predicate_is_splittable(&pred, params)?;
validate_predicate_is_splittable(&pred)?;
// If within limits, no splitting needed
if pred.statements.len() <= params.max_custom_predicate_arity {
if pred.statements.len() <= Params::max_custom_predicate_arity() {
return Ok(SplitResult {
predicates: vec![pred],
chain_info: None,
@ -173,12 +170,11 @@ struct OrderingResult {
fn order_constraints_optimally(
statements: Vec<StatementTmpl>,
_usage: &HashMap<String, WildcardUsage>,
params: &Params,
) -> OrderingResult {
let n = statements.len();
// If no splitting needed, preserve original order (identity mapping)
if n <= params.max_custom_predicate_arity {
if n <= Params::max_custom_predicate_arity() {
return OrderingResult {
statements,
reorder_map: (0..n).collect(),
@ -191,13 +187,8 @@ fn order_constraints_optimally(
let mut active_wildcards: HashSet<String> = HashSet::new();
while !remaining.is_empty() {
let best_idx = find_best_next_statement(
&statements,
&remaining,
&active_wildcards,
ordered.len(),
params,
);
let best_idx =
find_best_next_statement(&statements, &remaining, &active_wildcards, ordered.len());
remaining.remove(&best_idx);
let stmt = &statements[best_idx];
@ -268,10 +259,9 @@ fn find_best_next_statement(
remaining: &HashSet<usize>,
active_wildcards: &HashSet<String>,
ordered_count: usize,
params: &Params,
) -> usize {
// Calculate distance to next split point
let bucket_size = params.max_custom_predicate_arity - 1; // Reserve slot for chain call
let bucket_size = Params::max_custom_predicate_arity() - 1; // Reserve slot for chain call
let distance_to_split = bucket_size - (ordered_count % bucket_size);
let approaching_split = distance_to_split <= 2;
@ -432,7 +422,7 @@ fn split_into_chain(
let usage = analyze_wildcards(&pred.statements);
let real_statement_count = pred.statements.len();
let ordering_result = order_constraints_optimally(pred.statements, &usage, params);
let ordering_result = order_constraints_optimally(pred.statements, &usage);
let ordered_statements = ordering_result.statements;
let reorder_map = ordering_result.reorder_map;
@ -449,12 +439,12 @@ fn split_into_chain(
while pos < ordered_statements.len() {
let remaining = ordered_statements.len() - pos;
let is_last = remaining <= params.max_custom_predicate_arity;
let is_last = remaining <= Params::max_custom_predicate_arity();
let bucket_size = if is_last {
remaining // Last predicate uses all remaining
} else {
params.max_custom_predicate_arity - 1 // Reserve slot for chain call
Params::max_custom_predicate_arity() - 1 // Reserve slot for chain call
};
let end = pos + bucket_size;
@ -475,7 +465,7 @@ fn split_into_chain(
.cloned()
.collect();
let total_public = incoming_public.len() + new_promotions.len();
if total_public > params.max_statement_args {
if total_public > Params::max_statement_args() {
let context = crate::lang::error::SplitContext {
split_index: chain_links.len(),
statement_range: (pos, end),
@ -490,7 +480,7 @@ fn split_into_chain(
return Err(SplittingError::TooManyPublicArgsAtSplit {
predicate: original_name.clone(),
context: Box::new(context),
max_allowed: params.max_statement_args,
max_allowed: Params::max_statement_args(),
suggestion: suggestion.map(Box::new),
});
}
@ -688,10 +678,10 @@ fn generate_chain_predicates(
fn validate_chain(chain: &[CustomPredicateDef], params: &Params) -> Result<(), SplittingError> {
for pred in chain {
// Each predicate should have ≤ max_statements
assert!(pred.statements.len() <= params.max_custom_predicate_arity);
assert!(pred.statements.len() <= Params::max_custom_predicate_arity());
// Public args should fit
assert!(pred.args.public_args.len() <= params.max_statement_args);
assert!(pred.args.public_args.len() <= Params::max_statement_args());
// Total args should fit
let total =
@ -729,9 +719,8 @@ mod tests {
"#;
let pred = parse_predicate(input);
let params = Params::default();
assert!(validate_predicate_is_splittable(&pred, &params).is_ok());
assert!(validate_predicate_is_splittable(&pred).is_ok());
}
#[test]
@ -743,9 +732,8 @@ mod tests {
"#;
let pred = parse_predicate(input);
let params = Params::default(); // max_statement_args = 5
let result = validate_predicate_is_splittable(&pred, &params);
let result = validate_predicate_is_splittable(&pred);
assert!(matches!(
result,
Err(SplittingError::TooManyPublicArgs { .. })