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:
parent
498e946612
commit
a7a30176a7
20 changed files with 376 additions and 468 deletions
|
|
@ -355,7 +355,7 @@ pub fn batch_predicates(
|
|||
}
|
||||
|
||||
// Plan batch assignments in declaration order
|
||||
let assignments = plan_batch_assignments(&predicates, params.max_custom_batch_size)?;
|
||||
let assignments = plan_batch_assignments(&predicates, Params::max_custom_batch_size())?;
|
||||
|
||||
// Build reference map: name -> (batch_idx, idx_in_batch)
|
||||
let reference_map: HashMap<String, (usize, usize)> = assignments
|
||||
|
|
@ -1039,18 +1039,18 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_mutual_recursion_exceeds_capacity_error() {
|
||||
// Two predicates that call each other (SCC size = 2) with max batch size 1
|
||||
// Two predicates that call each other (SCC size = 5) with max batch size 4
|
||||
// Should error because an SCC cannot be split across batches
|
||||
let input = r#"
|
||||
pred1(A) = AND(pred2(A))
|
||||
pred2(B) = AND(pred1(B))
|
||||
pred2(B) = AND(pred3(B))
|
||||
pred3(B) = AND(pred4(B))
|
||||
pred4(B) = AND(pred5(B))
|
||||
pred5(B) = AND(pred1(B))
|
||||
"#;
|
||||
|
||||
let (predicates, validated) = parse_and_validate(input);
|
||||
let params = Params {
|
||||
max_custom_batch_size: 1, // force SCC > capacity
|
||||
..Default::default()
|
||||
};
|
||||
let params = Params::default();
|
||||
|
||||
let result = batch_predicates(
|
||||
preds_to_split_results(predicates),
|
||||
|
|
|
|||
|
|
@ -326,10 +326,10 @@ impl<'a> Lowerer<'a> {
|
|||
batches: Option<&PredicateBatches>,
|
||||
) -> Result<MWStatementTmpl, LoweringError> {
|
||||
// Enforce argument count limit for request statements
|
||||
if stmt.args.len() > self.params.max_statement_args {
|
||||
if stmt.args.len() > Params::max_statement_args() {
|
||||
return Err(LoweringError::TooManyStatementArgs {
|
||||
count: stmt.args.len(),
|
||||
max: self.params.max_statement_args,
|
||||
max: Params::max_statement_args(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -446,6 +446,7 @@ impl<'a> Lowerer<'a> {
|
|||
let result = frontend_ast_split::split_predicate_if_needed(pred, self.params)?;
|
||||
split_results.push(result);
|
||||
}
|
||||
|
||||
Ok(split_results)
|
||||
}
|
||||
}
|
||||
|
|
@ -676,7 +677,8 @@ mod tests {
|
|||
"#;
|
||||
|
||||
let params = Params::default();
|
||||
parse_validate_and_lower(input, ¶ms).unwrap();
|
||||
let result = parse_validate_and_lower(input, ¶ms);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -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, ¶ms).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, ¶ms);
|
||||
let result = validate_predicate_is_splittable(&pred);
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(SplittingError::TooManyPublicArgs { .. })
|
||||
|
|
|
|||
|
|
@ -572,10 +572,7 @@ mod tests {
|
|||
max_input_pods: 3,
|
||||
max_statements: 31,
|
||||
max_public_statements: 10,
|
||||
max_statement_args: 6,
|
||||
max_operation_args: 5,
|
||||
max_custom_predicate_arity: 5,
|
||||
max_custom_batch_size: 5,
|
||||
max_custom_predicate_wildcards: 12,
|
||||
..Default::default()
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue