migrate from anyhow to thiserror (#197)

* migrate from anyhow to thiserror (#190). pending polish error msgs

* Add backtrace and compartmentalize errors

- Include backtraces in the errors we generate.  To get this we can't
  just return a literal enum, because the backtrace requires a call.
- Related to the previous point: add methods to create errors so
  we can include the backtrace conveniently without changing too much
  the syntax.  So instead of `Err(Error::KeyNotFound(key))` (literal
  enum) it will be `Err(Error::key_not_found(key))` (method call)
- Each error should be local to its scope, and each scope should
  only return its own error.
  - The merkle tree should return `TreeError` and not Error
  - The middleware should return `MiddlewareError` and not Error
- With a global Error we can't easily include backend/frontend types in
  the error fields, so declare a `BackendError` and a `FrontendError`
  and follow the pattern from the previous point
- The Pod traits should be able to return backend errors and will be
  used in the frontend; for that we change them to use trait object
  Error: `dyn std::error::Error`

* fix error

* apply suggestions from @arnaucube

* rename XError and XResult to Error and Result

* reorg signature

* make frontend custom error more ergonomic

* remove unnecessary feature

---------

Co-authored-by: Eduard S. <eduardsanou@posteo.net>
This commit is contained in:
arnaucube 2025-04-22 15:07:04 +02:00 committed by GitHub
parent 58d3c6a236
commit 29545f03fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 696 additions and 273 deletions

View file

@ -3,7 +3,6 @@
use std::{collections::HashMap, convert::From, fmt};
use anyhow::{anyhow, Result};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
@ -14,9 +13,11 @@ use crate::middleware::{
};
mod custom;
mod error;
mod operation;
mod serialization;
pub use custom::*;
pub use error::*;
pub use operation::*;
use serialization::*;
@ -101,7 +102,7 @@ impl SignedPod {
self.pod.id()
}
pub fn verify(&self) -> Result<()> {
self.pod.verify()
self.pod.verify().map_err(Error::Backend)
}
pub fn kvs(&self) -> &HashMap<Key, Value> {
&self.kvs
@ -276,11 +277,17 @@ impl MainPodBuilder {
let container =
op.1.get(0)
.and_then(|arg| arg.value())
.ok_or(anyhow!("Invalid container argument for op {}.", op))?;
.ok_or(Error::custom(format!(
"Invalid container argument for op {}.",
op
)))?;
let key =
op.1.get(1)
.and_then(|arg| arg.value())
.ok_or(anyhow!("Invalid key argument for op {}.", op))?;
.ok_or(Error::custom(format!(
"Invalid key argument for op {}.",
op
)))?;
let proof = if op_type == &Native(ContainsFromEntries) {
container.prove_existence(key)?.1
} else {
@ -292,7 +299,7 @@ impl MainPodBuilder {
}
}
fn op(&mut self, public: bool, op: Operation) -> Result<Statement, anyhow::Error> {
fn op(&mut self, public: bool, op: Operation) -> Result<Statement> {
use NativeOperation::*;
let mut op = Self::fill_in_aux(Self::lower_op(op))?;
let Operation(op_type, ref mut args, _) = &mut op;
@ -301,7 +308,7 @@ impl MainPodBuilder {
// We are dealing with a copy here.
match (args).first() {
Some(OperationArg::Statement(s)) if args.len() == 1 => Ok(s.predicate().clone()),
_ => Err(anyhow!("Invalid arguments to copy operation: {:?}", args)),
_ => Err(Error::op_invalid_args("copy".to_string())),
}
})?;
@ -312,7 +319,7 @@ impl MainPodBuilder {
CopyStatement => match &args[0] {
OperationArg::Statement(s) => s.args().clone(),
_ => {
return Err(anyhow!("Invalid arguments to copy operation: {}", op));
return Err(Error::op_invalid_args("copy".to_string()));
}
},
EqualFromEntries => self.op_args_entries(public, args)?,
@ -331,14 +338,14 @@ impl MainPodBuilder {
if ak1 == ak2 {
vec![StatementArg::Key(ak0), StatementArg::Key(ak3)]
} else {
return Err(anyhow!(
"Invalid arguments to transitive equality operation"
return Err(Error::op_invalid_args(
"transitivity equality".to_string(),
));
}
}
_ => {
return Err(anyhow!(
"Invalid arguments to transitive equality operation"
return Err(Error::op_invalid_args(
"transitivity equality".to_string(),
));
}
}
@ -348,7 +355,7 @@ impl MainPodBuilder {
vec![StatementArg::Key(ak0), StatementArg::Key(ak1)]
}
_ => {
return Err(anyhow!("Invalid arguments to gt-to-neq operation"));
return Err(Error::op_invalid_args("gt-to-neq".to_string()));
}
},
LtToNotEqual => match args[0].clone() {
@ -356,7 +363,7 @@ impl MainPodBuilder {
vec![StatementArg::Key(ak0), StatementArg::Key(ak1)]
}
_ => {
return Err(anyhow!("Invalid arguments to lt-to-neq operation"));
return Err(Error::op_invalid_args("lt-to-neq".to_string()));
}
},
SumOf => match (args[0].clone(), args[1].clone(), args[2].clone()) {
@ -375,11 +382,11 @@ impl MainPodBuilder {
StatementArg::Key(ak2),
]
} else {
return Err(anyhow!("Invalid arguments to sum-of operation"));
return Err(Error::op_invalid_args("sum-of".to_string()));
}
}
_ => {
return Err(anyhow!("Invalid arguments to sum-of operation"));
return Err(Error::op_invalid_args("sum-of".to_string()));
}
},
ProductOf => match (args[0].clone(), args[1].clone(), args[2].clone()) {
@ -398,11 +405,11 @@ impl MainPodBuilder {
StatementArg::Key(ak2),
]
} else {
return Err(anyhow!("Invalid arguments to product-of operation"));
return Err(Error::op_invalid_args("product-of".to_string()));
}
}
_ => {
return Err(anyhow!("Invalid arguments to product-of operation"));
return Err(Error::op_invalid_args("product-of".to_string()));
}
},
MaxOf => match (args[0].clone(), args[1].clone(), args[2].clone()) {
@ -421,11 +428,11 @@ impl MainPodBuilder {
StatementArg::Key(ak2),
]
} else {
return Err(anyhow!("Invalid arguments to max-of operation"));
return Err(Error::op_invalid_args("max-of".to_string()));
}
}
_ => {
return Err(anyhow!("Invalid arguments to max-of operation"));
return Err(Error::op_invalid_args("max-of".to_string()));
}
},
ContainsFromEntries => self.op_args_entries(public, args)?,
@ -441,17 +448,17 @@ impl MainPodBuilder {
OperationType::Custom(cpr) => {
let pred = &cpr.batch.predicates[cpr.index];
if pred.statements.len() != args.len() {
return Err(anyhow!(
return Err(Error::custom(format!(
"Custom predicate operation needs {} statements but has {}.",
pred.statements.len(),
args.len()
));
)));
}
// All args should be statements to be pattern matched against statement templates.
let args = args.iter().map(
|a| match a {
OperationArg::Statement(s) => Ok(s.clone()),
_ => Err(anyhow!("Invalid argument {} to operation corresponding to custom predicate {:?}.", a, cpr))
_ => Err(Error::custom(format!("Invalid argument {} to operation corresponding to custom predicate {:?}.", a, cpr)))
}
).collect::<Result<Vec<_>>>()?;
@ -462,7 +469,7 @@ impl MainPodBuilder {
for (st_tmpl_arg, st_arg) in st_tmpl.args.iter().zip(&st_args) {
if !check_st_tmpl(st_tmpl_arg, st_arg, &mut wildcard_map) {
// TODO: Add wildcard_map in the error for better context
return Err(anyhow!("{} doesn't match {}", st, st_tmpl));
return Err(Error::statements_dont_match(st.clone(), st_tmpl.clone()));
}
}
}
@ -563,7 +570,11 @@ impl MainPodBuilder {
}
_ => None,
})
.ok_or(anyhow!("Missing POD type information in POD: {:?}", pod))?;
.ok_or(Error::custom(format!(
// TODO use a specific Error
"Missing POD type information in POD: {:?}",
pod
)))?;
// Replace instances of `SELF` with the POD ID for consistency
// with `pub_statements` method.
let public_statements = [type_statement]
@ -676,7 +687,7 @@ impl MainPodCompiler {
op.1.iter()
.flat_map(|arg| self.compile_op_arg(arg))
.collect_vec();
middleware::Operation::op(op.0.clone(), &mop_args, &op.2)
Ok(middleware::Operation::op(op.0.clone(), &mop_args, &op.2)?)
}
fn compile_st_op(&mut self, st: &Statement, op: &Operation, params: &Params) -> Result<()> {
@ -684,11 +695,10 @@ impl MainPodCompiler {
let is_correct = middle_op.check(params, st)?;
if !is_correct {
// todo: improve error handling
Err(anyhow!(
Err(Error::custom(format!(
"Compile failed due to invalid deduction:\n {} ⇏ {}",
middle_op,
st
))
middle_op, st
)))
} else {
self.push_st_op(st.clone(), middle_op);
Ok(())
@ -822,11 +832,10 @@ pub mod tests {
if kvs == embedded_kvs {
Ok(())
} else {
Err(anyhow!(
Err(Error::custom(format!(
"KVs {:?} do not agree with those embedded in the POD: {:?}",
kvs,
embedded_kvs
))
kvs, embedded_kvs
)))
}
}