diff --git a/Cargo.toml b/Cargo.toml index 7f08a55..1a0778c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ dyn-clone = "1.0.18" log = "0.4" env_logger = "0.11" lazy_static = "1.5.0" +thiserror = { version = "2.0.12" } # enabled by features: plonky2 = { git = "https://github.com/0xPolygonZero/plonky2", optional = true } serde = "1.0.219" diff --git a/src/backends/plonky2/circuits/common.rs b/src/backends/plonky2/circuits/common.rs index 240e2eb..cd12f63 100644 --- a/src/backends/plonky2/circuits/common.rs +++ b/src/backends/plonky2/circuits/common.rs @@ -2,7 +2,6 @@ use std::{array, iter}; -use anyhow::Result; use plonky2::{ field::{ extension::Extendable, @@ -19,6 +18,7 @@ use plonky2::{ use crate::{ backends::plonky2::{ basetypes::D, + error::Result, mainpod::{Operation, OperationArg, Statement}, primitives::merkletree::MerkleClaimAndProofTarget, }, @@ -75,7 +75,7 @@ impl StatementArgTarget { params: &Params, arg: &StatementArg, ) -> Result<()> { - pw.set_target_arr(&self.elements, &arg.to_fields(params)) + Ok(pw.set_target_arr(&self.elements, &arg.to_fields(params))?) } fn new(first: ValueTarget, second: ValueTarget) -> Self { diff --git a/src/backends/plonky2/circuits/mainpod.rs b/src/backends/plonky2/circuits/mainpod.rs index de8b06e..b2a7a38 100644 --- a/src/backends/plonky2/circuits/mainpod.rs +++ b/src/backends/plonky2/circuits/mainpod.rs @@ -1,4 +1,3 @@ -use anyhow::Result; use itertools::zip_eq; use plonky2::{ hash::{hash_types::HashOutTarget, poseidon::PoseidonHash}, @@ -16,6 +15,7 @@ use crate::{ }, signedpod::{SignedPodVerifyGadget, SignedPodVerifyTarget}, }, + error::Result, mainpod, primitives::merkletree::{ MerkleClaimAndProof, MerkleClaimAndProofTarget, MerkleProofGadget, diff --git a/src/backends/plonky2/circuits/signedpod.rs b/src/backends/plonky2/circuits/signedpod.rs index 73a0c37..3b06d19 100644 --- a/src/backends/plonky2/circuits/signedpod.rs +++ b/src/backends/plonky2/circuits/signedpod.rs @@ -1,6 +1,5 @@ use std::iter; -use anyhow::Result; use itertools::Itertools; use plonky2::{ hash::hash_types::{HashOut, HashOutTarget}, @@ -15,6 +14,7 @@ use crate::{ backends::plonky2::{ basetypes::D, circuits::common::{CircuitBuilderPod, StatementArgTarget, StatementTarget, ValueTarget}, + error::Result, primitives::{ merkletree::{ MerkleClaimAndProof, MerkleProofExistenceGadget, MerkleProofExistenceTarget, diff --git a/src/backends/plonky2/error.rs b/src/backends/plonky2/error.rs new file mode 100644 index 0000000..088591d --- /dev/null +++ b/src/backends/plonky2/error.rs @@ -0,0 +1,93 @@ +use std::{backtrace::Backtrace, fmt::Debug}; + +use crate::middleware::{PodId, PodType, Value}; + +pub type Result = core::result::Result; + +#[derive(thiserror::Error, Debug)] +pub enum InnerError { + #[error("id does not match, expected {0}, found {1}")] + IdNotEqual(PodId, PodId), + #[error("type does not match, expected {0}, found {1}")] + TypeNotEqual(PodType, Value), + + // POD related + #[error("invalid POD ID")] + PodIdInvalid, + #[error("verification failed: POD does not have type statement")] + NotTypeStatement, + #[error("repeated ValueOf")] + RepeatedValueOf, + #[error("Statement did not check")] + StatementNotCheck, + #[error("Key not found")] + KeyNotFound, + + // Other + #[error("{0}")] + Custom(String), +} + +#[derive(thiserror::Error)] +pub enum Error { + #[error("Inner: {inner}\n{backtrace}")] + Inner { + inner: Box, + backtrace: Box, + }, + #[error("anyhow::Error: {0}")] + Anyhow(#[from] anyhow::Error), + #[error("Plonky2 proof failed to verify: {0}")] + Plonky2ProofFail(anyhow::Error), + #[error("base64::DecodeError: {0}")] + Base64Decode(#[from] base64::DecodeError), + #[error(transparent)] + Tree(#[from] crate::backends::plonky2::primitives::merkletree::error::TreeError), + #[error(transparent)] + Middleware(#[from] crate::middleware::Error), +} + +impl Debug for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self, f) + } +} + +macro_rules! new { + ($inner:expr) => { + Error::Inner { + inner: Box::new($inner), + backtrace: Box::new(Backtrace::capture()), + } + }; +} +use InnerError::*; +impl Error { + pub(crate) fn custom(s: String) -> Self { + new!(Custom(s)) + } + pub(crate) fn plonky2_proof_fail(e: anyhow::Error) -> Self { + Self::Plonky2ProofFail(e) + } + pub(crate) fn key_not_found() -> Self { + new!(KeyNotFound) + } + pub(crate) fn statement_not_check() -> Self { + new!(StatementNotCheck) + } + pub(crate) fn repeated_value_of() -> Self { + new!(RepeatedValueOf) + } + pub(crate) fn not_type_statement() -> Self { + new!(NotTypeStatement) + } + pub(crate) fn pod_id_invalid() -> Self { + new!(PodIdInvalid) + } + pub(crate) fn id_not_equal(expected: PodId, found: PodId) -> Self { + new!(IdNotEqual(expected, found)) + } + pub(crate) fn type_not_equal(expected: PodType, found: Value) -> Self { + new!(TypeNotEqual(expected, found)) + } +} diff --git a/src/backends/plonky2/mainpod/mod.rs b/src/backends/plonky2/mainpod/mod.rs index 63d059f..c3324a3 100644 --- a/src/backends/plonky2/mainpod/mod.rs +++ b/src/backends/plonky2/mainpod/mod.rs @@ -2,7 +2,6 @@ pub mod operation; pub mod statement; use std::any::Any; -use anyhow::{anyhow, Result}; use itertools::Itertools; pub use operation::*; use plonky2::{ @@ -19,12 +18,13 @@ use crate::{ backends::plonky2::{ basetypes::{C, D}, circuits::mainpod::{MainPodVerifyCircuit, MainPodVerifyInput}, + error::{Error, Result}, primitives::merkletree::MerkleClaimAndProof, signedpod::SignedPod, }, middleware::{ - self, AnchoredKey, Hash, MainPodInputs, NativeOperation, NonePod, OperationType, Params, - Pod, PodId, PodProver, PodType, StatementArg, ToFields, F, KEY_TYPE, SELF, + self, AnchoredKey, DynError, Hash, MainPodInputs, NativeOperation, NonePod, OperationType, + Params, Pod, PodId, PodProver, PodType, StatementArg, ToFields, F, KEY_TYPE, SELF, }, }; @@ -70,11 +70,11 @@ pub(crate) fn extract_merkle_proofs( }) .collect(); if merkle_proofs.len() > params.max_merkle_proofs { - return Err(anyhow!( + return Err(Error::custom(format!( "The number of required Merkle proofs ({}) exceeds the maximum number ({}).", merkle_proofs.len(), params.max_merkle_proofs - )); + ))); } Ok(merkle_proofs) } @@ -90,10 +90,10 @@ fn find_op_arg(statements: &[Statement], op_arg: &middleware::Statement) -> Resu (&middleware::Statement::try_from(s.clone()).ok()? == op_arg).then_some(i) }) .map(OperationArg::Index) - .ok_or(anyhow!( + .ok_or(Error::custom(format!( "Statement corresponding to op arg {} not found", op_arg - )), + ))), } } @@ -109,10 +109,10 @@ fn find_op_aux( .enumerate() .find_map(|(i, pf)| (pf.proof == *pf_arg).then_some(i)) .map(OperationAux::MerkleProofIndex) - .ok_or(anyhow!( + .ok_or(Error::custom(format!( "Merkle proof corresponding to op arg {} not found", op_aux - )), + ))), } } @@ -279,8 +279,8 @@ pub(crate) fn process_public_statements_operations( pub struct Prover {} -impl PodProver for Prover { - fn prove(&mut self, params: &Params, inputs: MainPodInputs) -> Result> { +impl Prover { + fn _prove(&mut self, params: &Params, inputs: MainPodInputs) -> Result { let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); let main_pod = MainPodVerifyCircuit { @@ -328,12 +328,22 @@ impl PodProver for Prover { let data = builder.build::(); let proof = data.prove(pw)?; - Ok(Box::new(MainPod { + Ok(MainPod { params: params.clone(), id, public_statements, proof, - })) + }) + } +} + +impl PodProver for Prover { + fn prove( + &mut self, + params: &Params, + inputs: MainPodInputs, + ) -> Result, Box> { + Ok(self._prove(params, inputs).map(Box::new)?) } } @@ -364,16 +374,12 @@ pub(crate) fn normalize_statement(statement: &Statement, self_id: PodId) -> midd .unwrap() } -impl Pod for MainPod { - fn verify(&self) -> Result<()> { +impl MainPod { + fn _verify(&self) -> Result<()> { // 2. get the id out of the public statements let id: PodId = PodId(hash_statements(&self.public_statements, &self.params)); if id != self.id { - return Err(anyhow!( - "id does not match, expected {}, computed {}", - self.id, - id - )); + return Err(Error::id_not_equal(self.id, id)); } // 1, 3, 4, 5 verification via the zkSNARK proof @@ -387,7 +393,13 @@ impl Pod for MainPod { let data = builder.build::(); data.verify(self.proof.clone()) - .map_err(|e| anyhow!("MainPod proof verification failure: {:?}", e)) + .map_err(|e| Error::custom(format!("MainPod proof verification failure: {:?}", e))) + } +} + +impl Pod for MainPod { + fn verify(&self) -> Result<(), Box> { + Ok(self._verify()?) } fn id(&self) -> PodId { @@ -418,13 +430,14 @@ pub mod tests { signedpod::Signer, }, examples::{zu_kyc_pod_builder, zu_kyc_sign_pod_builders}, - frontend, middleware, + frontend::{self}, + middleware, middleware::RawValue, op, }; #[test] - fn test_main_zu_kyc() -> Result<()> { + fn test_main_zu_kyc() -> frontend::Result<()> { let params = middleware::Params { // Currently the circuit uses random access that only supports vectors of length 64. // With max_input_main_pods=3 we need random access to a vector of length 73. @@ -447,7 +460,7 @@ pub mod tests { let kyc_pod = kyc_builder.prove(&mut prover, ¶ms)?; let pod = (kyc_pod.pod as Box).downcast::().unwrap(); - pod.verify() + Ok(pod.verify()?) } #[test] diff --git a/src/backends/plonky2/mainpod/operation.rs b/src/backends/plonky2/mainpod/operation.rs index 80a6825..1ba2069 100644 --- a/src/backends/plonky2/mainpod/operation.rs +++ b/src/backends/plonky2/mainpod/operation.rs @@ -1,11 +1,14 @@ use std::fmt; -use anyhow::{anyhow, Result}; use plonky2::field::types::Field; use serde::{Deserialize, Serialize}; use crate::{ - backends::plonky2::{mainpod::Statement, primitives::merkletree::MerkleClaimAndProof}, + backends::plonky2::{ + error::{Error, Result}, + mainpod::Statement, + primitives::merkletree::MerkleClaimAndProof, + }, middleware::{self, OperationType, Params, ToFields, F}, }; @@ -78,12 +81,16 @@ impl Operation { OperationAux::MerkleProofIndex(i) => crate::middleware::OperationAux::MerkleProof( merkle_proofs .get(i) - .ok_or(anyhow!("Missing Merkle proof index {}", i))? + .ok_or(Error::custom(format!("Missing Merkle proof index {}", i)))? .proof .clone(), ), }; - middleware::Operation::op(self.0.clone(), &deref_args, &deref_aux) + Ok(middleware::Operation::op( + self.0.clone(), + &deref_args, + &deref_aux, + )?) } } diff --git a/src/backends/plonky2/mainpod/statement.rs b/src/backends/plonky2/mainpod/statement.rs index 6b41637..b1fc4d2 100644 --- a/src/backends/plonky2/mainpod/statement.rs +++ b/src/backends/plonky2/mainpod/statement.rs @@ -1,10 +1,10 @@ use std::fmt; -use anyhow::{anyhow, Result}; use serde::{Deserialize, Serialize}; -use crate::middleware::{ - self, NativePredicate, Params, Predicate, StatementArg, ToFields, WildcardValue, +use crate::{ + backends::plonky2::error::{Error, Result}, + middleware::{self, NativePredicate, Params, Predicate, StatementArg, ToFields, WildcardValue}, }; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -36,7 +36,7 @@ impl ToFields for Statement { } impl TryFrom for middleware::Statement { - type Error = anyhow::Error; + type Error = Error; fn try_from(s: Statement) -> Result { type S = middleware::Statement; type NP = NativePredicate; @@ -78,7 +78,10 @@ impl TryFrom for middleware::Statement { (NP::MaxOf, (Some(SA::Key(ak1)), Some(SA::Key(ak2)), Some(SA::Key(ak3))), 3) => { S::MaxOf(ak1, ak2, ak3) } - _ => Err(anyhow!("Ill-formed statement expression {:?}", s))?, + _ => Err(Error::custom(format!( + "Ill-formed statement expression {:?}", + s + )))?, }, Predicate::Custom(cpr) => { let vs: Vec = proper_args diff --git a/src/backends/plonky2/mock/mainpod.rs b/src/backends/plonky2/mock/mainpod.rs index 9d744f5..08960e7 100644 --- a/src/backends/plonky2/mock/mainpod.rs +++ b/src/backends/plonky2/mock/mainpod.rs @@ -4,12 +4,12 @@ use std::fmt; -use anyhow::{anyhow, Result}; use base64::{prelude::BASE64_STANDARD, Engine}; use serde::{Deserialize, Serialize}; use crate::{ backends::plonky2::{ + error::{Error, Result}, mainpod::{ extract_merkle_proofs, hash_statements, layout_statements, normalize_statement, process_private_statements_operations, process_public_statements_operations, Operation, @@ -18,15 +18,19 @@ use crate::{ primitives::merkletree::MerkleClaimAndProof, }, middleware::{ - self, hash_str, AnchoredKey, MainPodInputs, NativePredicate, Params, Pod, PodId, PodProver, - Predicate, StatementArg, KEY_TYPE, SELF, + self, hash_str, AnchoredKey, DynError, MainPodInputs, NativePredicate, Params, Pod, PodId, + PodProver, Predicate, StatementArg, KEY_TYPE, SELF, }, }; pub struct MockProver {} impl PodProver for MockProver { - fn prove(&mut self, params: &Params, inputs: MainPodInputs) -> Result> { + fn prove( + &mut self, + params: &Params, + inputs: MainPodInputs, + ) -> Result, Box> { Ok(Box::new(MockMainPod::new(params, inputs)?)) } } @@ -177,10 +181,8 @@ impl MockMainPod { Ok(pod) } -} -impl Pod for MockMainPod { - fn verify(&self) -> Result<()> { + fn _verify(&self) -> Result<()> { // 1. TODO: Verify input pods let input_statement_offset = self.offset_input_statements(); @@ -245,24 +247,28 @@ impl Pod for MockMainPod { .unwrap() .check_and_log(&self.params, &s.clone().try_into().unwrap()) }) - .collect::>>() + .collect::, middleware::Error>>() .unwrap(); if !ids_match { - return Err(anyhow!("Verification failed: POD ID is incorrect.")); + return Err(Error::pod_id_invalid()); } if !has_type_statement { - return Err(anyhow!( - "Verification failed: POD does not have type statement." - )); + return Err(Error::not_type_statement()); } if !value_ofs_unique { - return Err(anyhow!("Verification failed: Repeated ValueOf")); + return Err(Error::repeated_value_of()); } if !statement_check.iter().all(|b| *b) { - return Err(anyhow!("Verification failed: Statement did not check.")); + return Err(Error::statement_not_check()); } Ok(()) } +} + +impl Pod for MockMainPod { + fn verify(&self) -> Result<(), Box> { + Ok(self._verify()?) + } fn id(&self) -> PodId { self.id } @@ -293,11 +299,12 @@ pub mod tests { great_boy_pod_full_flow, tickets_pod_full_flow, zu_kyc_pod_builder, zu_kyc_sign_pod_builders, }, + frontend, middleware::{self}, }; #[test] - fn test_mock_main_zu_kyc() -> Result<()> { + fn test_mock_main_zu_kyc() -> frontend::Result<()> { let params = middleware::Params::default(); let (gov_id_builder, pay_stub_builder, sanction_list_builder) = zu_kyc_sign_pod_builders(¶ms); @@ -331,7 +338,7 @@ pub mod tests { } #[test] - fn test_mock_main_great_boy() -> Result<()> { + fn test_mock_main_great_boy() -> frontend::Result<()> { let params = middleware::Params::default(); let great_boy_builder = great_boy_pod_full_flow()?; @@ -349,7 +356,7 @@ pub mod tests { } #[test] - fn test_mock_main_tickets() -> Result<()> { + fn test_mock_main_tickets() -> frontend::Result<()> { let params = middleware::Params::default(); let tickets_builder = tickets_pod_full_flow()?; let mut prover = MockProver {}; diff --git a/src/backends/plonky2/mock/signedpod.rs b/src/backends/plonky2/mock/signedpod.rs index 90c0f5f..0f862ee 100644 --- a/src/backends/plonky2/mock/signedpod.rs +++ b/src/backends/plonky2/mock/signedpod.rs @@ -1,14 +1,16 @@ use std::collections::HashMap; -use anyhow::{anyhow, Result}; use itertools::Itertools; use crate::{ - backends::plonky2::primitives::merkletree::MerkleTree, + backends::plonky2::{ + error::{Error, Result}, + primitives::merkletree::MerkleTree, + }, constants::MAX_DEPTH, middleware::{ - containers::Dictionary, hash_str, AnchoredKey, Hash, Key, Params, Pod, PodId, PodSigner, - PodType, RawValue, Statement, Value, KEY_SIGNER, KEY_TYPE, + containers::Dictionary, hash_str, AnchoredKey, DynError, Hash, Key, Params, Pod, PodId, + PodSigner, PodType, RawValue, Statement, Value, KEY_SIGNER, KEY_TYPE, }, }; @@ -22,8 +24,8 @@ impl MockSigner { } } -impl PodSigner for MockSigner { - fn sign(&mut self, _params: &Params, kvs: &HashMap) -> Result> { +impl MockSigner { + fn _sign(&mut self, _params: &Params, kvs: &HashMap) -> Result { let mut kvs = kvs.clone(); let pubkey = self.pubkey(); kvs.insert(Key::from(KEY_SIGNER), Value::from(pubkey)); @@ -32,7 +34,17 @@ impl PodSigner for MockSigner { let dict = Dictionary::new(kvs.clone())?; let id = PodId(dict.commitment()); let signature = format!("{}_signed_by_{}", id, pubkey); - Ok(Box::new(MockSignedPod { id, signature, kvs })) + Ok(MockSignedPod { id, signature, kvs }) + } +} + +impl PodSigner for MockSigner { + fn sign( + &mut self, + params: &Params, + kvs: &HashMap, + ) -> Result, Box> { + Ok(self._sign(params, kvs).map(Box::new)?) } } @@ -49,8 +61,8 @@ impl MockSignedPod { } } -impl Pod for MockSignedPod { - fn verify(&self) -> Result<()> { +impl MockSignedPod { + fn _verify(&self) -> Result<()> { // 1. Verify id let mt = MerkleTree::new( MAX_DEPTH, @@ -62,23 +74,18 @@ impl Pod for MockSignedPod { )?; let id = PodId(mt.root()); if id != self.id { - return Err(anyhow!( - "id does not match, expected {}, computed {}", - self.id, - id - )); + return Err(Error::id_not_equal(self.id, id)); } // 2. Verify type let value_at_type = self .kvs .get(&Key::from(KEY_TYPE)) - .ok_or(anyhow!("key not found"))?; + .ok_or(Error::key_not_found())?; if &Value::from(PodType::MockSigned) != value_at_type { - return Err(anyhow!( - "type does not match, expected MockSigned ({}), found {}", + return Err(Error::type_not_equal( PodType::MockSigned, - value_at_type + value_at_type.clone(), )); } @@ -86,18 +93,23 @@ impl Pod for MockSignedPod { let pk_hash = self .kvs .get(&Key::from(KEY_SIGNER)) - .ok_or(anyhow!("key not found"))?; + .ok_or(Error::key_not_found())?; let signature = format!("{}_signed_by_{}", id, pk_hash); if signature != self.signature { - return Err(anyhow!( + return Err(Error::custom(format!( "signature does not match, expected {}, computed {}", - self.id, - id - )); + self.id, id + ))); } Ok(()) } +} + +impl Pod for MockSignedPod { + fn verify(&self) -> Result<(), Box> { + Ok(self._verify()?) + } fn id(&self) -> PodId { self.id @@ -149,7 +161,7 @@ pub mod tests { .downcast::() .unwrap(); - pod.verify()?; + pod._verify()?; println!("id: {}", pod.id()); println!("kvs: {:?}", pod.kvs()); diff --git a/src/backends/plonky2/mod.rs b/src/backends/plonky2/mod.rs index abc940e..23cda93 100644 --- a/src/backends/plonky2/mod.rs +++ b/src/backends/plonky2/mod.rs @@ -1,6 +1,9 @@ pub mod basetypes; pub mod circuits; +mod error; pub mod mainpod; pub mod mock; pub mod primitives; pub mod signedpod; + +pub use error::*; diff --git a/src/backends/plonky2/primitives/merkletree_circuit.rs b/src/backends/plonky2/primitives/merkletree/circuit.rs similarity index 99% rename from src/backends/plonky2/primitives/merkletree_circuit.rs rename to src/backends/plonky2/primitives/merkletree/circuit.rs index 68a24ba..6c096a2 100644 --- a/src/backends/plonky2/primitives/merkletree_circuit.rs +++ b/src/backends/plonky2/primitives/merkletree/circuit.rs @@ -10,7 +10,6 @@ //! use std::iter; -use anyhow::Result; use plonky2::{ field::types::Field, hash::{ @@ -28,6 +27,7 @@ use crate::{ backends::plonky2::{ basetypes::D, circuits::common::{CircuitBuilderPod, ValueTarget}, + error::Result, primitives::merkletree::MerkleClaimAndProof, }, middleware::{EMPTY_HASH, EMPTY_VALUE, F, HASH_SIZE}, @@ -408,7 +408,10 @@ pub mod tests { use super::*; use crate::{ - backends::plonky2::{basetypes::C, primitives::merkletree::*}, + backends::plonky2::{ + basetypes::C, + primitives::merkletree::{keypath, kv_hash, MerkleTree}, + }, middleware::{hash_value, RawValue}, }; @@ -693,6 +696,8 @@ pub mod tests { assert_eq!( MerkleTree::verify(max_depth, tree2.root(), &proof, &key, &value) .unwrap_err() + .inner() + .unwrap() .to_string(), "proof of inclusion does not verify" ); diff --git a/src/backends/plonky2/primitives/merkletree/error.rs b/src/backends/plonky2/primitives/merkletree/error.rs new file mode 100644 index 0000000..f856883 --- /dev/null +++ b/src/backends/plonky2/primitives/merkletree/error.rs @@ -0,0 +1,79 @@ +//! tree errors + +use std::{backtrace::Backtrace, fmt::Debug}; + +pub type TreeResult = core::result::Result; + +#[derive(Debug, thiserror::Error)] +pub enum TreeInnerError { + #[error("key not found")] + KeyNotFound, + #[error("key already exists")] + KeyExists, + #[error("max depth reached")] + MaxDepth, + #[error("reached empty node, should not have entered")] + EmptyNode, + #[error("proof of {0} does not verify")] + ProofFail(String), // inclusion / exclusion + #[error("invalid {0} proof")] + InvalidProof(String), + #[error("key too short (key length: {0}) for the max_depth: {1}")] + TooShortKey(usize, usize), +} + +#[derive(thiserror::Error)] +pub enum TreeError { + #[error("Inner: {inner}\n{backtrace}")] + Inner { + inner: Box, + backtrace: Box, + }, + #[error("anyhow::Error: {0}")] + Anyhow(#[from] anyhow::Error), +} + +impl Debug for TreeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self, f) + } +} + +macro_rules! new { + ($inner:expr) => { + TreeError::Inner { + inner: Box::new($inner), + backtrace: Box::new(Backtrace::capture()), + } + }; +} +use TreeInnerError::*; +impl TreeError { + pub fn inner(&self) -> Option<&TreeInnerError> { + match self { + Self::Inner { inner, .. } => Some(inner), + _ => None, + } + } + pub(crate) fn key_not_found() -> Self { + new!(KeyNotFound) + } + pub(crate) fn key_exists() -> Self { + new!(KeyExists) + } + pub(crate) fn max_depth() -> Self { + new!(MaxDepth) + } + pub(crate) fn empty_node() -> Self { + new!(EmptyNode) + } + pub(crate) fn proof_fail(obj: String) -> Self { + new!(ProofFail(obj)) + } + pub(crate) fn invalid_proof(obj: String) -> Self { + new!(InvalidProof(obj)) + } + pub(crate) fn too_short_key(depth: usize, max_depth: usize) -> Self { + new!(TooShortKey(depth, max_depth)) + } +} diff --git a/src/backends/plonky2/primitives/merkletree.rs b/src/backends/plonky2/primitives/merkletree/mod.rs similarity index 92% rename from src/backends/plonky2/primitives/merkletree.rs rename to src/backends/plonky2/primitives/merkletree/mod.rs index ce5bf98..7824fb7 100644 --- a/src/backends/plonky2/primitives/merkletree.rs +++ b/src/backends/plonky2/primitives/merkletree/mod.rs @@ -2,13 +2,16 @@ //! https://0xparc.github.io/pod2/merkletree.html . use std::{collections::HashMap, fmt, iter::IntoIterator}; -use anyhow::{anyhow, Result}; use plonky2::field::types::Field; use serde::{Deserialize, Serialize}; -pub use super::merkletree_circuit::*; use crate::middleware::{hash_fields, Hash, RawValue, EMPTY_HASH, EMPTY_VALUE, F}; +pub mod circuit; +pub use circuit::*; +pub mod error; +pub use error::{TreeError, TreeResult}; + /// Implements the MerkleTree specified at /// https://0xparc.github.io/pod2/merkletree.html #[derive(Clone, Debug)] @@ -19,12 +22,12 @@ pub struct MerkleTree { impl MerkleTree { /// builds a new `MerkleTree` where the leaves contain the given key-values - pub fn new(max_depth: usize, kvs: &HashMap) -> Result { + pub fn new(max_depth: usize, kvs: &HashMap) -> TreeResult { // Construct leaves. let mut leaves: Vec<_> = kvs .iter() .map(|(k, v)| Leaf::new(max_depth, *k, *v)) - .collect::>()?; + .collect::>()?; // Start with a leaf or conclude with an empty node as root. let mut root = leaves.pop().map(Node::Leaf).unwrap_or(Node::None); @@ -50,17 +53,17 @@ impl MerkleTree { } /// returns the value at the given key - pub fn get(&self, key: &RawValue) -> Result { + pub fn get(&self, key: &RawValue) -> TreeResult { let path = keypath(self.max_depth, *key)?; let key_resolution = self.root.down(0, self.max_depth, path, None)?; match key_resolution { Some((k, v)) if &k == key => Ok(v), - _ => Err(anyhow!("key not found")), + _ => Err(TreeError::key_not_found()), } } /// returns a boolean indicating whether the key exists in the tree - pub fn contains(&self, key: &RawValue) -> Result { + pub fn contains(&self, key: &RawValue) -> TreeResult { let path = keypath(self.max_depth, *key)?; match self.root.down(0, self.max_depth, path, None) { Ok(Some((k, _))) => { @@ -77,7 +80,7 @@ impl MerkleTree { /// returns a proof of existence, which proves that the given key exists in /// the tree. It returns the `value` of the leaf at the given `key`, and the /// `MerkleProof`. - pub fn prove(&self, key: &RawValue) -> Result<(RawValue, MerkleProof)> { + pub fn prove(&self, key: &RawValue) -> TreeResult<(RawValue, MerkleProof)> { let path = keypath(self.max_depth, *key)?; let mut siblings: Vec = Vec::new(); @@ -94,7 +97,7 @@ impl MerkleTree { other_leaf: None, }, )), - _ => Err(anyhow!("key not found")), + _ => Err(TreeError::key_not_found()), } } @@ -102,7 +105,7 @@ impl MerkleTree { /// `key` does not exist in the tree. The return value specifies /// the key-value pair in the leaf reached as a result of /// resolving `key` as well as a `MerkleProof`. - pub fn prove_nonexistence(&self, key: &RawValue) -> Result { + pub fn prove_nonexistence(&self, key: &RawValue) -> TreeResult { let path = keypath(self.max_depth, *key)?; let mut siblings: Vec = Vec::new(); @@ -124,7 +127,7 @@ impl MerkleTree { siblings, other_leaf: Some((k, v)), }), - _ => Err(anyhow!("key found")), + _ => Err(TreeError::key_not_found()), } // both cases prove that the given key don't exist in the tree. ∎ } @@ -136,11 +139,11 @@ impl MerkleTree { proof: &MerkleProof, key: &RawValue, value: &RawValue, - ) -> Result<()> { + ) -> TreeResult<()> { let h = proof.compute_root_from_leaf(max_depth, key, Some(*value))?; if h != root { - Err(anyhow!("proof of inclusion does not verify")) + Err(TreeError::proof_fail("inclusion".to_string())) } else { Ok(()) } @@ -153,16 +156,18 @@ impl MerkleTree { root: Hash, proof: &MerkleProof, key: &RawValue, - ) -> Result<()> { + ) -> TreeResult<()> { match proof.other_leaf { - Some((k, _v)) if &k == key => Err(anyhow!("Invalid non-existence proof.")), + Some((k, _v)) if &k == key => { + Err(TreeError::invalid_proof("non-existence".to_string())) + } _ => { let k = proof.other_leaf.map(|(k, _)| k).unwrap_or(*key); let v: Option = proof.other_leaf.map(|(_, v)| v); let h = proof.compute_root_from_leaf(max_depth, &k, v)?; if h != root { - Err(anyhow!("proof of exclusion does not verify")) + Err(TreeError::proof_fail("exclusion".to_string())) } else { Ok(()) } @@ -240,9 +245,9 @@ impl MerkleProof { max_depth: usize, key: &RawValue, value: Option, - ) -> Result { + ) -> TreeResult { if self.siblings.len() >= max_depth { - return Err(anyhow!("max depth reached")); + return Err(TreeError::max_depth()); } let path = keypath(max_depth, *key)?; @@ -372,9 +377,9 @@ impl Node { max_depth: usize, path: Vec, mut siblings: Option<&mut Vec>, - ) -> Result> { + ) -> TreeResult> { if lvl >= max_depth { - return Err(anyhow!("max depth reached")); + return Err(TreeError::max_depth()); } match self { @@ -402,9 +407,9 @@ impl Node { } // adds the leaf at the tree from the current node (self), without computing any hash - pub(crate) fn add_leaf(&mut self, lvl: usize, max_depth: usize, leaf: Leaf) -> Result<()> { + pub(crate) fn add_leaf(&mut self, lvl: usize, max_depth: usize, leaf: Leaf) -> TreeResult<()> { if lvl >= max_depth { - return Err(anyhow!("max depth reached")); + return Err(TreeError::max_depth()); } match self { @@ -436,7 +441,7 @@ impl Node { // Note: current approach returns an error when trying to // add to a leaf where the key already exists. We could also // ignore it if needed. - return Err(anyhow!("key already exists")); + return Err(TreeError::key_exists()); } let old_leaf = l.clone(); // set self as an intermediate node @@ -444,7 +449,7 @@ impl Node { return self.down_till_divergence(lvl, max_depth, old_leaf, leaf); } Self::None => { - return Err(anyhow!("reached empty node, should not have entered")); + return Err(TreeError::empty_node()); } } Ok(()) @@ -460,9 +465,9 @@ impl Node { max_depth: usize, old_leaf: Leaf, new_leaf: Leaf, - ) -> Result<()> { + ) -> TreeResult<()> { if lvl >= max_depth { - return Err(anyhow!("max depth reached")); + return Err(TreeError::max_depth()); } if let Node::Intermediate(ref mut n) = self { @@ -535,7 +540,7 @@ struct Leaf { value: RawValue, } impl Leaf { - fn new(max_depth: usize, key: RawValue, value: RawValue) -> Result { + fn new(max_depth: usize, key: RawValue, value: RawValue) -> TreeResult { Ok(Self { hash: None, path: keypath(max_depth, key)?, @@ -560,17 +565,13 @@ impl Leaf { // max-depth? ie, what happens when two keys share the same path for more bits // than the max_depth? /// returns the path of the given key -pub(crate) fn keypath(max_depth: usize, k: RawValue) -> Result> { +pub(crate) fn keypath(max_depth: usize, k: RawValue) -> TreeResult> { let bytes = k.to_bytes(); if max_depth > 8 * bytes.len() { // note that our current keys are of Value type, which are 4 Goldilocks // field elements, ie ~256 bits, therefore the max_depth can not be // bigger than 256. - return Err(anyhow!( - "key to short (key length: {}) for the max_depth: {}", - 8 * bytes.len(), - max_depth - )); + return Err(TreeError::too_short_key(8 * bytes.len(), max_depth)); } Ok((0..max_depth) .map(|n| bytes[n / 8] & (1 << (n % 8)) != 0) @@ -617,7 +618,7 @@ pub mod tests { use super::*; #[test] - fn test_merkletree() -> Result<()> { + fn test_merkletree() -> TreeResult<()> { let mut kvs = HashMap::new(); for i in 0..8 { if i == 1 { diff --git a/src/backends/plonky2/primitives/mod.rs b/src/backends/plonky2/primitives/mod.rs index 1fa75d7..7dcd478 100644 --- a/src/backends/plonky2/primitives/mod.rs +++ b/src/backends/plonky2/primitives/mod.rs @@ -1,4 +1,2 @@ pub mod merkletree; -mod merkletree_circuit; pub mod signature; -mod signature_circuit; diff --git a/src/backends/plonky2/primitives/signature_circuit.rs b/src/backends/plonky2/primitives/signature/circuit.rs similarity index 97% rename from src/backends/plonky2/primitives/signature_circuit.rs rename to src/backends/plonky2/primitives/signature/circuit.rs index f25ee43..914f697 100644 --- a/src/backends/plonky2/primitives/signature_circuit.rs +++ b/src/backends/plonky2/primitives/signature/circuit.rs @@ -1,5 +1,4 @@ #![allow(unused)] -use anyhow::Result; use lazy_static::lazy_static; use plonky2::{ field::types::Field, @@ -26,8 +25,9 @@ use crate::{ backends::plonky2::{ basetypes::{Proof, C, D}, circuits::common::{CircuitBuilderPod, ValueTarget}, + error::Result, primitives::signature::{ - PublicKey, SecretKey, Signature, DUMMY_PUBLIC_INPUTS, DUMMY_SIGNATURE, + PublicKey, SecretKey, Signature, DUMMY_PUBLIC_INPUTS, DUMMY_SIGNATURE, VP, }, }, middleware::{Hash, RawValue, EMPTY_HASH, EMPTY_VALUE, F, VALUE_SIZE}, @@ -67,7 +67,7 @@ impl SignatureVerifyGadget { pub fn eval(&self, builder: &mut CircuitBuilder) -> Result { let enabled = builder.add_virtual_bool_target_safe(); - let common_data = super::signature::VP.0.common.clone(); + let common_data = VP.0.common.clone(); // targets related to the 'public inputs' for the verification of the // `SignatureInternalCircuit` proof. @@ -161,10 +161,7 @@ impl SignatureVerifyTarget { )?; } - pw.set_verifier_data_target( - &self.verifier_data_targ, - &super::signature::VP.0.verifier_only, - )?; + pw.set_verifier_data_target(&self.verifier_data_targ, &VP.0.verifier_only)?; Ok(()) } diff --git a/src/backends/plonky2/primitives/signature.rs b/src/backends/plonky2/primitives/signature/mod.rs similarity index 97% rename from src/backends/plonky2/primitives/signature.rs rename to src/backends/plonky2/primitives/signature/mod.rs index cbd3d6c..bb3a4e1 100644 --- a/src/backends/plonky2/primitives/signature.rs +++ b/src/backends/plonky2/primitives/signature/mod.rs @@ -1,6 +1,5 @@ //! Proof-based signatures using Plonky2 proofs, following //! https://eprint.iacr.org/2024/1553 . -use anyhow::Result; use lazy_static::lazy_static; use plonky2::{ field::types::Sample, @@ -20,9 +19,14 @@ use plonky2::{ }, }; -pub use super::signature_circuit::*; +pub mod circuit; +pub use circuit::*; + use crate::{ - backends::plonky2::basetypes::{Proof, C, D}, + backends::plonky2::{ + basetypes::{Proof, C, D}, + error::{Error, Result}, + }, middleware::{RawValue, F, VALUE_SIZE}, }; @@ -121,6 +125,7 @@ impl Signature { proof: self.0.clone(), public_inputs, }) + .map_err(Error::plonky2_proof_fail) } } diff --git a/src/backends/plonky2/signedpod.rs b/src/backends/plonky2/signedpod.rs index d6c10d7..12b98e5 100644 --- a/src/backends/plonky2/signedpod.rs +++ b/src/backends/plonky2/signedpod.rs @@ -1,24 +1,26 @@ use std::collections::HashMap; -use anyhow::{anyhow, Result}; use itertools::Itertools; use crate::{ - backends::plonky2::primitives::{ - merkletree::MerkleTree, - signature::{PublicKey, SecretKey, Signature}, + backends::plonky2::{ + error::{Error, Result}, + primitives::{ + merkletree::MerkleTree, + signature::{PublicKey, SecretKey, Signature}, + }, }, constants::MAX_DEPTH, middleware::{ - containers::Dictionary, AnchoredKey, Hash, Key, Params, Pod, PodId, PodSigner, PodType, - RawValue, Statement, Value, KEY_SIGNER, KEY_TYPE, + containers::Dictionary, AnchoredKey, DynError, Hash, Key, Params, Pod, PodId, PodSigner, + PodType, RawValue, Statement, Value, KEY_SIGNER, KEY_TYPE, }, }; pub struct Signer(pub SecretKey); -impl PodSigner for Signer { - fn sign(&mut self, _params: &Params, kvs: &HashMap) -> Result> { +impl Signer { + fn _sign(&mut self, _params: &Params, kvs: &HashMap) -> Result { let mut kvs = kvs.clone(); let pubkey = self.0.public_key(); kvs.insert(Key::from(KEY_SIGNER), Value::from(pubkey.0)); @@ -28,11 +30,21 @@ impl PodSigner for Signer { let id = RawValue::from(dict.commitment()); // PodId as Value let signature: Signature = self.0.sign(id)?; - Ok(Box::new(SignedPod { + Ok(SignedPod { id: PodId(Hash::from(id)), signature, dict, - })) + }) + } +} + +impl PodSigner for Signer { + fn sign( + &mut self, + params: &Params, + kvs: &HashMap, + ) -> Result, Box> { + Ok(self._sign(params, kvs).map(Box::new)?) } } @@ -43,15 +55,14 @@ pub struct SignedPod { pub dict: Dictionary, } -impl Pod for SignedPod { - fn verify(&self) -> Result<()> { +impl SignedPod { + fn _verify(&self) -> Result<()> { // 1. Verify type let value_at_type = self.dict.get(&Key::from(KEY_TYPE))?; if Value::from(PodType::Signed) != *value_at_type { - return Err(anyhow!( - "type does not match, expected Signed ({}), found {}", + return Err(Error::type_not_equal( PodType::Signed, - value_at_type + value_at_type.clone(), )); } @@ -67,11 +78,7 @@ impl Pod for SignedPod { )?; let id = PodId(mt.root()); if id != self.id { - return Err(anyhow!( - "id does not match, expected {}, computed {}", - self.id, - id - )); + return Err(Error::id_not_equal(self.id, id)); } // 3. Verify signature @@ -81,6 +88,12 @@ impl Pod for SignedPod { Ok(()) } +} + +impl Pod for SignedPod { + fn verify(&self) -> Result<(), Box> { + Ok(self._verify().map_err(Box::new)?) + } fn id(&self) -> PodId { self.id @@ -135,7 +148,7 @@ pub mod tests { let pod = pod.sign(&mut signer).unwrap(); let pod = (pod.pod as Box).downcast::().unwrap(); - pod.verify()?; + pod._verify()?; println!("id: {}", pod.id()); println!("kvs: {:?}", pod.kvs()); diff --git a/src/examples/custom.rs b/src/examples/custom.rs index 439fa05..3131555 100644 --- a/src/examples/custom.rs +++ b/src/examples/custom.rs @@ -1,10 +1,9 @@ use std::sync::Arc; -use anyhow::Result; use StatementTmplBuilder as STB; use crate::{ - frontend::{key, literal, CustomPredicateBatchBuilder, StatementTmplBuilder}, + frontend::{key, literal, CustomPredicateBatchBuilder, Result, StatementTmplBuilder}, middleware::{ CustomPredicateBatch, CustomPredicateRef, NativePredicate as NP, Params, PodType, Predicate, KEY_SIGNER, KEY_TYPE, diff --git a/src/examples/mod.rs b/src/examples/mod.rs index ef9fdb0..f94d099 100644 --- a/src/examples/mod.rs +++ b/src/examples/mod.rs @@ -2,12 +2,11 @@ pub mod custom; use std::collections::HashSet; -use anyhow::Result; use custom::{eth_dos_batch, eth_friend_batch}; use crate::{ backends::plonky2::mock::signedpod::MockSigner, - frontend::{MainPodBuilder, SignedPod, SignedPodBuilder}, + frontend::{MainPodBuilder, Result, SignedPod, SignedPodBuilder}, middleware::{ containers::Set, CustomPredicateRef, Params, PodType, Statement, TypedValue, Value, KEY_SIGNER, KEY_TYPE, diff --git a/src/frontend/custom.rs b/src/frontend/custom.rs index 74ef68e..708fe77 100644 --- a/src/frontend/custom.rs +++ b/src/frontend/custom.rs @@ -1,11 +1,10 @@ #![allow(unused)] use std::{collections::HashMap, fmt, hash as h, iter, iter::zip, sync::Arc}; -use anyhow::{anyhow, Result}; use schemars::JsonSchema; use crate::{ - frontend::{AnchoredKey, Statement, StatementArg}, + frontend::{AnchoredKey, Error, Result, Statement, StatementArg}, middleware::{ self, hash_str, CustomPredicate, CustomPredicateBatch, Key, KeyOrWildcard, NativePredicate, Params, PodId, Predicate, StatementTmpl, StatementTmplArg, ToFields, Value, Wildcard, @@ -131,17 +130,17 @@ impl CustomPredicateBatchBuilder { sts: &[StatementTmplBuilder], ) -> Result { if args.len() > params.max_statement_args { - return Err(anyhow!( - "args.len {} is over the limit {}", + return Err(Error::max_length( + "args.len".to_string(), args.len(), - params.max_statement_args + params.max_statement_args, )); } if (args.len() + priv_args.len()) > params.max_custom_predicate_wildcards { - return Err(anyhow!( - "wildcards.len {} is over the limit {}", + return Err(Error::max_length( + "wildcards.len".to_string(), args.len() + priv_args.len(), - params.max_custom_predicate_wildcards + params.max_custom_predicate_wildcards, )); } diff --git a/src/frontend/error.rs b/src/frontend/error.rs new file mode 100644 index 0000000..a578d56 --- /dev/null +++ b/src/frontend/error.rs @@ -0,0 +1,63 @@ +use std::{backtrace::Backtrace, fmt::Debug}; + +use crate::middleware::{DynError, Statement, StatementTmpl}; + +pub type Result = core::result::Result; + +#[derive(thiserror::Error, Debug)] +pub enum InnerError { + #[error("{0} {1} is over the limit {2}")] + MaxLength(String, usize, usize), + #[error("{0} doesn't match {1}")] + StatementsDontMatch(Statement, StatementTmpl), + #[error("invalid arguments to {0} operation")] + OpInvalidArgs(String), + // Other + #[error("{0}")] + Custom(String), +} + +#[derive(thiserror::Error)] +pub enum Error { + #[error("Inner: {inner}\n{backtrace}")] + Inner { + inner: Box, + backtrace: Box, + }, + #[error(transparent)] + Infallible(#[from] std::convert::Infallible), + #[error(transparent)] + Backend(#[from] Box), + #[error(transparent)] + Middleware(#[from] crate::middleware::Error), +} + +impl Debug for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self, f) + } +} + +macro_rules! new { + ($inner:expr) => { + Error::Inner { + inner: Box::new($inner), + backtrace: Box::new(Backtrace::capture()), + } + }; +} +use InnerError::*; +impl Error { + pub(crate) fn custom(s: impl Into) -> Self { + new!(Custom(s.into())) + } + pub(crate) fn op_invalid_args(s: String) -> Self { + new!(OpInvalidArgs(s)) + } + pub(crate) fn statements_dont_match(s0: Statement, s1: StatementTmpl) -> Self { + new!(StatementsDontMatch(s0, s1)) + } + pub(crate) fn max_length(obj: String, found: usize, expect: usize) -> Self { + new!(MaxLength(obj, found, expect)) + } +} diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs index e73e49f..f5e592b 100644 --- a/src/frontend/mod.rs +++ b/src/frontend/mod.rs @@ -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 { &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 { + fn op(&mut self, public: bool, op: Operation) -> Result { 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::>>()?; @@ -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 + ))) } } diff --git a/src/frontend/serialization.rs b/src/frontend/serialization.rs index 116830a..64158f4 100644 --- a/src/frontend/serialization.rs +++ b/src/frontend/serialization.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use crate::{ backends::plonky2::mock::{mainpod::MockMainPod, signedpod::MockSignedPod}, - frontend::{MainPod, SignedPod, Statement}, + frontend::{Error, MainPod, SignedPod, Statement}, middleware::{containers::Dictionary, Key, PodId, Value}, }; @@ -20,14 +20,14 @@ pub struct SignedPodHelper { } impl TryFrom for SignedPod { - type Error = anyhow::Error; + type Error = Error; fn try_from(helper: SignedPodHelper) -> Result { if helper.pod_class != "Signed" { - return Err(anyhow::anyhow!("pod_class is not Signed")); + return Err(Error::custom("pod_class is not Signed")); } if helper.pod_type != "Mock" { - return Err(anyhow::anyhow!("pod_type is not Mock")); + return Err(Error::custom("pod_type is not Mock")); } let dict = Dictionary::new(helper.entries.clone())?.clone(); @@ -62,18 +62,18 @@ pub struct MainPodHelper { } impl TryFrom for MainPod { - type Error = anyhow::Error; // or you can create a custom error type + type Error = Error; // or you can create a custom error type fn try_from(helper: MainPodHelper) -> Result { if helper.pod_class != "Main" { - return Err(anyhow::anyhow!("pod_class is not Main")); + return Err(Error::custom("pod_class is not Main")); } if helper.pod_type != "Mock" { - return Err(anyhow::anyhow!("pod_type is not Mock")); + return Err(Error::custom("pod_type is not Mock")); } let pod = MockMainPod::deserialize(helper.proof) - .map_err(|e| anyhow::anyhow!("Failed to deserialize proof: {}", e))?; + .map_err(|e| Error::custom(format!("Failed to deserialize proof: {}", e)))?; Ok(MainPod { pod: Box::new(pod), @@ -97,12 +97,10 @@ impl From for MainPodHelper { mod tests { use std::collections::HashSet; - use anyhow::Result; // Pretty assertions give nicer diffs between expected and actual values use pretty_assertions::assert_eq; use schemars::schema_for; - // use schemars::generate::SchemaSettings; use super::*; use crate::{ backends::plonky2::mock::{mainpod::MockProver, signedpod::MockSigner}, @@ -110,7 +108,7 @@ mod tests { eth_dos_pod_builder, eth_friend_signed_pod_builder, zu_kyc_pod_builder, zu_kyc_sign_pod_builders, }, - frontend::SignedPodBuilder, + frontend::{Result, SignedPodBuilder}, middleware::{ self, containers::{Array, Set}, diff --git a/src/middleware/basetypes.rs b/src/middleware/basetypes.rs index 66f1fda..9dbc513 100644 --- a/src/middleware/basetypes.rs +++ b/src/middleware/basetypes.rs @@ -45,7 +45,6 @@ use std::{ fmt, }; -use anyhow::Result; use hex::{FromHex, FromHexError}; use plonky2::{ field::{ diff --git a/src/middleware/containers.rs b/src/middleware/containers.rs index 9b1b569..1a630a5 100644 --- a/src/middleware/containers.rs +++ b/src/middleware/containers.rs @@ -1,8 +1,8 @@ +//! This file implements the types defined at +//! https://0xparc.github.io/pod2/values.html#dictionary-array-set . + use std::collections::{HashMap, HashSet}; -/// This file implements the types defined at -/// https://0xparc.github.io/pod2/values.html#dictionary-array-set . -use anyhow::{anyhow, Result}; use schemars::JsonSchema; use serde::{Deserialize, Deserializer, Serialize}; @@ -11,7 +11,7 @@ use super::serialization::{ordered_map, ordered_set}; use crate::backends::plonky2::primitives::merkletree::{MerkleProof, MerkleTree}; use crate::{ constants::MAX_DEPTH, - middleware::{hash_value, Hash, Key, RawValue, Value, EMPTY_VALUE}, + middleware::{hash_value, Error, Hash, Key, RawValue, Result, Value, EMPTY_VALUE}, }; /// Dictionary: the user original keys and values are hashed to be used in the leaf. @@ -43,7 +43,7 @@ impl Dictionary { pub fn get(&self, key: &Key) -> Result<&Value> { self.kvs .get(key) - .ok_or_else(|| anyhow!("key \"{}\" not found", key.name())) + .ok_or_else(|| Error::custom(format!("key \"{}\" not found", key.name()))) } pub fn prove(&self, key: &Key) -> Result<(&Value, MerkleProof)> { let (_, mtp) = self.mt.prove(&RawValue(key.hash().0))?; @@ -51,15 +51,23 @@ impl Dictionary { Ok((value, mtp)) } pub fn prove_nonexistence(&self, key: &Key) -> Result { - self.mt.prove_nonexistence(&RawValue(key.hash().0)) + Ok(self.mt.prove_nonexistence(&RawValue(key.hash().0))?) } pub fn verify(root: Hash, proof: &MerkleProof, key: &Key, value: &Value) -> Result<()> { let key = RawValue(key.hash().0); - MerkleTree::verify(MAX_DEPTH, root, proof, &key, &value.raw()) + Ok(MerkleTree::verify( + MAX_DEPTH, + root, + proof, + &key, + &value.raw(), + )?) } pub fn verify_nonexistence(root: Hash, proof: &MerkleProof, key: &Key) -> Result<()> { let key = RawValue(key.hash().0); - MerkleTree::verify_nonexistence(MAX_DEPTH, root, proof, &key) + Ok(MerkleTree::verify_nonexistence( + MAX_DEPTH, root, proof, &key, + )?) } // TODO: Rename to dict to be consistent maybe? pub fn kvs(&self) -> &HashMap { @@ -142,15 +150,26 @@ impl Set { } pub fn prove_nonexistence(&self, value: &Value) -> Result { let h = hash_value(&value.raw()); - self.mt.prove_nonexistence(&RawValue::from(h)) + Ok(self.mt.prove_nonexistence(&RawValue::from(h))?) } pub fn verify(root: Hash, proof: &MerkleProof, value: &Value) -> Result<()> { let h = hash_value(&value.raw()); - MerkleTree::verify(MAX_DEPTH, root, proof, &RawValue::from(h), &EMPTY_VALUE) + Ok(MerkleTree::verify( + MAX_DEPTH, + root, + proof, + &RawValue::from(h), + &EMPTY_VALUE, + )?) } pub fn verify_nonexistence(root: Hash, proof: &MerkleProof, value: &Value) -> Result<()> { let h = hash_value(&value.raw()); - MerkleTree::verify_nonexistence(MAX_DEPTH, root, proof, &RawValue::from(h)) + Ok(MerkleTree::verify_nonexistence( + MAX_DEPTH, + root, + proof, + &RawValue::from(h), + )?) } pub fn set(&self) -> &HashSet { &self.set @@ -217,9 +236,9 @@ impl Array { self.mt.root() } pub fn get(&self, i: usize) -> Result<&Value> { - self.array - .get(i) - .ok_or_else(|| anyhow!("index {} out of bounds 0..{}", i, self.array.len())) + self.array.get(i).ok_or_else(|| { + Error::custom(format!("index {} out of bounds 0..{}", i, self.array.len())) + }) } pub fn prove(&self, i: usize) -> Result<(&Value, MerkleProof)> { let (_, mtp) = self.mt.prove(&RawValue::from(i as i64))?; @@ -227,13 +246,13 @@ impl Array { Ok((value, mtp)) } pub fn verify(root: Hash, proof: &MerkleProof, i: usize, value: &Value) -> Result<()> { - MerkleTree::verify( + Ok(MerkleTree::verify( MAX_DEPTH, root, proof, &RawValue::from(i as i64), &value.raw(), - ) + )?) } pub fn array(&self) -> &[Value] { &self.array diff --git a/src/middleware/custom.rs b/src/middleware/custom.rs index 4ab3d57..23891ce 100644 --- a/src/middleware/custom.rs +++ b/src/middleware/custom.rs @@ -1,12 +1,11 @@ use std::{fmt, iter, sync::Arc}; -use anyhow::{anyhow, Result}; use plonky2::field::types::Field; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use crate::middleware::{ - hash_fields, Hash, Key, NativePredicate, Params, ToFields, Value, F, HASH_SIZE, + hash_fields, Error, Hash, Key, NativePredicate, Params, Result, ToFields, Value, F, HASH_SIZE, }; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, JsonSchema)] @@ -217,7 +216,11 @@ impl CustomPredicate { args_len: usize, ) -> Result { if statements.len() > params.max_custom_predicate_arity { - return Err(anyhow!("Custom predicate depends on too many statements")); + return Err(Error::max_length( + "statements.len".to_string(), + statements.len(), + params.max_custom_predicate_arity, + )); } Ok(Self { @@ -394,7 +397,6 @@ impl fmt::Display for Predicate { mod tests { use std::{array, sync::Arc}; - use anyhow::Result; use plonky2::field::goldilocks_field::GoldilocksField; use super::*; diff --git a/src/middleware/error.rs b/src/middleware/error.rs new file mode 100644 index 0000000..3ef660b --- /dev/null +++ b/src/middleware/error.rs @@ -0,0 +1,71 @@ +//! middleware errors + +use std::{backtrace::Backtrace, fmt::Debug}; + +use crate::middleware::{Operation, Statement, StatementArg}; + +pub type Result = core::result::Result; + +#[derive(Debug, thiserror::Error)] +pub enum MiddlewareInnerError { + #[error("incorrect statement args")] + IncorrectStatementArgs, + #[error("invalid deduction: {0:?} ⇏ {1:#}")] + InvalidDeduction(Operation, Statement), + #[error("statement argument {0:?} should be a {1}")] + InvalidStatementArg(StatementArg, String), + #[error("{0} {1} is over the limit {2}")] + MaxLength(String, usize, usize), + #[error("{0} amount of {1} should be {1} but it's {2}")] + DiffAmount(String, String, usize, usize), + // Other + #[error("{0}")] + Custom(String), +} + +#[derive(thiserror::Error)] +pub enum Error { + #[error("Inner: {inner}\n{backtrace}")] + Inner { + inner: Box, + backtrace: Box, + }, + #[error(transparent)] + Tree(#[from] crate::backends::plonky2::primitives::merkletree::error::TreeError), +} + +impl Debug for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self, f) + } +} + +macro_rules! new { + ($inner:expr) => { + Error::Inner { + inner: Box::new($inner), + backtrace: Box::new(Backtrace::capture()), + } + }; +} +use MiddlewareInnerError::*; +impl Error { + pub(crate) fn incorrect_statements_args() -> Self { + new!(IncorrectStatementArgs) + } + pub(crate) fn invalid_deduction(op: Operation, st: Statement) -> Self { + new!(InvalidDeduction(op, st)) + } + pub(crate) fn invalid_statement_arg(st_arg: StatementArg, v: String) -> Self { + new!(InvalidStatementArg(st_arg, v)) + } + pub(crate) fn max_length(obj: String, found: usize, expect: usize) -> Self { + new!(MaxLength(obj, found, expect)) + } + pub(crate) fn diff_amount(obj: String, unit: String, expect: usize, found: usize) -> Self { + new!(DiffAmount(obj, unit, expect, found)) + } + pub(crate) fn custom(s: String) -> Self { + new!(Custom(s)) + } +} diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index 7f8c3fe..99f5caf 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -8,21 +8,21 @@ use std::{ hash, }; -use anyhow::anyhow; use containers::{Array, Dictionary, Set}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; pub mod containers; mod custom; +mod error; mod operation; pub mod serialization; mod statement; use std::{any::Any, collections::HashMap, fmt}; -use anyhow::Result; pub use basetypes::*; pub use custom::*; use dyn_clone::DynClone; +pub use error::*; pub use operation::*; use serialization::*; pub use statement::*; @@ -126,22 +126,25 @@ impl From for TypedValue { } impl TryFrom<&TypedValue> for i64 { - type Error = anyhow::Error; + type Error = Error; fn try_from(v: &TypedValue) -> std::result::Result { if let TypedValue::Int(n) = v { Ok(*n) } else { - Err(anyhow!("Value not an int")) + Err(Error::custom("Value not an int".to_string())) } } } impl TryFrom for Key { - type Error = anyhow::Error; + type Error = Error; fn try_from(tv: TypedValue) -> Result { match tv { TypedValue::String(s) => Ok(Key::new(s)), - _ => Err(anyhow!("Value {} cannot be converted to a key.", tv)), + _ => Err(Error::custom(format!( + "Value {} cannot be converted to a key.", + tv + ))), } } } @@ -367,20 +370,31 @@ impl Value { match &self.typed() { TypedValue::Array(a) => match key.typed() { TypedValue::Int(i) if i >= &0 => a.prove((*i) as usize), - _ => Err(anyhow!("Invalid key {} for container {}.", key, self))?, + _ => Err(Error::custom(format!( + "Invalid key {} for container {}.", + key, self + )))?, }, TypedValue::Dictionary(d) => d.prove(&key.typed().clone().try_into()?), TypedValue::Set(s) => Ok((key, s.prove(key)?)), - _ => Err(anyhow!("Invalid container value {}", self.typed())), + _ => Err(Error::custom(format!( + "Invalid container value {}", + self.typed() + ))), } } /// Determines Merkle non-existence proof for `key` in `self` (if applicable). pub(crate) fn prove_nonexistence<'a>(&'a self, key: &'a Value) -> Result { match &self.typed() { - TypedValue::Array(_) => Err(anyhow!("Arrays do not support `NotContains` operation.")), + TypedValue::Array(_) => Err(Error::custom( + "Arrays do not support `NotContains` operation.".to_string(), + )), TypedValue::Dictionary(d) => d.prove_nonexistence(&key.typed().clone().try_into()?), TypedValue::Set(s) => s.prove_nonexistence(key), - _ => Err(anyhow!("Invalid container value {}", self.typed())), + _ => Err(Error::custom(format!( + "Invalid container value {}", + self.typed() + ))), } } } @@ -654,8 +668,10 @@ impl Params { } } +pub type DynError = dyn std::error::Error + Send + Sync; + pub trait Pod: fmt::Debug + DynClone + Any { - fn verify(&self) -> Result<()>; + fn verify(&self) -> Result<(), Box>; fn id(&self) -> PodId; fn pub_statements(&self) -> Vec; /// Extract key-values from ValueOf public statements @@ -683,7 +699,11 @@ pub trait Pod: fmt::Debug + DynClone + Any { dyn_clone::clone_trait_object!(Pod); pub trait PodSigner { - fn sign(&mut self, params: &Params, kvs: &HashMap) -> Result>; + fn sign( + &mut self, + params: &Params, + kvs: &HashMap, + ) -> Result, Box>; } /// This is a filler type that fulfills the Pod trait and always verifies. It's empty. This @@ -692,7 +712,7 @@ pub trait PodSigner { pub struct NonePod {} impl Pod for NonePod { - fn verify(&self) -> Result<()> { + fn verify(&self) -> Result<(), Box> { Ok(()) } fn id(&self) -> PodId { @@ -718,7 +738,11 @@ pub struct MainPodInputs<'a> { } pub trait PodProver { - fn prove(&mut self, params: &Params, inputs: MainPodInputs) -> Result>; + fn prove( + &mut self, + params: &Params, + inputs: MainPodInputs, + ) -> Result, Box>; } pub trait ToFields { diff --git a/src/middleware/operation.rs b/src/middleware/operation.rs index df97c1b..7c1d71e 100644 --- a/src/middleware/operation.rs +++ b/src/middleware/operation.rs @@ -1,6 +1,5 @@ use std::{fmt, iter, sync::Arc}; -use anyhow::{anyhow, Result}; use log::error; use plonky2::field::types::Field; use serde::{Deserialize, Serialize}; @@ -8,9 +7,9 @@ use serde::{Deserialize, Serialize}; use crate::{ backends::plonky2::primitives::merkletree::MerkleProof, middleware::{ - custom::KeyOrWildcard, AnchoredKey, CustomPredicateBatch, CustomPredicateRef, - NativePredicate, Params, Predicate, Statement, StatementArg, StatementTmplArg, ToFields, - Wildcard, WildcardValue, F, SELF, + custom::KeyOrWildcard, AnchoredKey, CustomPredicateBatch, CustomPredicateRef, Error, + NativePredicate, Params, Predicate, Result, Statement, StatementArg, StatementTmplArg, + ToFields, Wildcard, WildcardValue, F, SELF, }, }; @@ -253,11 +252,10 @@ impl Operation { Self::ProductOf(s1, s2, s3) } (NO::MaxOf, (Some(s1), Some(s2), Some(s3)), OA::None, 3) => Self::MaxOf(s1, s2, s3), - _ => Err(anyhow!( + _ => Err(Error::custom(format!( "Ill-formed operation {:?} with arguments {:?}.", - op_code, - args - ))?, + op_code, args + )))?, }, OperationType::Custom(cpr) => Self::Custom(cpr, args.to_vec()), }) @@ -320,10 +318,9 @@ impl Operation { { check_custom_pred(params, batch, *index, args, s_args) } - _ => Err(anyhow!( - "Invalid deduction: {:?} ⇏ {:#}", - self, - output_statement + _ => Err(Error::invalid_deduction( + self.clone(), + output_statement.clone(), )), } } @@ -386,17 +383,19 @@ fn check_custom_pred( ) -> Result { let pred = &batch.predicates[index]; if pred.statements.len() != args.len() { - return Err(anyhow!( - "Custom predicate operation needs {} statements but has {}.", + return Err(Error::diff_amount( + "custom predicate operation".to_string(), + "statements".to_string(), pred.statements.len(), - args.len() + args.len(), )); } if pred.args_len != s_args.len() { - return Err(anyhow!( - "Custom predicate statement needs {} args but has {}.", + return Err(Error::diff_amount( + "custom predicate statement".to_string(), + "args".to_string(), pred.args_len, - s_args.len() + s_args.len(), )); } diff --git a/src/middleware/statement.rs b/src/middleware/statement.rs index f3049e8..a542bfe 100644 --- a/src/middleware/statement.rs +++ b/src/middleware/statement.rs @@ -1,14 +1,13 @@ use std::{fmt, iter}; -use anyhow::{anyhow, Result}; use plonky2::field::types::Field; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use strum_macros::FromRepr; use crate::middleware::{ - AnchoredKey, CustomPredicateRef, Key, Params, PodId, Predicate, RawValue, ToFields, Value, F, - VALUE_SIZE, + AnchoredKey, CustomPredicateRef, Error, Key, Params, PodId, Predicate, RawValue, Result, + ToFields, Value, F, VALUE_SIZE, }; // TODO: Maybe store KEY_SIGNER and KEY_TYPE as Key with lazy_static @@ -152,7 +151,7 @@ impl Statement { { Ok(Self::ValueOf(a0, v1)) } else { - Err(anyhow!("Incorrect statement args")) + Err(Error::incorrect_statements_args()) } } Native(NativePredicate::Equal) => { @@ -161,7 +160,7 @@ impl Statement { { Ok(Self::Equal(a0, a1)) } else { - Err(anyhow!("Incorrect statement args")) + Err(Error::incorrect_statements_args()) } } Native(NativePredicate::NotEqual) => { @@ -170,7 +169,7 @@ impl Statement { { Ok(Self::NotEqual(a0, a1)) } else { - Err(anyhow!("Incorrect statement args")) + Err(Error::incorrect_statements_args()) } } Native(NativePredicate::Gt) => { @@ -179,7 +178,7 @@ impl Statement { { Ok(Self::Gt(a0, a1)) } else { - Err(anyhow!("Incorrect statement args")) + Err(Error::incorrect_statements_args()) } } Native(NativePredicate::Lt) => { @@ -188,7 +187,7 @@ impl Statement { { Ok(Self::Lt(a0, a1)) } else { - Err(anyhow!("Incorrect statement args")) + Err(Error::incorrect_statements_args()) } } Native(NativePredicate::Contains) => { @@ -197,7 +196,7 @@ impl Statement { { Ok(Self::Contains(a0, a1, a2)) } else { - Err(anyhow!("Incorrect statement args")) + Err(Error::incorrect_statements_args()) } } Native(NativePredicate::NotContains) => { @@ -206,7 +205,7 @@ impl Statement { { Ok(Self::NotContains(a0, a1)) } else { - Err(anyhow!("Incorrect statement args")) + Err(Error::incorrect_statements_args()) } } Native(NativePredicate::SumOf) => { @@ -215,7 +214,7 @@ impl Statement { { Ok(Self::SumOf(a0, a1, a2)) } else { - Err(anyhow!("Incorrect statement args")) + Err(Error::incorrect_statements_args()) } } Native(NativePredicate::ProductOf) => { @@ -224,7 +223,7 @@ impl Statement { { Ok(Self::ProductOf(a0, a1, a2)) } else { - Err(anyhow!("Incorrect statement args")) + Err(Error::incorrect_statements_args()) } } Native(NativePredicate::MaxOf) => { @@ -233,17 +232,17 @@ impl Statement { { Ok(Self::MaxOf(a0, a1, a2)) } else { - Err(anyhow!("Incorrect statement args")) + Err(Error::incorrect_statements_args()) } } - Native(np) => Err(anyhow!("Predicate {:?} is syntax sugar", np)), + Native(np) => Err(Error::custom(format!("Predicate {:?} is syntax sugar", np))), BatchSelf(_) => unreachable!(), Custom(cpr) => { let v_args: Result> = args .iter() .map(|x| match x { StatementArg::WildcardLiteral(v) => Ok(v.clone()), - _ => Err(anyhow!("Incorrect statement args")), + _ => Err(Error::incorrect_statements_args()), }) .collect(); Ok(Self::Custom(cpr, v_args?)) @@ -302,13 +301,19 @@ impl StatementArg { pub fn literal(&self) -> Result { match self { Self::Literal(value) => Ok(value.clone()), - _ => Err(anyhow!("Statement argument {:?} is not a literal.", self)), + _ => Err(Error::invalid_statement_arg( + self.clone(), + "literal".to_string(), + )), } } pub fn key(&self) -> Result { match self { Self::Key(ak) => Ok(ak.clone()), - _ => Err(anyhow!("Statement argument {:?} is not a key.", self)), + _ => Err(Error::invalid_statement_arg( + self.clone(), + "key".to_string(), + )), } } }