MultiPodBuilder improvements (#473)

- Export MultiPodBuilder Error
- Redefine MultiPodBuilder error to wrap the frontend Error (this way we get better formatting instead of an embedded string)
- handle initial wildcard values in `MultiPodBuilder.op` just like the `MainPodBuilder` does
This commit is contained in:
Eduard S. 2026-02-06 10:53:01 +01:00 committed by GitHub
parent b04560c362
commit 2bd99ef322
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 52 additions and 49 deletions

View file

@ -28,7 +28,8 @@ mod serialization;
pub use custom::*; pub use custom::*;
pub use error::*; pub use error::*;
pub use multi_pod::{ pub use multi_pod::{
MultiPodBuilder, MultiPodResult, MultiPodSolution, Options as MultiPodOptions, Error as MultiPodError, MultiPodBuilder, MultiPodResult, MultiPodSolution,
Options as MultiPodOptions,
}; };
pub use operation::*; pub use operation::*;
pub use pod_request::*; pub use pod_request::*;

View file

@ -51,7 +51,7 @@ use std::collections::{BTreeSet, HashMap};
use crate::{ use crate::{
frontend::{MainPod, MainPodBuilder, Operation, OperationArg}, frontend::{MainPod, MainPodBuilder, Operation, OperationArg},
middleware::{Hash, MainPodProver, Params, Statement, VDSet}, middleware::{Hash, MainPodProver, Params, Statement, VDSet, Value},
}; };
mod cost; mod cost;
@ -63,10 +63,11 @@ use deps::{DependencyGraph, StatementSource};
pub use solver::MultiPodSolution; pub use solver::MultiPodSolution;
/// Error type for multi-POD operations. /// Error type for multi-POD operations.
#[derive(Debug, Clone)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
Custom(String),
/// Error from the frontend. /// Error from the frontend.
Frontend(String), Frontend(#[from] crate::frontend::Error),
/// Error from the MILP solver. /// Error from the MILP solver.
Solver(String), Solver(String),
/// No solution exists (shouldn't happen with valid input). /// No solution exists (shouldn't happen with valid input).
@ -76,21 +77,14 @@ pub enum Error {
impl std::fmt::Display for Error { impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Error::Frontend(msg) => write!(f, "Frontend error: {}", msg), Error::Custom(msg) => write!(f, "Custom error: {}", msg),
Error::Frontend(e) => write!(f, "Frontend error: {}", e),
Error::Solver(msg) => write!(f, "Solver error: {}", msg), Error::Solver(msg) => write!(f, "Solver error: {}", msg),
Error::NoSolution => write!(f, "No solution exists"), Error::NoSolution => write!(f, "No solution exists"),
} }
} }
} }
impl std::error::Error for Error {}
impl From<crate::frontend::Error> for Error {
fn from(e: crate::frontend::Error) -> Self {
Error::Frontend(e.to_string())
}
}
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
/// Default maximum number of PODs the solver will consider. /// Default maximum number of PODs the solver will consider.
@ -175,6 +169,8 @@ pub struct MultiPodBuilder {
statements: Vec<Statement>, statements: Vec<Statement>,
/// Operations that produce each statement. /// Operations that produce each statement.
operations: Vec<Operation>, operations: Vec<Operation>,
/// Optional initial wildcard values for custom operations
operations_wildcard_values: Vec<Vec<(usize, Value)>>,
/// Indices of statements that should be public in output PODs. /// Indices of statements that should be public in output PODs.
/// Uses Vec since max_public_statements is small (≤8); indices are naturally sorted. /// Uses Vec since max_public_statements is small (≤8); indices are naturally sorted.
output_public_indices: Vec<usize>, output_public_indices: Vec<usize>,
@ -193,6 +189,7 @@ pub struct SolvedMultiPod {
input_pods: Vec<MainPod>, input_pods: Vec<MainPod>,
statements: Vec<Statement>, statements: Vec<Statement>,
operations: Vec<Operation>, operations: Vec<Operation>,
operations_wildcard_values: Vec<Vec<(usize, Value)>>,
solution: MultiPodSolution, solution: MultiPodSolution,
deps: DependencyGraph, deps: DependencyGraph,
} }
@ -313,6 +310,7 @@ impl SolvedMultiPod {
for &stmt_idx in &statements_sorted { for &stmt_idx in &statements_sorted {
let is_public = public_set.contains(&stmt_idx); let is_public = public_set.contains(&stmt_idx);
let mut op = self.operations[stmt_idx].clone(); let mut op = self.operations[stmt_idx].clone();
let wildcard_values = self.operations_wildcard_values[stmt_idx].clone();
// Remap Statement arguments that reference locally-proved statements. // Remap Statement arguments that reference locally-proved statements.
// For external dependencies (from input PODs including earlier generated PODs), // For external dependencies (from input PODs including earlier generated PODs),
@ -330,17 +328,13 @@ impl SolvedMultiPod {
} }
} }
let stmt = builder let stmt = builder.op(is_public, wildcard_values, op)?;
.op(is_public, vec![], op)
.map_err(|e| Error::Frontend(e.to_string()))?;
added_statements.insert(stmt_idx, stmt); added_statements.insert(stmt_idx, stmt);
} }
// Step 4: Prove the POD // Step 4: Prove the POD
let pod = builder let pod = builder.prove(prover)?;
.prove(prover)
.map_err(|e| Error::Frontend(e.to_string()))?;
Ok(pod) Ok(pod)
} }
@ -370,34 +364,48 @@ impl MultiPodBuilder {
input_pods: Vec::new(), input_pods: Vec::new(),
statements: Vec::new(), statements: Vec::new(),
operations: Vec::new(), operations: Vec::new(),
operations_wildcard_values: Vec::new(),
output_public_indices: Vec::new(), output_public_indices: Vec::new(),
} }
} }
/// Add an external input POD. /// Add an external input POD.
pub fn add_pod(&mut self, pod: MainPod) -> Result<()> { pub fn add_pod(&mut self, pod: MainPod) -> Result<()> {
self.builder self.builder.add_pod(pod.clone())?;
.add_pod(pod.clone())
.map_err(|e| Error::Frontend(e.to_string()))?;
self.input_pods.push(pod); self.input_pods.push(pod);
Ok(()) Ok(())
} }
/// Add a public operation (statement will be public in output). /// Add a public operation (statement will be public in output).
pub fn pub_op(&mut self, op: Operation) -> Result<Statement> { pub fn pub_op(&mut self, op: Operation) -> Result<Statement> {
let stmt = self.add_operation(op)?; self.op(true, vec![], op)
// Index is always new (just added), so push without duplicate check
self.output_public_indices.push(self.statements.len() - 1);
Ok(stmt)
} }
/// Add a private operation. /// Add a private operation.
pub fn priv_op(&mut self, op: Operation) -> Result<Statement> { pub fn priv_op(&mut self, op: Operation) -> Result<Statement> {
self.add_operation(op) self.op(false, vec![], op)
}
pub fn op(
&mut self,
public: bool,
wildcard_values: Vec<(usize, Value)>,
op: Operation,
) -> Result<Statement> {
let stmt = self.add_operation(wildcard_values, op)?;
if public {
// Index is always new (just added), so push without duplicate check
self.output_public_indices.push(self.statements.len() - 1);
}
Ok(stmt)
} }
/// Internal: Add an operation and create its statement. /// Internal: Add an operation and create its statement.
fn add_operation(&mut self, op: Operation) -> Result<Statement> { fn add_operation(
&mut self,
wildcard_values: Vec<(usize, Value)>,
op: Operation,
) -> Result<Statement> {
// Get or create the cached builder // Get or create the cached builder
// //
// NOTE: We clone input pods here because MainPodBuilder takes ownership. // NOTE: We clone input pods here because MainPodBuilder takes ownership.
@ -407,11 +415,11 @@ impl MultiPodBuilder {
// while existing code using MainPodBuilder (with the default) would be unaffected. // while existing code using MainPodBuilder (with the default) would be unaffected.
let stmt = self let stmt = self
.builder .builder
.op(false, vec![], op.clone()) .op(false, wildcard_values.clone(), op.clone())?;
.map_err(|e| Error::Frontend(e.to_string()))?;
self.statements.push(stmt.clone()); self.statements.push(stmt.clone());
self.operations.push(op); self.operations.push(op);
self.operations_wildcard_values.push(wildcard_values);
Ok(stmt) Ok(stmt)
} }
@ -427,7 +435,7 @@ impl MultiPodBuilder {
} }
Ok(()) Ok(())
} else { } else {
Err(Error::Frontend( Err(Error::Custom(
"reveal() called with statement not found in builder".to_string(), "reveal() called with statement not found in builder".to_string(),
)) ))
} }
@ -511,6 +519,7 @@ impl MultiPodBuilder {
input_pods: self.input_pods, input_pods: self.input_pods,
statements: self.statements, statements: self.statements,
operations: self.operations, operations: self.operations,
operations_wildcard_values: self.operations_wildcard_values,
solution, solution,
deps, deps,
}) })
@ -570,10 +579,7 @@ mod tests {
assert!(result.intermediate_pods().is_empty()); assert!(result.intermediate_pods().is_empty());
// Verify the POD // Verify the POD
result.pods[0] result.pods[0].pod.verify().unwrap();
.pod
.verify()
.map_err(|e| Error::Frontend(e.to_string()))?;
Ok(()) Ok(())
} }
@ -624,7 +630,7 @@ mod tests {
for (i, pod) in result.pods.iter().enumerate() { for (i, pod) in result.pods.iter().enumerate() {
pod.pod pod.pod
.verify() .verify()
.map_err(|e| Error::Frontend(format!("POD {} verification failed: {}", i, e)))?; .unwrap_or_else(|_| panic!("POD {} verification failed", i));
} }
Ok(()) Ok(())
@ -745,7 +751,7 @@ mod tests {
for (i, pod) in result.pods.iter().enumerate() { for (i, pod) in result.pods.iter().enumerate() {
pod.pod pod.pod
.verify() .verify()
.map_err(|e| Error::Frontend(format!("POD {} verification failed: {}", i, e)))?; .unwrap_or_else(|_| panic!("POD {} verification failed", i));
} }
Ok(()) Ok(())
@ -796,7 +802,7 @@ mod tests {
for (i, pod) in result.pods.iter().enumerate() { for (i, pod) in result.pods.iter().enumerate() {
pod.pod pod.pod
.verify() .verify()
.map_err(|e| Error::Frontend(format!("POD {} verification failed: {}", i, e)))?; .unwrap_or_else(|_| panic!("POD {} verification failed", i));
} }
Ok(()) Ok(())
@ -947,7 +953,7 @@ mod tests {
for (i, pod) in result.pods.iter().enumerate() { for (i, pod) in result.pods.iter().enumerate() {
pod.pod pod.pod
.verify() .verify()
.map_err(|e| Error::Frontend(format!("POD {} verification failed: {}", i, e)))?; .unwrap_or_else(|_| panic!("POD {} verification failed", i));
} }
Ok(()) Ok(())
@ -1187,11 +1193,7 @@ mod tests {
let result = solved.prove(&prover)?; let result = solved.prove(&prover)?;
assert_eq!(result.pods.len(), 1); assert_eq!(result.pods.len(), 1);
result result.output_pod().pod.verify().unwrap();
.output_pod()
.pod
.verify()
.map_err(|e| Error::Frontend(format!("Output POD verification failed: {}", e)))?;
Ok(()) Ok(())
} }
@ -1243,7 +1245,7 @@ mod tests {
for (i, pod) in result.pods.iter().enumerate() { for (i, pod) in result.pods.iter().enumerate() {
pod.pod pod.pod
.verify() .verify()
.map_err(|e| Error::Frontend(format!("POD {} verification failed: {}", i, e)))?; .unwrap_or_else(|_| panic!("POD {} verification failed", i));
} }
Ok(()) Ok(())
@ -1307,7 +1309,7 @@ mod tests {
for (i, pod) in result.pods.iter().enumerate() { for (i, pod) in result.pods.iter().enumerate() {
pod.pod pod.pod
.verify() .verify()
.map_err(|e| Error::Frontend(format!("POD {} verification failed: {}", i, e)))?; .unwrap_or_else(|_| panic!("POD {} verification failed", i));
} }
Ok(()) Ok(())
@ -1366,7 +1368,7 @@ mod tests {
for (i, pod) in result.pods.iter().enumerate() { for (i, pod) in result.pods.iter().enumerate() {
pod.pod pod.pod
.verify() .verify()
.map_err(|e| Error::Frontend(format!("POD {} verification failed: {}", i, e)))?; .unwrap_or_else(|_| panic!("POD {} verification failed", i));
} }
Ok(()) Ok(())
@ -1491,7 +1493,7 @@ mod tests {
for (i, pod) in result.pods.iter().enumerate() { for (i, pod) in result.pods.iter().enumerate() {
pod.pod pod.pod
.verify() .verify()
.map_err(|e| Error::Frontend(format!("POD {} verification failed: {}", i, e)))?; .unwrap_or_else(|_| panic!("POD {} verification failed", i));
} }
Ok(()) Ok(())
@ -1608,7 +1610,7 @@ mod tests {
for (i, pod) in result.pods.iter().enumerate() { for (i, pod) in result.pods.iter().enumerate() {
pod.pod pod.pod
.verify() .verify()
.map_err(|e| Error::Frontend(format!("POD {} verification failed: {}", i, e)))?; .unwrap_or_else(|_| panic!("POD {} verification failed", i));
} }
Ok(()) Ok(())