Просмотр исходного кода

fix: port the code generator to the sigma-proofs draft API

Michele Orrù 4 дней назад
Родитель
Сommit
998e45dee5

+ 9 - 7
sigma-compiler-core/src/codegen.rs

@@ -412,7 +412,7 @@ impl CodeGen {
                     #witness_var: &Witness,
                     #witness_var: &Witness,
                     #sid_var: &[u8],
                     #sid_var: &[u8],
                     #rng_var: &mut (impl CryptoRng + RngCore),
                     #rng_var: &mut (impl CryptoRng + RngCore),
-                ) -> Result<Vec<u8>, SigmaError> {
+                ) -> Result<Vec<u8>, InvalidWitness> {
                     #dumper
                     #dumper
                     let Instance { #instance_ids } = #instance_var.clone();
                     let Instance { #instance_ids } = #instance_var.clone();
                     let Witness { #witness_ids } = #witness_var.clone();
                     let Witness { #witness_ids } = #witness_var.clone();
@@ -468,7 +468,7 @@ impl CodeGen {
                         let #id: Point = {
                         let #id: Point = {
                             let end = #offset_var + #element_len_var;
                             let end = #offset_var + #element_len_var;
                             if #proof_var.len() < end {
                             if #proof_var.len() < end {
-                                return Err(SigmaError::VerificationFailure);
+                                return Err(VerificationError);
                             }
                             }
                             let mut repr = <Point as group::GroupEncoding>::Repr::default();
                             let mut repr = <Point as group::GroupEncoding>::Repr::default();
                             repr.as_mut()
                             repr.as_mut()
@@ -477,7 +477,7 @@ impl CodeGen {
                             Option::<Point>::from(
                             Option::<Point>::from(
                                 <Point as group::GroupEncoding>::from_bytes(&repr)
                                 <Point as group::GroupEncoding>::from_bytes(&repr)
                             )
                             )
-                            .ok_or(SigmaError::VerificationFailure)?
+                            .ok_or(VerificationError)?
                         };
                         };
                     },
                     },
                     StructField::VecPoint(id) => quote! {
                     StructField::VecPoint(id) => quote! {
@@ -487,7 +487,7 @@ impl CodeGen {
                             for _ in 0..expected_len {
                             for _ in 0..expected_len {
                                 let end = #offset_var + #element_len_var;
                                 let end = #offset_var + #element_len_var;
                                 if #proof_var.len() < end {
                                 if #proof_var.len() < end {
-                                    return Err(SigmaError::VerificationFailure);
+                                    return Err(VerificationError);
                                 }
                                 }
                                 let mut repr =
                                 let mut repr =
                                     <Point as group::GroupEncoding>::Repr::default();
                                     <Point as group::GroupEncoding>::Repr::default();
@@ -497,7 +497,7 @@ impl CodeGen {
                                 let point = Option::<Point>::from(
                                 let point = Option::<Point>::from(
                                     <Point as group::GroupEncoding>::from_bytes(&repr)
                                     <Point as group::GroupEncoding>::from_bytes(&repr)
                                 )
                                 )
-                                .ok_or(SigmaError::VerificationFailure)?;
+                                .ok_or(VerificationError)?;
                                 points.push(point);
                                 points.push(point);
                             }
                             }
                             #id = points;
                             #id = points;
@@ -538,7 +538,7 @@ impl CodeGen {
                     #instance_var: &Instance,
                     #instance_var: &Instance,
                     #proof_var: &[u8],
                     #proof_var: &[u8],
                     #sid_var: &[u8],
                     #sid_var: &[u8],
-                ) -> Result<(), SigmaError> {
+                ) -> Result<(), VerificationError> {
                     #dumper
                     #dumper
                     let Instance { #instance_ids } = #instance_var.clone();
                     let Instance { #instance_ids } = #instance_var.clone();
                     #verify_pre_instance_code
                     #verify_pre_instance_code
@@ -576,7 +576,9 @@ impl CodeGen {
                 use sigma_compiler::group::ff::{Field, PrimeField};
                 use sigma_compiler::group::ff::{Field, PrimeField};
                 use sigma_compiler::rand::{CryptoRng, RngCore};
                 use sigma_compiler::rand::{CryptoRng, RngCore};
                 use sigma_compiler::sigma_proofs;
                 use sigma_compiler::sigma_proofs;
-                use sigma_compiler::sigma_proofs::errors::Error as SigmaError;
+                use sigma_compiler::sigma_proofs::errors::{
+                    InvalidInstance, InvalidWitness, VerificationError,
+                };
                 use sigma_compiler::subtle::ConditionallySelectable;
                 use sigma_compiler::subtle::ConditionallySelectable;
                 use sigma_compiler::vecutils::*;
                 use sigma_compiler::vecutils::*;
                 use std::ops::Neg;
                 use std::ops::Neg;

+ 1 - 1
sigma-compiler-core/src/notequals.rs

@@ -273,7 +273,7 @@ pub fn transform(
             let #Lx_var = #Lx_code;
             let #Lx_var = #Lx_code;
             let #j_var = <Scalar as Field>::invert(&#Lx_var)
             let #j_var = <Scalar as Field>::invert(&#Lx_var)
                 .into_option()
                 .into_option()
-                .ok_or(SigmaError::VerificationFailure)?;
+                .ok_or(InvalidWitness)?;
             let #s_var = -#rand_var * #j_var;
             let #s_var = -#rand_var * #j_var;
         });
         });
 
 

+ 9 - 1
sigma-compiler-core/src/pubscalareq.rs

@@ -78,9 +78,17 @@ pub fn transform(
                                     // If we're in the root disjunction branch,
                                     // If we're in the root disjunction branch,
                                     // add code to both the prover and the
                                     // add code to both the prover and the
                                     // verifier to directly check the statement.
                                     // verifier to directly check the statement.
+                                    // The check is on public values only, so
+                                    // both parties reach the same verdict; the
+                                    // shared snippet names the instance error
+                                    // and `?` converts it to whichever of the
+                                    // two functions it landed in.
                                     codegen.prove_verify_append(quote! {
                                     codegen.prove_verify_append(quote! {
                                         if #id != #right_tokens {
                                         if #id != #right_tokens {
-                                            return Err(SigmaError::VerificationFailure);
+                                            return Err(InvalidInstance::new(concat!(
+                                                "public scalar equality does not hold: ",
+                                                stringify!(#id),
+                                            )).into());
                                         }
                                         }
                                     });
                                     });
 
 

+ 3 - 1
sigma-compiler-core/src/rangeproof.rs

@@ -403,7 +403,9 @@ pub fn transform(
             if #bitrep_scalars_var.is_empty() {
             if #bitrep_scalars_var.is_empty() {
                 // The upper bound was either less than 2, or more than
                 // The upper bound was either less than 2, or more than
                 // i128::MAX
                 // i128::MAX
-                return Err(SigmaError::VerificationFailure);
+                return Err(InvalidInstance::new(
+                    "range upper bound has no bit representation",
+                ).into());
             }
             }
             let #nbits_var = #bitrep_scalars_var.len();
             let #nbits_var = #bitrep_scalars_var.len();
         });
         });

+ 34 - 38
sigma-compiler-core/src/sigma/codegen.rs

@@ -202,10 +202,10 @@ impl<'a> CodeGen<'a> {
     }
     }
 
 
     /// Generate the code for the `protocol` and `protocol_witness`
     /// Generate the code for the `protocol` and `protocol_witness`
-    /// functions that create the `ComposedRelation` and `ComposedWitness`
+    /// functions that create the `ComposedInstance` and `ComposedWitness`
     /// structs, respectively, given a slice of [`Expr`]s that will be
     /// structs, respectively, given a slice of [`Expr`]s that will be
     /// bundled into a single `LinearRelation`.  The `protocol` code
     /// bundled into a single `LinearRelation`.  The `protocol` code
-    /// must evaluate to a `Result<ComposedRelation>` and the `protocol_witness`
+    /// must evaluate to a `Result<ComposedInstance>` and the `protocol_witness`
     /// code must evaluate to a `Result<ComposedWitness>`.
     /// code must evaluate to a `Result<ComposedWitness>`.
     fn linear_relation_codegen(&self, exprs: &[&Expr]) -> (TokenStream, TokenStream) {
     fn linear_relation_codegen(&self, exprs: &[&Expr]) -> (TokenStream, TokenStream) {
         let instance_var = format_ident!("{}instance", self.unique_prefix);
         let instance_var = format_ident!("{}instance", self.unique_prefix);
@@ -260,7 +260,7 @@ impl<'a> CodeGen<'a> {
                         vec_param_vars.insert(id.clone());
                         vec_param_vars.insert(id.clone());
                         Ok(quote! {#instance_var.#id})
                         Ok(quote! {#instance_var.#id})
                     }
                     }
-                })
+                }, false)
                 .unwrap();
                 .unwrap();
             let AExprType::Point {
             let AExprType::Point {
                 is_pub: true,
                 is_pub: true,
@@ -356,7 +356,7 @@ impl<'a> CodeGen<'a> {
                         }
                         }
                         Ok(quote! { #id })
                         Ok(quote! { #id })
                     }
                     }
-                })
+                }, true)
             else {
             else {
                 let expr_str = quote! { #expr }.to_string();
                 let expr_str = quote! { #expr }.to_string();
                 panic!("Right side of = is not a valid arithmetic expression: {expr_str}");
                 panic!("Right side of = is not a valid arithmetic expression: {expr_str}");
@@ -386,12 +386,11 @@ impl<'a> CodeGen<'a> {
                     param_vec_code = quote! {
                     param_vec_code = quote! {
                         #param_vec_code
                         #param_vec_code
                         if #vec_len_var != #instance_var.#thisvar.len() {
                         if #vec_len_var != #instance_var.#thisvar.len() {
-                            eprintln!(
+                            return Err(InvalidInstance::new(format!(
                                 "Instance variables {} and {} must have the same length",
                                 "Instance variables {} and {} must have the same length",
                                 stringify!(#firstvar),
                                 stringify!(#firstvar),
                                 stringify!(#thisvar),
                                 stringify!(#thisvar),
-                            );
-                            return Err(SigmaError::VerificationFailure);
+                            )));
                         }
                         }
                     };
                     };
                 }
                 }
@@ -410,7 +409,7 @@ impl<'a> CodeGen<'a> {
                                 stringify!(#firstvar),
                                 stringify!(#firstvar),
                                 stringify!(#witvar),
                                 stringify!(#witvar),
                             );
                             );
-                            return Err(SigmaError::VerificationFailure);
+                            return Err(InvalidWitness);
                         }
                         }
                     }
                     }
                 }
                 }
@@ -453,7 +452,7 @@ impl<'a> CodeGen<'a> {
                     #eq_code
                     #eq_code
                     #element_assigns
                     #element_assigns
 
 
-                    SigmaOk(ComposedRelation::try_from(#lr_var).unwrap())
+                    ComposedInstance::try_from(#lr_var)
                 }
                 }
             },
             },
             quote! {
             quote! {
@@ -461,7 +460,7 @@ impl<'a> CodeGen<'a> {
                     #witness_vec_code
                     #witness_vec_code
                     let mut witnessvec = Vec::new();
                     let mut witnessvec = Vec::new();
                     #witness_code
                     #witness_code
-                    SigmaOk(ComposedWitness::Simple(witnessvec))
+                    ProverResult::Ok(ComposedWitness::Simple(witnessvec))
                 }
                 }
             },
             },
         )
         )
@@ -481,10 +480,10 @@ impl<'a> CodeGen<'a> {
             // leaf "true")
             // leaf "true")
             StatementTree::Leaf(_) if statement.is_leaf_true() => (
             StatementTree::Leaf(_) if statement.is_leaf_true() => (
                 quote! {
                 quote! {
-                    Ok(ComposedRelation::try_from(LinearRelation::<Point>::new()).unwrap())
+                    InstanceResult::Ok(ComposedInstance::try_from(LinearRelation::<Point>::new()).unwrap())
                 },
                 },
                 quote! {
                 quote! {
-                    Ok(ComposedWitness::Simple(vec![]))
+                    ProverResult::Ok(ComposedWitness::Simple(vec![]))
                 },
                 },
             ),
             ),
             // The StatementTree is a single statement.  Generate a
             // The StatementTree is a single statement.  Generate a
@@ -516,13 +515,13 @@ impl<'a> CodeGen<'a> {
                             .unzip();
                             .unzip();
                     (
                     (
                         quote! {
                         quote! {
-                            SigmaOk(ComposedRelation::and([
+                            ComposedInstance::and([
                                 #proto_code?,
                                 #proto_code?,
                                 #(#others_proto?,)*
                                 #(#others_proto?,)*
-                            ]))
+                            ])
                         },
                         },
                         quote! {
                         quote! {
-                            SigmaOk(ComposedWitness::and([
+                            ProverResult::Ok(ComposedWitness::and([
                                 #witness_code?,
                                 #witness_code?,
                                 #(#others_witness?,)*
                                 #(#others_witness?,)*
                             ]))
                             ]))
@@ -537,12 +536,12 @@ impl<'a> CodeGen<'a> {
                     .unzip();
                     .unzip();
                 (
                 (
                     quote! {
                     quote! {
-                        SigmaOk(ComposedRelation::or([
+                        ComposedInstance::or([
                             #(#proto?,)*
                             #(#proto?,)*
-                        ]))
+                        ])
                     },
                     },
                     quote! {
                     quote! {
-                        SigmaOk(ComposedWitness::or([
+                        ProverResult::Ok(ComposedWitness::or([
                             #(#witness?,)*
                             #(#witness?,)*
                         ]))
                         ]))
                     },
                     },
@@ -555,12 +554,12 @@ impl<'a> CodeGen<'a> {
                     .unzip();
                     .unzip();
                 (
                 (
                     quote! {
                     quote! {
-                        SigmaOk(ComposedRelation::threshold(#thresh, [
+                        ComposedInstance::threshold(#thresh, [
                             #(#proto?,)*
                             #(#proto?,)*
-                        ]))
+                        ])
                     },
                     },
                     quote! {
                     quote! {
-                        SigmaOk(ComposedWitness::threshold([
+                        ProverResult::Ok(ComposedWitness::threshold([
                             #(#witness?,)*
                             #(#witness?,)*
                         ]))
                         ]))
                     },
                     },
@@ -666,7 +665,7 @@ impl<'a> CodeGen<'a> {
             quote! {
             quote! {
                 fn protocol(
                 fn protocol(
                     #instance_var: &Instance,
                     #instance_var: &Instance,
-                ) -> SigmaResult<ComposedRelation<Point>> {
+                ) -> Result<ComposedInstance<Point>, InvalidInstance> {
                     #protocol_code
                     #protocol_code
                 }
                 }
             }
             }
@@ -678,7 +677,7 @@ impl<'a> CodeGen<'a> {
                 fn protocol_witness(
                 fn protocol_witness(
                     instance: &Instance,
                     instance: &Instance,
                     witness: &Witness,
                     witness: &Witness,
-                ) -> SigmaResult<ComposedWitness<Point>> {
+                ) -> Result<ComposedWitness<Point>, InvalidWitness> {
                     #witness_code
                     #witness_code
                 }
                 }
             }
             }
@@ -694,7 +693,6 @@ impl<'a> CodeGen<'a> {
             let rng_var = format_ident!("{}rng", self.unique_prefix);
             let rng_var = format_ident!("{}rng", self.unique_prefix);
             let proto_var = format_ident!("{}proto", self.unique_prefix);
             let proto_var = format_ident!("{}proto", self.unique_prefix);
             let proto_witness_var = format_ident!("{}proto_witness", self.unique_prefix);
             let proto_witness_var = format_ident!("{}proto_witness", self.unique_prefix);
-            let nizk_var = format_ident!("{}nizk", self.unique_prefix);
 
 
             quote! {
             quote! {
                 pub fn prove(
                 pub fn prove(
@@ -702,12 +700,12 @@ impl<'a> CodeGen<'a> {
                     #witness_var: &Witness,
                     #witness_var: &Witness,
                     #session_id_var: &[u8],
                     #session_id_var: &[u8],
                     #rng_var: &mut (impl CryptoRng + RngCore),
                     #rng_var: &mut (impl CryptoRng + RngCore),
-                ) -> SigmaResult<Vec<u8>> {
+                ) -> Result<Vec<u8>, InvalidWitness> {
+                    // The proof nonces are drawn internally by sigma-proofs
+                    let _ = #rng_var;
                     let #proto_var = protocol(#instance_var)?;
                     let #proto_var = protocol(#instance_var)?;
                     let #proto_witness_var = protocol_witness(#instance_var, #witness_var)?;
                     let #proto_witness_var = protocol_witness(#instance_var, #witness_var)?;
-                    let #nizk_var = #proto_var.into_nizk(#session_id_var);
-
-                    #nizk_var.prove_compact(&#proto_witness_var, #rng_var)
+                    prove_compact(#session_id_var, &#proto_var, &#proto_witness_var)
                 }
                 }
             }
             }
         } else {
         } else {
@@ -720,18 +718,15 @@ impl<'a> CodeGen<'a> {
             let proof_var = format_ident!("{}proof", self.unique_prefix);
             let proof_var = format_ident!("{}proof", self.unique_prefix);
             let session_id_var = format_ident!("{}session_id", self.unique_prefix);
             let session_id_var = format_ident!("{}session_id", self.unique_prefix);
             let proto_var = format_ident!("{}proto", self.unique_prefix);
             let proto_var = format_ident!("{}proto", self.unique_prefix);
-            let nizk_var = format_ident!("{}nizk", self.unique_prefix);
 
 
             quote! {
             quote! {
                 pub fn verify(
                 pub fn verify(
                     #instance_var: &Instance,
                     #instance_var: &Instance,
                     #proof_var: &[u8],
                     #proof_var: &[u8],
                     #session_id_var: &[u8],
                     #session_id_var: &[u8],
-                ) -> SigmaResult<()> {
+                ) -> Result<(), VerificationError> {
                     let #proto_var = protocol(#instance_var)?;
                     let #proto_var = protocol(#instance_var)?;
-                    let #nizk_var = #proto_var.into_nizk(#session_id_var);
-
-                    #nizk_var.verify_compact(#proof_var)
+                    verify_compact(#session_id_var, &#proto_var, #proof_var)
                 }
                 }
             }
             }
         } else {
         } else {
@@ -756,11 +751,12 @@ impl<'a> CodeGen<'a> {
                 use sigma_compiler::subtle::CtOption;
                 use sigma_compiler::subtle::CtOption;
                 use sigma_compiler::vecutils::*;
                 use sigma_compiler::vecutils::*;
                 use sigma_proofs::{
                 use sigma_proofs::{
-                    composition::{ComposedRelation, ComposedWitness},
-                    errors::Error as SigmaError,
-                    errors::Ok as SigmaOk,
-                    errors::Result as SigmaResult,
-                    LinearRelation, Nizk,
+                    composition::{ComposedInstance, ComposedWitness},
+                    errors::{
+                        InstanceResult, InvalidInstance, InvalidWitness, ProverResult,
+                        VerificationError,
+                    },
+                    prove_compact, verify_compact, LinearRelation,
                 };
                 };
                 use std::ops::Neg;
                 use std::ops::Neg;
                 #dump_use
                 #dump_use

+ 33 - 6
sigma-compiler-core/src/sigma/types.rs

@@ -755,6 +755,23 @@ pub fn tokens_mul_maybe_vec(
 
 
 pub struct AExprTokenFold<'a> {
 pub struct AExprTokenFold<'a> {
     ident_closure: &'a mut dyn FnMut(&Ident, AExprType) -> Result<TokenStream>,
     ident_closure: &'a mut dyn FnMut(&Ident, AExprType) -> Result<TokenStream>,
+    /// Whether the emitted expression is built from sigma-proofs relation
+    /// variables rather than from runtime Scalars and Points.  It decides
+    /// how `sum` is summed; see [`AExprTokenFold::sum_tokens`].
+    over_relation_vars: bool,
+}
+
+impl AExprTokenFold<'_> {
+    /// The tokens summing `arge`, whose elements are relation variables
+    /// exactly when this fold emits into a relation and `over_vars` says
+    /// the expression is not built purely from public runtime values.
+    fn sum_tokens(&self, over_vars: bool, arge: TokenStream) -> TokenStream {
+        if self.over_relation_vars && over_vars {
+            quote! { sigma_compiler::vecutils::sum_vars(&(#arge)) }
+        } else {
+            quote! { sigma_compiler::vecutils::sum_vec(&(#arge)) }
+        }
+    }
 }
 }
 
 
 impl<'a> AExprFold<TokenStream> for AExprTokenFold<'a> {
 impl<'a> AExprFold<TokenStream> for AExprTokenFold<'a> {
@@ -837,13 +854,18 @@ impl<'a> AExprFold<TokenStream> for AExprTokenFold<'a> {
     }
     }
 
 
     /// Called when summing a vector of `Scalar`s
     /// Called when summing a vector of `Scalar`s
+    ///
+    /// Over relation variables the sum has to stay a sum of those
+    /// variables: adding two of them normalizes to sigma-proofs' canonical
+    /// weighted sum, which the remaining elements can no longer be added
+    /// into.  Over runtime values the sum is just their sum.
     fn sum_scalars(
     fn sum_scalars(
         &mut self,
         &mut self,
         arg: (AExprType, TokenStream),
         arg: (AExprType, TokenStream),
         _restype: AExprType,
         _restype: AExprType,
     ) -> Result<TokenStream> {
     ) -> Result<TokenStream> {
-        let arge = arg.1;
-        Ok(quote! { sigma_compiler::vecutils::sum_vec(&(#arge)) })
+        let over_vars = !matches!(arg.0, AExprType::Scalar { is_pub: true, .. });
+        Ok(self.sum_tokens(over_vars, arg.1))
     }
     }
 
 
     /// Called when summing a vector of `Point`s
     /// Called when summing a vector of `Point`s
@@ -852,8 +874,7 @@ impl<'a> AExprFold<TokenStream> for AExprTokenFold<'a> {
         arg: (AExprType, TokenStream),
         arg: (AExprType, TokenStream),
         _restype: AExprType,
         _restype: AExprType,
     ) -> Result<TokenStream> {
     ) -> Result<TokenStream> {
-        let arge = arg.1;
-        Ok(quote! { sigma_compiler::vecutils::sum_vec(&(#arge)) })
+        Ok(self.sum_tokens(true, arg.1))
     }
     }
 
 
     /// Called when subtracting two `Scalar`s
     /// Called when subtracting two `Scalar`s
@@ -986,18 +1007,24 @@ impl<'a> AExprFold<TokenStream> for AExprTokenFold<'a> {
 pub fn expr_type_tokens(vars: &VarDict, expr: &Expr) -> Result<(AExprType, TokenStream)> {
 pub fn expr_type_tokens(vars: &VarDict, expr: &Expr) -> Result<(AExprType, TokenStream)> {
     let mut fold = AExprTokenFold {
     let mut fold = AExprTokenFold {
         ident_closure: &mut |id, _var_type| Ok(quote! { #id }),
         ident_closure: &mut |id, _var_type| Ok(quote! { #id }),
+        over_relation_vars: false,
     };
     };
     fold.fold(vars, expr)
     fold.fold(vars, expr)
 }
 }
 
 
 /// Like [`expr_type_tokens`], but call a custom closure on encountering
 /// Like [`expr_type_tokens`], but call a custom closure on encountering
-/// each [`struct@Ident`].
+/// each [`struct@Ident`].  Set `over_relation_vars` when the closure
+/// yields sigma-proofs relation variables instead of runtime values.
 pub fn expr_type_tokens_id_closure(
 pub fn expr_type_tokens_id_closure(
     vars: &VarDict,
     vars: &VarDict,
     expr: &Expr,
     expr: &Expr,
     ident_closure: &mut dyn FnMut(&Ident, AExprType) -> Result<TokenStream>,
     ident_closure: &mut dyn FnMut(&Ident, AExprType) -> Result<TokenStream>,
+    over_relation_vars: bool,
 ) -> Result<(AExprType, TokenStream)> {
 ) -> Result<(AExprType, TokenStream)> {
-    let mut fold = AExprTokenFold { ident_closure };
+    let mut fold = AExprTokenFold {
+        ident_closure,
+        over_relation_vars,
+    };
     fold.fold(vars, expr)
     fold.fold(vars, expr)
 }
 }
 
 

+ 1 - 1
sigma-compiler-core/src/substitution.rs

@@ -156,7 +156,7 @@ pub fn transform(
                                 // for illegal inputs (but is constant time for
                                 // for illegal inputs (but is constant time for
                                 // valid inputs)
                                 // valid inputs)
                                 if #id != #right_tokens {
                                 if #id != #right_tokens {
-                                    return Err(SigmaError::VerificationFailure);
+                                    return Err(InvalidWitness);
                                 }
                                 }
                             });
                             });
                         }
                         }

+ 9 - 6
src/rangeutils.rs

@@ -2,7 +2,7 @@
 //! processing of range statements.
 //! processing of range statements.
 
 
 use group::ff::PrimeField;
 use group::ff::PrimeField;
-use sigma_proofs::errors::Error;
+use sigma_proofs::errors::InvalidInstance;
 use subtle::Choice;
 use subtle::Choice;
 
 
 /// Convert a [`Scalar`] to an [`u128`], assuming it fits in an [`i128`]
 /// Convert a [`Scalar`] to an [`u128`], assuming it fits in an [`i128`]
@@ -67,13 +67,14 @@ pub fn bit_decomp<S: PrimeField>(mut s: S, nbits: u32) -> Vec<Choice> {
 /// constant time.
 /// constant time.
 ///
 ///
 /// [`Scalar`]: https://docs.rs/group/0.13.0/group/trait.Group.html#associatedtype.Scalar
 /// [`Scalar`]: https://docs.rs/group/0.13.0/group/trait.Group.html#associatedtype.Scalar
-pub fn bitrep_scalars_vartime<S: PrimeField>(upper: S) -> Result<Vec<S>, Error> {
+pub fn bitrep_scalars_vartime<S: PrimeField>(upper: S) -> Result<Vec<S>, InvalidInstance> {
     // Get the `u128` value of `upper`, and its number of bits `nbits`
     // Get the `u128` value of `upper`, and its number of bits `nbits`
-    let (upper_val, mut nbits) = bit_decomp_vartime(upper).ok_or(Error::VerificationFailure)?;
+    let (upper_val, mut nbits) = bit_decomp_vartime(upper)
+        .ok_or_else(|| InvalidInstance::new("range upper bound exceeds i128::MAX"))?;
 
 
     // Ensure `nbits` is at least 2.
     // Ensure `nbits` is at least 2.
     if nbits < 2 {
     if nbits < 2 {
-        return Err(Error::VerificationFailure);
+        return Err(InvalidInstance::new("range upper bound must be at least 2"));
     }
     }
 
 
     // If upper is exactly a power of 2, use one fewer bit
     // If upper is exactly a power of 2, use one fewer bit
@@ -207,7 +208,7 @@ mod tests {
     // Obliviously test whether x is in 0..upper (that is, 0 <= x <
     // Obliviously test whether x is in 0..upper (that is, 0 <= x <
     // upper) using bit decomposition.  `upper` is considered public,
     // upper) using bit decomposition.  `upper` is considered public,
     // but `x` is private.  `upper` must be at least 2.
     // but `x` is private.  `upper` must be at least 2.
-    fn bitrep_tester(upper: Scalar, x: Scalar, expected: bool) -> Result<(), Error> {
+    fn bitrep_tester(upper: Scalar, x: Scalar, expected: bool) -> Result<(), InvalidInstance> {
         let rep_scalars = bitrep_scalars_vartime(upper)?;
         let rep_scalars = bitrep_scalars_vartime(upper)?;
         let bitrep = compute_bitrep(x, &rep_scalars);
         let bitrep = compute_bitrep(x, &rep_scalars);
 
 
@@ -219,7 +220,9 @@ mod tests {
         }
         }
 
 
         if (x == x_out) != expected {
         if (x == x_out) != expected {
-            return Err(Error::VerificationFailure);
+            return Err(InvalidInstance::new(
+                "bit representation disagrees with the range",
+            ));
         }
         }
 
 
         Ok(())
         Ok(())

+ 12 - 0
src/vecutils.rs

@@ -111,3 +111,15 @@ where
 {
 {
     summable.iter().cloned().sum()
     summable.iter().cloned().sum()
 }
 }
+
+/// Add the elements of a vector of sigma-proofs relation variables.
+///
+/// [`sum_vec`] cannot serve these: adding two relation variables
+/// normalizes straight to the canonical weighted sum of the algebra, and
+/// the remaining elements are not of that type, so there is no output type
+/// satisfying both of its bounds.  Summing into
+/// [`Sum<T>`](sigma_proofs::linear_relation::Sum) keeps the term type, and
+/// the arithmetic that consumes it normalizes once at the end.
+pub fn sum_vars<T: Clone>(summable: &[T]) -> sigma_proofs::linear_relation::Sum<T> {
+    summable.iter().cloned().sum()
+}

+ 2 - 2
tests/basic.rs

@@ -6,7 +6,7 @@ use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
 #[test]
 #[test]
-fn basic_test() -> sigma_proofs::errors::Result<()> {
+fn basic_test() -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (x, z, rand r, rand s),
         (x, z, rand r, rand s),
         (C, D, const cind A, const cind B),
         (C, D, const cind A, const cind B),
@@ -30,5 +30,5 @@ fn basic_test() -> sigma_proofs::errors::Result<()> {
     let witness = proof::Witness { x, z, r, s };
     let witness = proof::Witness { x, z, r, s };
 
 
     let proof = proof::prove(&instance, &witness, b"basic_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"basic_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"basic_test")
+    Ok(proof::verify(&instance, &proof, b"basic_test")?)
 }
 }

+ 2 - 2
tests/basic_sum.rs

@@ -5,7 +5,7 @@ use group::Group;
 use sha2::Sha512;
 use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
-fn basic_sum_test_vecsize(vecsize: usize) -> sigma_proofs::errors::Result<()> {
+fn basic_sum_test_vecsize(vecsize: usize) -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (vec x, y, rand vec r, rand s),
         (vec x, y, rand vec r, rand s),
         (vec C, D, const cind A, const cind B),
         (vec C, D, const cind A, const cind B),
@@ -29,7 +29,7 @@ fn basic_sum_test_vecsize(vecsize: usize) -> sigma_proofs::errors::Result<()> {
     let witness = proof::Witness { x, y, r, s };
     let witness = proof::Witness { x, y, r, s };
 
 
     let proof = proof::prove(&instance, &witness, b"basic_sum_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"basic_sum_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"basic_sum_test")
+    Ok(proof::verify(&instance, &proof, b"basic_sum_test")?)
 }
 }
 
 
 #[test]
 #[test]

+ 4 - 3
tests/basic_vec.rs

@@ -5,7 +5,7 @@ use group::Group;
 use sha2::Sha512;
 use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
-fn basic_vec_test_vecsize(vecsize: usize) -> sigma_proofs::errors::Result<()> {
+fn basic_vec_test_vecsize(vecsize: usize) -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (vec x, rand vec r),
         (vec x, rand vec r),
         (vec C, const cind A, const cind B),
         (vec C, const cind A, const cind B),
@@ -24,12 +24,13 @@ fn basic_vec_test_vecsize(vecsize: usize) -> sigma_proofs::errors::Result<()> {
     let witness = proof::Witness { x, r };
     let witness = proof::Witness { x, r };
 
 
     let proof = proof::prove(&instance, &witness, b"basic_vec_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"basic_vec_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"basic_vec_test")
+    Ok(proof::verify(&instance, &proof, b"basic_vec_test")?)
 }
 }
 
 
 #[test]
 #[test]
 fn basic_vec_test() {
 fn basic_vec_test() {
-    basic_vec_test_vecsize(0).unwrap();
+    // No equations at size 0: a relation that binds nothing is refused.
+    assert!(basic_vec_test_vecsize(0).is_err());
     basic_vec_test_vecsize(1).unwrap();
     basic_vec_test_vecsize(1).unwrap();
     basic_vec_test_vecsize(2).unwrap();
     basic_vec_test_vecsize(2).unwrap();
     basic_vec_test_vecsize(20).unwrap();
     basic_vec_test_vecsize(20).unwrap();

+ 1 - 1
tests/disj.rs

@@ -6,7 +6,7 @@ use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
 #[test]
 #[test]
-fn disj_test() -> sigma_proofs::errors::Result<()> {
+fn disj_test() -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (x, rand r),
         (x, rand r),
         (C, const cind A, const cind B),
         (C, const cind A, const cind B),

+ 4 - 3
tests/disj_vec.rs

@@ -5,7 +5,7 @@ use group::Group;
 use sha2::Sha512;
 use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
-fn disj_vec_test_vecsize(vecsize: usize) -> sigma_proofs::errors::Result<()> {
+fn disj_vec_test_vecsize(vecsize: usize) -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (vec x, vec y, pub vec a, rand vec r, rand vec s),
         (vec x, vec y, pub vec a, rand vec r, rand vec s),
         (vec C, vec D, const cind A, const cind B),
         (vec C, vec D, const cind A, const cind B),
@@ -39,12 +39,13 @@ fn disj_vec_test_vecsize(vecsize: usize) -> sigma_proofs::errors::Result<()> {
     let witness = proof::Witness { x, y, r, s };
     let witness = proof::Witness { x, y, r, s };
 
 
     let proof = proof::prove(&instance, &witness, b"disj_vec_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"disj_vec_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"disj_vec_test")
+    Ok(proof::verify(&instance, &proof, b"disj_vec_test")?)
 }
 }
 
 
 #[test]
 #[test]
 fn disj_vec_test() {
 fn disj_vec_test() {
-    disj_vec_test_vecsize(0).unwrap();
+    // No equations at size 0: a relation that binds nothing is refused.
+    assert!(disj_vec_test_vecsize(0).is_err());
     disj_vec_test_vecsize(1).unwrap();
     disj_vec_test_vecsize(1).unwrap();
     disj_vec_test_vecsize(2).unwrap();
     disj_vec_test_vecsize(2).unwrap();
     disj_vec_test_vecsize(20).unwrap();
     disj_vec_test_vecsize(20).unwrap();

+ 4 - 4
tests/dl.rs

@@ -4,7 +4,7 @@ use group::Group;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
 #[test]
 #[test]
-fn dl_zero_test() -> sigma_proofs::errors::Result<()> {
+fn dl_zero_test() -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (x),
         (x),
         (C, const B),
         (C, const B),
@@ -21,11 +21,11 @@ fn dl_zero_test() -> sigma_proofs::errors::Result<()> {
     let witness = proof::Witness { x };
     let witness = proof::Witness { x };
 
 
     let proof = proof::prove(&instance, &witness, b"dl_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"dl_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"dl_test")
+    Ok(proof::verify(&instance, &proof, b"dl_test")?)
 }
 }
 
 
 #[test]
 #[test]
-fn dl_one_test() -> sigma_proofs::errors::Result<()> {
+fn dl_one_test() -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (x),
         (x),
         (C, const B),
         (C, const B),
@@ -42,5 +42,5 @@ fn dl_one_test() -> sigma_proofs::errors::Result<()> {
     let witness = proof::Witness { x };
     let witness = proof::Witness { x };
 
 
     let proof = proof::prove(&instance, &witness, b"dl_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"dl_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"dl_test")
+    Ok(proof::verify(&instance, &proof, b"dl_test")?)
 }
 }

+ 4 - 3
tests/dot_product.rs

@@ -3,7 +3,7 @@ use curve25519_dalek::ristretto::RistrettoPoint as G;
 use group::Group;
 use group::Group;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
-fn dot_product_test_vecsize(vecsize: usize) -> sigma_proofs::errors::Result<()> {
+fn dot_product_test_vecsize(vecsize: usize) -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (vec x, pub vec a),
         (vec x, pub vec a),
         (C, D, E, F, vec A, B),
         (C, D, E, F, vec A, B),
@@ -37,12 +37,13 @@ fn dot_product_test_vecsize(vecsize: usize) -> sigma_proofs::errors::Result<()>
     let witness = proof::Witness { x };
     let witness = proof::Witness { x };
 
 
     let proof = proof::prove(&instance, &witness, b"dot_product_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"dot_product_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"dot_product_test")
+    Ok(proof::verify(&instance, &proof, b"dot_product_test")?)
 }
 }
 
 
 #[test]
 #[test]
 fn dot_product_test() {
 fn dot_product_test() {
-    dot_product_test_vecsize(0).unwrap();
+    // No equations at size 0: a relation that binds nothing is refused.
+    assert!(dot_product_test_vecsize(0).is_err());
     dot_product_test_vecsize(1).unwrap();
     dot_product_test_vecsize(1).unwrap();
     dot_product_test_vecsize(2).unwrap();
     dot_product_test_vecsize(2).unwrap();
     dot_product_test_vecsize(20).unwrap();
     dot_product_test_vecsize(20).unwrap();

+ 9 - 3
tests/emptystatement.rs

@@ -2,8 +2,15 @@
 use curve25519_dalek::ristretto::RistrettoPoint as G;
 use curve25519_dalek::ristretto::RistrettoPoint as G;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
+/// A statement with no variables and no equations binds nothing, so it is
+/// satisfied by the empty witness. sigma-proofs refuses to compile one: as a
+/// branch of a disjunction it would make the whole proof forgeable.
+///
+/// The generated prover unwraps `compile`, so the refusal arrives as a panic
+/// rather than an `Err`.
 #[test]
 #[test]
-fn emptystatement_test() -> sigma_proofs::errors::Result<()> {
+#[should_panic(expected = "the statement has no content")]
+fn emptystatement_test() {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (),
         (),
         (),
         (),
@@ -14,6 +21,5 @@ fn emptystatement_test() -> sigma_proofs::errors::Result<()> {
     let instance = proof::Instance {};
     let instance = proof::Instance {};
     let witness = proof::Witness {};
     let witness = proof::Witness {};
 
 
-    let proof = proof::prove(&instance, &witness, b"emptystatement_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"emptystatement_test")
+    assert!(proof::prove(&instance, &witness, b"emptystatement_test", &mut rng).is_err());
 }
 }

+ 4 - 4
tests/left_expr.rs

@@ -6,7 +6,7 @@ use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
 #[test]
 #[test]
-fn left_expr_test() -> sigma_proofs::errors::Result<()> {
+fn left_expr_test() -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (x, y, pub a, rand r, rand s),
         (x, y, pub a, rand r, rand s),
         (C, D, const cind A, const cind B),
         (C, D, const cind A, const cind B),
@@ -30,11 +30,11 @@ fn left_expr_test() -> sigma_proofs::errors::Result<()> {
     let witness = proof::Witness { x, y, r, s };
     let witness = proof::Witness { x, y, r, s };
 
 
     let proof = proof::prove(&instance, &witness, b"left_expr_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"left_expr_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"left_expr_test")
+    Ok(proof::verify(&instance, &proof, b"left_expr_test")?)
 }
 }
 
 
 #[test]
 #[test]
-fn left_expr_vec_test() -> sigma_proofs::errors::Result<()> {
+fn left_expr_vec_test() -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (vec x, vec y, z, pub vec a, pub b, rand vec r, rand vec s, rand t),
         (vec x, vec y, z, pub vec a, pub b, rand vec r, rand vec s, rand t),
         (vec C, vec D, E, const cind A, const cind B),
         (vec C, vec D, E, const cind A, const cind B),
@@ -78,5 +78,5 @@ fn left_expr_vec_test() -> sigma_proofs::errors::Result<()> {
     let witness = proof::Witness { x, y, z, r, s, t };
     let witness = proof::Witness { x, y, z, r, s, t };
 
 
     let proof = proof::prove(&instance, &witness, b"left_expr_vec_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"left_expr_vec_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"left_expr_vec_test")
+    Ok(proof::verify(&instance, &proof, b"left_expr_vec_test")?)
 }
 }

+ 2 - 2
tests/notequals.rs

@@ -5,7 +5,7 @@ use group::Group;
 use sha2::Sha512;
 use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
-fn do_test(x_u128: u128) -> sigma_proofs::errors::Result<()> {
+fn do_test(x_u128: u128) -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (x, rand r),
         (x, rand r),
         (C, const cind A, const cind B),
         (C, const cind A, const cind B),
@@ -25,7 +25,7 @@ fn do_test(x_u128: u128) -> sigma_proofs::errors::Result<()> {
     let witness = proof::Witness { x, r };
     let witness = proof::Witness { x, r };
 
 
     let proof = proof::prove(&instance, &witness, b"notequals_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"notequals_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"notequals_test")
+    Ok(proof::verify(&instance, &proof, b"notequals_test")?)
 }
 }
 
 
 #[test]
 #[test]

+ 2 - 2
tests/pubscalars.rs

@@ -5,7 +5,7 @@ use group::Group;
 use sha2::Sha512;
 use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
-fn pubscalars_test_val(b_val: u128) -> sigma_proofs::errors::Result<()> {
+fn pubscalars_test_val(b_val: u128) -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (x, z, rand r, rand s, pub a, pub b),
         (x, z, rand r, rand s, pub a, pub b),
         (C, D, const cind A, const cind B),
         (C, D, const cind A, const cind B),
@@ -32,7 +32,7 @@ fn pubscalars_test_val(b_val: u128) -> sigma_proofs::errors::Result<()> {
     let witness = proof::Witness { x, z, r, s };
     let witness = proof::Witness { x, z, r, s };
 
 
     let proof = proof::prove(&instance, &witness, b"pubscalars_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"pubscalars_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"pubscalars_test")
+    Ok(proof::verify(&instance, &proof, b"pubscalars_test")?)
 }
 }
 
 
 #[test]
 #[test]

+ 2 - 2
tests/pubscalars_or.rs

@@ -5,7 +5,7 @@ use group::Group;
 use sha2::Sha512;
 use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
-fn pubscalars_or_test_val(b_val: u128) -> sigma_proofs::errors::Result<()> {
+fn pubscalars_or_test_val(b_val: u128) -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (x, rand r, pub a, pub b),
         (x, rand r, pub a, pub b),
         (C, const cind A, const cind B),
         (C, const cind A, const cind B),
@@ -30,7 +30,7 @@ fn pubscalars_or_test_val(b_val: u128) -> sigma_proofs::errors::Result<()> {
     let witness = proof::Witness { x, r };
     let witness = proof::Witness { x, r };
 
 
     let proof = proof::prove(&instance, &witness, b"pubscalars_or_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"pubscalars_or_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"pubscalars_or_test")
+    Ok(proof::verify(&instance, &proof, b"pubscalars_or_test")?)
 }
 }
 
 
 #[test]
 #[test]

+ 2 - 2
tests/pubscalars_or_and.rs

@@ -5,7 +5,7 @@ use group::Group;
 use sha2::Sha512;
 use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
-fn pubscalars_or_and_test_val(x_val: u128, b_val: u128) -> sigma_proofs::errors::Result<()> {
+fn pubscalars_or_and_test_val(x_val: u128, b_val: u128) -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (x, rand r, pub a, pub b),
         (x, rand r, pub a, pub b),
         (C, const cind A, const cind B),
         (C, const cind A, const cind B),
@@ -36,7 +36,7 @@ fn pubscalars_or_and_test_val(x_val: u128, b_val: u128) -> sigma_proofs::errors:
     let witness = proof::Witness { x, r };
     let witness = proof::Witness { x, r };
 
 
     let proof = proof::prove(&instance, &witness, b"pubscalars_or_and_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"pubscalars_or_and_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"pubscalars_or_and_test")
+    Ok(proof::verify(&instance, &proof, b"pubscalars_or_and_test")?)
 }
 }
 
 
 #[test]
 #[test]

+ 9 - 19
tests/pubscalars_or_and_vec.rs

@@ -9,7 +9,7 @@ fn pubscalars_or_vec_test_vecsize_val(
     vecsize: usize,
     vecsize: usize,
     b_val: u128,
     b_val: u128,
     x_val: Option<u128>,
     x_val: Option<u128>,
-) -> sigma_proofs::errors::Result<()> {
+) -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (vec x, vec y, pub vec a, pub vec b, rand vec r, rand vec s),
         (vec x, vec y, pub vec a, pub vec b, rand vec r, rand vec s),
         (vec C, vec D, const cind A, const cind B),
         (vec C, vec D, const cind A, const cind B),
@@ -56,27 +56,17 @@ fn pubscalars_or_vec_test_vecsize_val(
     let witness = proof::Witness { x, y, r, s };
     let witness = proof::Witness { x, y, r, s };
 
 
     let proof = proof::prove(&instance, &witness, b"pubscalars_vec_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"pubscalars_vec_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"pubscalars_vec_test")
+    Ok(proof::verify(&instance, &proof, b"pubscalars_vec_test")?)
 }
 }
 
 
 fn pubscalars_or_vec_emptyvec() {
 fn pubscalars_or_vec_emptyvec() {
-    pubscalars_or_vec_test_vecsize_val(0, 0, Some(0)).unwrap();
-    pubscalars_or_vec_test_vecsize_val(0, 1, Some(0)).unwrap();
-    pubscalars_or_vec_test_vecsize_val(0, 2, Some(0)).unwrap();
-    pubscalars_or_vec_test_vecsize_val(0, 3, Some(0)).unwrap();
-    pubscalars_or_vec_test_vecsize_val(0, 4, Some(0)).unwrap();
-
-    pubscalars_or_vec_test_vecsize_val(0, 0, Some(1)).unwrap();
-    pubscalars_or_vec_test_vecsize_val(0, 1, Some(1)).unwrap();
-    pubscalars_or_vec_test_vecsize_val(0, 2, Some(1)).unwrap();
-    pubscalars_or_vec_test_vecsize_val(0, 3, Some(1)).unwrap();
-    pubscalars_or_vec_test_vecsize_val(0, 4, Some(1)).unwrap();
-
-    pubscalars_or_vec_test_vecsize_val(0, 0, None).unwrap();
-    pubscalars_or_vec_test_vecsize_val(0, 1, None).unwrap();
-    pubscalars_or_vec_test_vecsize_val(0, 2, None).unwrap();
-    pubscalars_or_vec_test_vecsize_val(0, 3, None).unwrap();
-    pubscalars_or_vec_test_vecsize_val(0, 4, None).unwrap();
+    // No equations at size 0: a relation that binds nothing is refused, for
+    // every combination of the public scalar and the optional AND branch.
+    for x_val in [Some(0), Some(1), None] {
+        for b_val in 0..=4 {
+            assert!(pubscalars_or_vec_test_vecsize_val(0, b_val, x_val).is_err());
+        }
+    }
 }
 }
 
 
 fn pubscalars_or_vec_vecsize(vecsize: usize) {
 fn pubscalars_or_vec_vecsize(vecsize: usize) {

+ 7 - 7
tests/pubscalars_or_vec.rs

@@ -8,7 +8,7 @@ use sigma_compiler::*;
 fn pubscalars_or_vec_test_vecsize_val(
 fn pubscalars_or_vec_test_vecsize_val(
     vecsize: usize,
     vecsize: usize,
     b_val: u128,
     b_val: u128,
-) -> sigma_proofs::errors::Result<()> {
+) -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (vec x, pub vec a, pub vec b, rand vec r),
         (vec x, pub vec a, pub vec b, rand vec r),
         (vec C, const cind A, const cind B),
         (vec C, const cind A, const cind B),
@@ -37,16 +37,16 @@ fn pubscalars_or_vec_test_vecsize_val(
     let witness = proof::Witness { x, r };
     let witness = proof::Witness { x, r };
 
 
     let proof = proof::prove(&instance, &witness, b"pubscalars_vec_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"pubscalars_vec_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"pubscalars_vec_test")
+    Ok(proof::verify(&instance, &proof, b"pubscalars_vec_test")?)
 }
 }
 
 
 #[test]
 #[test]
 fn pubscalars_or_vec_test() {
 fn pubscalars_or_vec_test() {
-    pubscalars_or_vec_test_vecsize_val(0, 0).unwrap();
-    pubscalars_or_vec_test_vecsize_val(0, 1).unwrap();
-    pubscalars_or_vec_test_vecsize_val(0, 2).unwrap();
-    pubscalars_or_vec_test_vecsize_val(0, 3).unwrap();
-    pubscalars_or_vec_test_vecsize_val(0, 4).unwrap();
+    // No equations at size 0: a relation that binds nothing is refused,
+    // whichever branch the public scalar selects.
+    for b_val in 0..=4 {
+        assert!(pubscalars_or_vec_test_vecsize_val(0, b_val).is_err());
+    }
     for vecsize in [1, 2, 20] {
     for vecsize in [1, 2, 20] {
         pubscalars_or_vec_test_vecsize_val(vecsize, 0).unwrap();
         pubscalars_or_vec_test_vecsize_val(vecsize, 0).unwrap();
         pubscalars_or_vec_test_vecsize_val(vecsize, 1).unwrap_err();
         pubscalars_or_vec_test_vecsize_val(vecsize, 1).unwrap_err();

+ 4 - 3
tests/pubscalars_vec.rs

@@ -5,7 +5,7 @@ use group::Group;
 use sha2::Sha512;
 use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
-fn pubscalars_vec_test_vecsize(vecsize: usize) -> sigma_proofs::errors::Result<()> {
+fn pubscalars_vec_test_vecsize(vecsize: usize) -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (vec x, vec y, pub vec a, pub vec b, rand vec r, rand vec s),
         (vec x, vec y, pub vec a, pub vec b, rand vec r, rand vec s),
         (vec C, vec D, const cind A, const cind B),
         (vec C, vec D, const cind A, const cind B),
@@ -36,12 +36,13 @@ fn pubscalars_vec_test_vecsize(vecsize: usize) -> sigma_proofs::errors::Result<(
     let witness = proof::Witness { x, y, r, s };
     let witness = proof::Witness { x, y, r, s };
 
 
     let proof = proof::prove(&instance, &witness, b"pubscalars_vec_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"pubscalars_vec_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"pubscalars_vec_test")
+    Ok(proof::verify(&instance, &proof, b"pubscalars_vec_test")?)
 }
 }
 
 
 #[test]
 #[test]
 fn pubscalars_vec_test() {
 fn pubscalars_vec_test() {
-    pubscalars_vec_test_vecsize(0).unwrap();
+    // No equations at size 0: a relation that binds nothing is refused.
+    assert!(pubscalars_vec_test_vecsize(0).is_err());
     pubscalars_vec_test_vecsize(1).unwrap();
     pubscalars_vec_test_vecsize(1).unwrap();
     pubscalars_vec_test_vecsize(2).unwrap();
     pubscalars_vec_test_vecsize(2).unwrap();
     pubscalars_vec_test_vecsize(20).unwrap();
     pubscalars_vec_test_vecsize(20).unwrap();

+ 2 - 2
tests/pubstatements.rs

@@ -5,7 +5,7 @@ use group::Group;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
 #[test]
 #[test]
-fn pubstatements_test() -> sigma_proofs::errors::Result<()> {
+fn pubstatements_test() -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (x, pub a),
         (x, pub a),
         (C, D, const cind B),
         (C, D, const cind B),
@@ -25,5 +25,5 @@ fn pubstatements_test() -> sigma_proofs::errors::Result<()> {
     let witness = proof::Witness { x };
     let witness = proof::Witness { x };
 
 
     let proof = proof::prove(&instance, &witness, b"pubstatements_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"pubstatements_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"pubstatements_test")
+    Ok(proof::verify(&instance, &proof, b"pubstatements_test")?)
 }
 }

+ 2 - 2
tests/pubstatements_vec.rs

@@ -4,7 +4,7 @@ use group::ff::PrimeField;
 use group::Group;
 use group::Group;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
-fn pubstatements_vec_test_vecsize(vecsize: usize) -> sigma_proofs::errors::Result<()> {
+fn pubstatements_vec_test_vecsize(vecsize: usize) -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (vec x, pub vec a),
         (vec x, pub vec a),
         (vec C, vec D, const cind B),
         (vec C, vec D, const cind B),
@@ -24,7 +24,7 @@ fn pubstatements_vec_test_vecsize(vecsize: usize) -> sigma_proofs::errors::Resul
     let witness = proof::Witness { x };
     let witness = proof::Witness { x };
 
 
     let proof = proof::prove(&instance, &witness, b"pubstatements_vec_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"pubstatements_vec_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"pubstatements_vec_test")
+    Ok(proof::verify(&instance, &proof, b"pubstatements_vec_test")?)
 }
 }
 
 
 #[test]
 #[test]

+ 2 - 2
tests/range.rs

@@ -6,7 +6,7 @@ use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
 #[test]
 #[test]
-fn range_test() -> sigma_proofs::errors::Result<()> {
+fn range_test() -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (x, y, pub a, rand r),
         (x, y, pub a, rand r),
         (C, D, const cind A, const cind B),
         (C, D, const cind A, const cind B),
@@ -31,5 +31,5 @@ fn range_test() -> sigma_proofs::errors::Result<()> {
     let witness = proof::Witness { x, y, r };
     let witness = proof::Witness { x, y, r };
 
 
     let proof = proof::prove(&instance, &witness, b"range_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"range_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"range_test")
+    Ok(proof::verify(&instance, &proof, b"range_test")?)
 }
 }

+ 2 - 2
tests/range_dump.rs

@@ -7,7 +7,7 @@ use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
 #[test]
 #[test]
-fn range_dump_test() -> sigma_proofs::errors::Result<()> {
+fn range_dump_test() -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (x, y, pub a, rand r),
         (x, y, pub a, rand r),
         (C, D, const cind A, const cind B),
         (C, D, const cind A, const cind B),
@@ -39,5 +39,5 @@ fn range_dump_test() -> sigma_proofs::errors::Result<()> {
     let buf = sigma_compiler::dumper::dump_buffer();
     let buf = sigma_compiler::dumper::dump_buffer();
     print!("{buf}");
     print!("{buf}");
 
 
-    res
+    Ok(res?)
 }
 }

+ 1 - 1
tests/simple_or.rs

@@ -5,7 +5,7 @@ use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
 #[test]
 #[test]
-fn simple_or_test() -> sigma_proofs::errors::Result<()> {
+fn simple_or_test() -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (x, y),
         (x, y),
         (C, const cind A, const cind B),
         (C, const cind A, const cind B),

+ 2 - 2
tests/substitution_or.rs

@@ -6,7 +6,7 @@ use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
 #[test]
 #[test]
-fn substitution_or_test() -> sigma_proofs::errors::Result<()> {
+fn substitution_or_test() -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (x, z, rand r, rand s),
         (x, z, rand r, rand s),
         (C, D, const cind A, const cind B),
         (C, D, const cind A, const cind B),
@@ -33,5 +33,5 @@ fn substitution_or_test() -> sigma_proofs::errors::Result<()> {
     let witness = proof::Witness { x, z, r, s };
     let witness = proof::Witness { x, z, r, s };
 
 
     let proof = proof::prove(&instance, &witness, b"substitution_or_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"substitution_or_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"substitution_or_test")
+    Ok(proof::verify(&instance, &proof, b"substitution_or_test")?)
 }
 }

+ 4 - 3
tests/substitution_vec.rs

@@ -5,7 +5,7 @@ use group::Group;
 use sha2::Sha512;
 use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
-fn substitution_vec_test_vecsize(vecsize: usize) -> sigma_proofs::errors::Result<()> {
+fn substitution_vec_test_vecsize(vecsize: usize) -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (vec x, vec y, rand vec r, rand vec s),
         (vec x, vec y, rand vec r, rand vec s),
         (vec C, vec D, const cind A, const cind B),
         (vec C, vec D, const cind A, const cind B),
@@ -29,12 +29,13 @@ fn substitution_vec_test_vecsize(vecsize: usize) -> sigma_proofs::errors::Result
     let witness = proof::Witness { x, y, r, s };
     let witness = proof::Witness { x, y, r, s };
 
 
     let proof = proof::prove(&instance, &witness, b"substitution_vec_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"substitution_vec_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"substitution_vec_test")
+    Ok(proof::verify(&instance, &proof, b"substitution_vec_test")?)
 }
 }
 
 
 #[test]
 #[test]
 fn substitution_vec_test() {
 fn substitution_vec_test() {
-    substitution_vec_test_vecsize(0).unwrap();
+    // No equations at size 0: a relation that binds nothing is refused.
+    assert!(substitution_vec_test_vecsize(0).is_err());
     substitution_vec_test_vecsize(1).unwrap();
     substitution_vec_test_vecsize(1).unwrap();
     substitution_vec_test_vecsize(2).unwrap();
     substitution_vec_test_vecsize(2).unwrap();
     substitution_vec_test_vecsize(20).unwrap();
     substitution_vec_test_vecsize(20).unwrap();

+ 2 - 2
tests/subtract.rs

@@ -4,7 +4,7 @@ use group::Group;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
 #[test]
 #[test]
-fn subtract_test() -> sigma_proofs::errors::Result<()> {
+fn subtract_test() -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (x),
         (x),
         (C, const cind B),
         (C, const cind B),
@@ -21,5 +21,5 @@ fn subtract_test() -> sigma_proofs::errors::Result<()> {
     let witness = proof::Witness { x };
     let witness = proof::Witness { x };
 
 
     let proof = proof::prove(&instance, &witness, b"subtract_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"subtract_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"subtract_test")
+    Ok(proof::verify(&instance, &proof, b"subtract_test")?)
 }
 }

+ 4 - 3
tests/subtract_vec.rs

@@ -5,7 +5,7 @@ use group::Group;
 use sha2::Sha512;
 use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
-fn subtract_vec_test_vecsize(vecsize: usize) -> sigma_proofs::errors::Result<()> {
+fn subtract_vec_test_vecsize(vecsize: usize) -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (vec x),
         (vec x),
         (vec C, vec D, vec E, const cind A, const cind B),
         (vec C, vec D, vec E, const cind A, const cind B),
@@ -33,12 +33,13 @@ fn subtract_vec_test_vecsize(vecsize: usize) -> sigma_proofs::errors::Result<()>
     let witness = proof::Witness { x };
     let witness = proof::Witness { x };
 
 
     let proof = proof::prove(&instance, &witness, b"subtract_vec_test", &mut rng)?;
     let proof = proof::prove(&instance, &witness, b"subtract_vec_test", &mut rng)?;
-    proof::verify(&instance, &proof, b"subtract_vec_test")
+    Ok(proof::verify(&instance, &proof, b"subtract_vec_test")?)
 }
 }
 
 
 #[test]
 #[test]
 fn subtract_vec_test() {
 fn subtract_vec_test() {
-    subtract_vec_test_vecsize(0).unwrap();
+    // No equations at size 0: a relation that binds nothing is refused.
+    assert!(subtract_vec_test_vecsize(0).is_err());
     subtract_vec_test_vecsize(1).unwrap();
     subtract_vec_test_vecsize(1).unwrap();
     subtract_vec_test_vecsize(2).unwrap();
     subtract_vec_test_vecsize(2).unwrap();
     subtract_vec_test_vecsize(20).unwrap();
     subtract_vec_test_vecsize(20).unwrap();

+ 11 - 13
tests/threshold.rs

@@ -5,7 +5,7 @@ use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
 #[test]
 #[test]
-fn threshold_test() -> sigma_proofs::errors::Result<()> {
+fn threshold_test() -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { thresh3,
     sigma_compiler! { thresh3,
         (x1, x2, x3, x4, x5, rand r),
         (x1, x2, x3, x4, x5, rand r),
         (C, const cind G0, const cind G1, const cind G2, const cind G3,
         (C, const cind G0, const cind G1, const cind G2, const cind G3,
@@ -53,18 +53,16 @@ fn threshold_test() -> sigma_proofs::errors::Result<()> {
             r,
             r,
         };
         };
 
 
-        match thresh3::prove(&instance, &witness, b"thresh_test", &mut rng) {
-            Ok(_) if num_true < 3 => {
-                panic!("THRESH passed when it should have failed (true_pattern = {true_pattern})")
-            }
-            Err(_) if num_true >= 3 => {
-                panic!("THRESH failed when it should have passed (true_pattern = {true_pattern})")
-            }
-            Ok(proof) => {
-                thresh3::verify(&instance, &proof, b"thresh_test")?;
-            }
-            Err(_) => {}
-        }
+        // The prover checks witness shape, not validity: an unsatisfiable
+        // threshold still yields a NARG string, and verification is what
+        // rejects it.
+        let proof = thresh3::prove(&instance, &witness, b"thresh_test", &mut rng)?;
+        let verified = thresh3::verify(&instance, &proof, b"thresh_test").is_ok();
+        assert_eq!(
+            verified,
+            num_true >= 3,
+            "THRESH verification disagreed with satisfiability (true_pattern = {true_pattern})"
+        );
     }
     }
     Ok(())
     Ok(())
 }
 }

+ 11 - 13
tests/threshold_pubscalars.rs

@@ -5,7 +5,7 @@ use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
 #[test]
 #[test]
-fn threshold_pubscalars_test() -> sigma_proofs::errors::Result<()> {
+fn threshold_pubscalars_test() -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { thresh3,
     sigma_compiler! { thresh3,
         (pub x1, pub x2, pub x3, pub x4, pub x5, rand r),
         (pub x1, pub x2, pub x3, pub x4, pub x5, rand r),
         (C, const cind G0, const cind G1, const cind G2, const cind G3,
         (C, const cind G0, const cind G1, const cind G2, const cind G3,
@@ -51,18 +51,16 @@ fn threshold_pubscalars_test() -> sigma_proofs::errors::Result<()> {
         };
         };
         let witness = thresh3::Witness { r };
         let witness = thresh3::Witness { r };
 
 
-        match thresh3::prove(&instance, &witness, b"thresh_pubscalars_test", &mut rng) {
-            Ok(_) if num_true < 3 => {
-                panic!("THRESH passed when it should have failed (true_pattern = {true_pattern})")
-            }
-            Err(_) if num_true >= 3 => {
-                panic!("THRESH failed when it should have passed (true_pattern = {true_pattern})")
-            }
-            Ok(proof) => {
-                thresh3::verify(&instance, &proof, b"thresh_pubscalars_test")?;
-            }
-            Err(_) => {}
-        }
+        // The prover checks witness shape, not validity: an unsatisfiable
+        // threshold still yields a NARG string, and verification is what
+        // rejects it.
+        let proof = thresh3::prove(&instance, &witness, b"thresh_pubscalars_test", &mut rng)?;
+        let verified = thresh3::verify(&instance, &proof, b"thresh_pubscalars_test").is_ok();
+        assert_eq!(
+            verified,
+            num_true >= 3,
+            "THRESH verification disagreed with satisfiability (true_pattern = {true_pattern})"
+        );
     }
     }
     Ok(())
     Ok(())
 }
 }

+ 1 - 1
tests/two_true.rs

@@ -5,7 +5,7 @@ use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 
 
 #[test]
 #[test]
-fn two_true_test() -> sigma_proofs::errors::Result<()> {
+fn two_true_test() -> Result<(), Box<dyn std::error::Error>> {
     sigma_compiler! { proof,
     sigma_compiler! { proof,
         (x, y),
         (x, y),
         (C, D, const cind A, const cind B),
         (C, D, const cind A, const cind B),