Skip to content

Commit

Permalink
Merge pull request #2194 from AleoHQ/prevent_panics
Browse files Browse the repository at this point in the history
Prevent panics
  • Loading branch information
howardwu authored Nov 24, 2023
2 parents 7f69a82 + 2fc2e73 commit b93295c
Show file tree
Hide file tree
Showing 19 changed files with 232 additions and 285 deletions.
12 changes: 0 additions & 12 deletions algorithms/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,10 @@ pub enum SNARKError {

#[error("Circuit not found")]
CircuitNotFound,

#[error("terminated")]
Terminated,
}

impl From<AHPError> for SNARKError {
fn from(err: AHPError) -> Self {
SNARKError::Crate("AHPError", format!("{err:?}"))
}
}

impl From<crate::polycommit::PCError> for SNARKError {
fn from(err: crate::polycommit::PCError) -> Self {
match err {
crate::polycommit::PCError::Terminated => SNARKError::Terminated,
err => SNARKError::Crate("PCError", format!("{err:?}")),
}
}
}
110 changes: 26 additions & 84 deletions algorithms/src/polycommit/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,74 +13,75 @@
// limitations under the License.

/// The error type for `PolynomialCommitment`.
#[derive(Debug)]
#[derive(Debug, Error)]
pub enum PCError {
AnyhowError(anyhow::Error),
#[error("{0}")]
AnyhowError(#[from] anyhow::Error),

/// The query set contains a label for a polynomial that was not provided as
/// input to the `PC::open`.
#[error("QuerySet` refers to polynomial \"{label}\", but it was not provided.")]
MissingPolynomial {
/// The label of the missing polynomial.
/// The label of the missing polynomial
label: String,
},

/// `Evaluations` does not contain an evaluation for the polynomial labelled
/// `label` at a particular query.
#[error("`QuerySet` refers to polynomial \"{label}\", but `Evaluations` does not contain an evaluation for it.")]
MissingEvaluation {
/// The label of the missing polynomial.
label: String,
},

/// The provided polynomial was meant to be hiding, but `rng` was `None`.
#[error("The provided polynomial was meant to be hiding, but `rng` was `None`.")]
MissingRng,

/// The degree provided in setup was too small; degree 0 polynomials
/// are not supported.
#[error("The degree provided in setup was too small; degree 0 polynomials are not supported.")]
DegreeIsZero,

/// The degree of the polynomial passed to `commit` or `open`
/// was too large.
#[error(
"the number of coefficients in the polynomial ({num_coefficients:?}) is greater than \
the maximum number of powers in `Powers` ({num_powers:?})"
)]
TooManyCoefficients {
/// The number of coefficients in the polynomial.
num_coefficients: usize,
/// The maximum number of powers provided in `Powers`.
num_powers: usize,
},

/// The hiding bound was not `None`, but the hiding bound was zero.
#[error("The hiding bound was not `None`, but the hiding bound was zero.")]
HidingBoundIsZero,

/// The hiding bound was too large for the given `Powers`.
#[error(
"the degree of the hiding poly ({hiding_poly_degree:?}) is not less than the maximum number of powers in `Powers` ({num_powers:?})"
)]
HidingBoundToolarge {
/// The hiding bound
hiding_poly_degree: usize,
/// The number of powers.
num_powers: usize,
},

/// The lagrange basis is not a power of two.
#[error("The lagrange basis is not a power of two.")]
LagrangeBasisSizeIsNotPowerOfTwo,

/// The lagrange basis is larger than the supported degree,
#[error("The lagrange basis is larger than the supported degree.")]
LagrangeBasisSizeIsTooLarge,

/// The degree provided to `trim` was too large.
#[error("The degree provided to `trim` was too large.")]
TrimmingDegreeTooLarge,

/// The provided equation contained multiple polynomials, of which least one
/// had a strict degree bound.
#[error("the eqaution \"{0}\" contained degree-bounded polynomials")]
EquationHasDegreeBounds(String),

/// The required degree bound is not supported by ck/vk
#[error("the degree bound ({0}) is not supported by the parameters")]
UnsupportedDegreeBound(usize),

/// The provided equation contained multiple polynomials, of which least one
/// had a strict degree bound.
#[error("the Lagrange basis size ({0}) is not supported by the parameters")]
UnsupportedLagrangeBasisSize(usize),

/// The degree bound for the `index`-th polynomial passed to `commit`, `open`
/// or `check` was incorrect, that is, `degree_bound >= poly_degree` or
/// `degree_bound <= max_degree`.
#[error(
"the degree bound ({degree_bound}) for the polynomial {label} \
(having degree {poly_degree}) is greater than the maximum degree ({max_degree})"
)]
IncorrectDegreeBound {
/// Degree of the polynomial.
poly_degree: usize,
Expand All @@ -91,63 +92,4 @@ pub enum PCError {
/// Index of the offending polynomial.
label: String,
},

Terminated,
}

impl snarkvm_utilities::error::Error for PCError {}

impl From<anyhow::Error> for PCError {
fn from(other: anyhow::Error) -> Self {
Self::AnyhowError(other)
}
}

impl core::fmt::Display for PCError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::AnyhowError(error) => write!(f, "{error}"),
Self::MissingPolynomial { label } => {
write!(f, "`QuerySet` refers to polynomial \"{label}\", but it was not provided.")
}
Self::MissingEvaluation { label } => write!(
f,
"`QuerySet` refers to polynomial \"{label}\", but `Evaluations` does not contain an evaluation for it."
),
Self::MissingRng => write!(f, "hiding commitments require `Some(rng)`"),
Self::DegreeIsZero => write!(f, "this scheme does not support committing to degree 0 polynomials"),
Self::TooManyCoefficients { num_coefficients, num_powers } => write!(
f,
"the number of coefficients in the polynomial ({num_coefficients:?}) is greater than\
the maximum number of powers in `Powers` ({num_powers:?})"
),
Self::HidingBoundIsZero => write!(f, "this scheme does not support non-`None` hiding bounds that are 0"),
Self::HidingBoundToolarge { hiding_poly_degree, num_powers } => write!(
f,
"the degree of the hiding poly ({hiding_poly_degree:?}) is not less than the maximum number of powers in `Powers` ({num_powers:?})"
),
Self::TrimmingDegreeTooLarge => write!(f, "the degree provided to `trim` was too large"),
Self::EquationHasDegreeBounds(e) => {
write!(f, "the eqaution \"{e}\" contained degree-bounded polynomials")
}
Self::UnsupportedDegreeBound(bound) => {
write!(f, "the degree bound ({bound:?}) is not supported by the parameters")
}
Self::LagrangeBasisSizeIsNotPowerOfTwo => {
write!(f, "the Lagrange Basis size is not a power of two")
}
Self::UnsupportedLagrangeBasisSize(size) => {
write!(f, "the Lagrange basis size ({size:?}) is not supported by the parameters")
}
Self::LagrangeBasisSizeIsTooLarge => {
write!(f, "the Lagrange Basis size larger than max supported degree")
}
Self::IncorrectDegreeBound { poly_degree, degree_bound, max_degree, label } => write!(
f,
"the degree bound ({degree_bound}) for the polynomial {label} \
(having degree {poly_degree}) is greater than the maximum degree ({max_degree})"
),
Self::Terminated => write!(f, "terminated"),
}
}
}
3 changes: 1 addition & 2 deletions algorithms/src/polycommit/sonic_pc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,10 +515,9 @@ impl<E: PairingEngine, S: AlgebraicSponge<E::Fq, 2>> SonicKZG10<E, S> {
.ok_or(PCError::MissingPolynomial { label: label.to_string() })?;

if cur_comm.degree_bound().is_some() {
if num_polys != 1 {
if num_polys != 1 || !coeff.is_one() {
return Err(PCError::EquationHasDegreeBounds(lc_label));
}
assert!(coeff.is_one(), "Coefficient must be one for degree-bounded equations");
degree_bound = cur_comm.degree_bound();
}
coeffs_and_comms.push((*coeff, cur_comm.commitment()));
Expand Down
2 changes: 1 addition & 1 deletion algorithms/src/r1cs/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub enum SynthesisError {
Unsatisfiable,
/// During synthesis, our polynomials ended up being too high of degree
#[error("Polynomial degree is too large")]
PolynomialDegreeTooLarge,
PolyTooLarge,
/// During proof generation, we encountered an identity in the CRS
#[error("Encountered an identity element in the CRS")]
UnexpectedIdentity,
Expand Down
81 changes: 44 additions & 37 deletions algorithms/src/snark/varuna/ahp/ahp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use crate::{
SNARKMode,
},
};
use anyhow::anyhow;
use anyhow::{anyhow, ensure, Result};
use snarkvm_fields::{Field, PrimeField};

use core::{borrow::Borrow, marker::PhantomData};
Expand Down Expand Up @@ -79,17 +79,17 @@ impl<F: PrimeField, SM: SNARKMode> AHPForR1CS<F, SM> {
/// of this protocol.
/// The number of the variables must include the "one" variable. That is, it
/// must be with respect to the number of formatted public inputs.
pub fn max_degree(num_constraints: usize, num_variables: usize, num_non_zero: usize) -> Result<usize, AHPError> {
pub fn max_degree(num_constraints: usize, num_variables: usize, num_non_zero: usize) -> Result<usize> {
let zk_bound = Self::zk_bound().unwrap_or(0);
let constraint_domain_size =
EvaluationDomain::<F>::compute_size_of_domain(num_constraints).ok_or(AHPError::PolynomialDegreeTooLarge)?;
EvaluationDomain::<F>::compute_size_of_domain(num_constraints).ok_or(AHPError::PolyTooLarge)?;
let variable_domain_size =
EvaluationDomain::<F>::compute_size_of_domain(num_variables).ok_or(AHPError::PolynomialDegreeTooLarge)?;
EvaluationDomain::<F>::compute_size_of_domain(num_variables).ok_or(AHPError::PolyTooLarge)?;
let non_zero_domain_size =
EvaluationDomain::<F>::compute_size_of_domain(num_non_zero).ok_or(AHPError::PolynomialDegreeTooLarge)?;
EvaluationDomain::<F>::compute_size_of_domain(num_non_zero).ok_or(AHPError::PolyTooLarge)?;

// these should correspond with the bounds set in the <round>.rs files
Ok(*[
[
2 * constraint_domain_size + 2 * zk_bound - 2,
2 * variable_domain_size + 2 * zk_bound - 2,
if SM::ZK { variable_domain_size + 3 } else { 0 }, // mask_poly
Expand All @@ -99,34 +99,40 @@ impl<F: PrimeField, SM: SNARKMode> AHPForR1CS<F, SM> {
]
.iter()
.max()
.unwrap())
.copied()
.ok_or(anyhow!("Could not find max_degree"))
}

/// Get all the strict degree bounds enforced in the AHP.
pub fn get_degree_bounds(info: &CircuitInfo) -> [usize; 4] {
pub fn get_degree_bounds(info: &CircuitInfo) -> Result<[usize; 4]> {
let num_variables = info.num_variables;
let num_non_zero_a = info.num_non_zero_a;
let num_non_zero_b = info.num_non_zero_b;
let num_non_zero_c = info.num_non_zero_c;
[
EvaluationDomain::<F>::compute_size_of_domain(num_variables).unwrap() - 2,
EvaluationDomain::<F>::compute_size_of_domain(num_non_zero_a).unwrap() - 2,
EvaluationDomain::<F>::compute_size_of_domain(num_non_zero_b).unwrap() - 2,
EvaluationDomain::<F>::compute_size_of_domain(num_non_zero_c).unwrap() - 2,
]
Ok([
EvaluationDomain::<F>::compute_size_of_domain(num_variables).ok_or(SynthesisError::PolyTooLarge)? - 2,
EvaluationDomain::<F>::compute_size_of_domain(num_non_zero_a).ok_or(SynthesisError::PolyTooLarge)? - 2,
EvaluationDomain::<F>::compute_size_of_domain(num_non_zero_b).ok_or(SynthesisError::PolyTooLarge)? - 2,
EvaluationDomain::<F>::compute_size_of_domain(num_non_zero_c).ok_or(SynthesisError::PolyTooLarge)? - 2,
])
}

pub(crate) fn cmp_non_zero_domains(
info: &CircuitInfo,
max_candidate: Option<EvaluationDomain<F>>,
) -> Result<NonZeroDomains<F>, SynthesisError> {
let domain_a = EvaluationDomain::new(info.num_non_zero_a).ok_or(SynthesisError::PolynomialDegreeTooLarge)?;
let domain_b = EvaluationDomain::new(info.num_non_zero_b).ok_or(SynthesisError::PolynomialDegreeTooLarge)?;
let domain_c = EvaluationDomain::new(info.num_non_zero_c).ok_or(SynthesisError::PolynomialDegreeTooLarge)?;
let new_candidate = [domain_a, domain_b, domain_c].into_iter().max_by_key(|d| d.size()).unwrap();
) -> Result<NonZeroDomains<F>> {
let domain_a = EvaluationDomain::new(info.num_non_zero_a).ok_or(SynthesisError::PolyTooLarge)?;
let domain_b = EvaluationDomain::new(info.num_non_zero_b).ok_or(SynthesisError::PolyTooLarge)?;
let domain_c = EvaluationDomain::new(info.num_non_zero_c).ok_or(SynthesisError::PolyTooLarge)?;
let new_candidate = [domain_a, domain_b, domain_c]
.into_iter()
.max_by_key(|d| d.size())
.ok_or(anyhow!("could not find max domain"))?;
let mut max_non_zero_domain = Some(new_candidate);
if max_candidate.is_some() && max_candidate.unwrap().size() > new_candidate.size() {
max_non_zero_domain = max_candidate;
if let Some(max_candidate) = max_candidate {
if max_candidate.size() > new_candidate.size() {
max_non_zero_domain = Some(max_candidate);
}
}
Ok(NonZeroDomains { max_non_zero_domain, domain_a, domain_b, domain_c })
}
Expand Down Expand Up @@ -167,8 +173,8 @@ impl<F: PrimeField, SM: SNARKMode> AHPForR1CS<F, SM> {
prover_third_message: &prover::ThirdMessage<F>,
prover_fourth_message: &prover::FourthMessage<F>,
state: &verifier::State<F, SM>,
) -> Result<BTreeMap<String, LinearCombination<F>>, AHPError> {
assert!(!public_inputs.is_empty());
) -> Result<BTreeMap<String, LinearCombination<F>>> {
ensure!(!public_inputs.is_empty());
let max_constraint_domain = state.max_constraint_domain;
let max_variable_domain = state.max_variable_domain;
let max_non_zero_domain = state.max_non_zero_domain;
Expand All @@ -181,11 +187,12 @@ impl<F: PrimeField, SM: SNARKMode> AHPForR1CS<F, SM> {
.iter()
.map(|p| {
let public_input = prover::ConstraintSystem::format_public_input(p);
Self::formatted_public_input_is_admissible(&public_input).map(|_| public_input)
Self::formatted_public_input_is_admissible(&public_input)?;
Ok::<_, AHPError>(public_input)
})
.collect::<Result<Vec<_>, _>>();
assert_eq!(public_inputs.as_ref().unwrap()[0].len(), input_domain.size());
public_inputs
.collect::<Result<Vec<_>, _>>()?;
ensure!(public_inputs[0].len() == input_domain.size());
Ok(public_inputs)
})
.collect::<Result<Vec<_>, _>>()?;

Expand Down Expand Up @@ -361,7 +368,7 @@ impl<F: PrimeField, SM: SNARKMode> AHPForR1CS<F, SM> {
let g_m = LinearCombination::new(g_m_label.clone(), [(F::one(), g_m_label)]);
let g_m_at_gamma = evals.get_lc_eval(&g_m, gamma)?;

let (a_poly, b_poly) = Self::construct_matrix_linear_combinations(evals, id, m, v_rc, challenges, rc);
let (a_poly, b_poly) = Self::construct_matrix_linear_combinations(evals, id, m, v_rc, challenges, rc)?;
let g_m_term = Self::construct_g_m_term(gamma, g_m_at_gamma, sum, *selector, a_poly, b_poly);

matrix_sumcheck += (delta, &g_m_term);
Expand Down Expand Up @@ -402,7 +409,7 @@ impl<F: PrimeField, SM: SNARKMode> AHPForR1CS<F, SM> {
v_rc_at_alpha_beta: F,
challenges: QueryPoints<F>,
rc_size: F,
) -> (LinearCombination<F>, LinearCombination<F>) {
) -> Result<(LinearCombination<F>, LinearCombination<F>)> {
let label_a_poly = format!("circuit_{id}_a_poly_{matrix}");
let label_b_poly = format!("circuit_{id}_b_poly_{matrix}");
let QueryPoints { alpha, beta, gamma } = challenges;
Expand All @@ -412,9 +419,9 @@ impl<F: PrimeField, SM: SNARKMode> AHPForR1CS<F, SM> {
let a_poly_eval_available = evals.get_lc_eval(&a_poly, gamma).is_ok();
let b_poly = LinearCombination::new(label_b_poly.clone(), [(F::one(), label_b_poly.clone())]);
let b_poly_eval_available = evals.get_lc_eval(&b_poly, gamma).is_ok();
assert_eq!(a_poly_eval_available, b_poly_eval_available);
ensure!(a_poly_eval_available == b_poly_eval_available);
if a_poly_eval_available && b_poly_eval_available {
return (a_poly, b_poly);
return Ok((a_poly, b_poly));
};

// When running as the verifier, we need to construct a(X) and b(X) from the indexing polynomials
Expand All @@ -431,7 +438,7 @@ impl<F: PrimeField, SM: SNARKMode> AHPForR1CS<F, SM> {
(F::one(), (label_row_col).into()),
]);
b *= rc_size;
(a, b)
Ok((a, b))
}
}

Expand All @@ -441,14 +448,14 @@ impl<F: PrimeField, SM: SNARKMode> AHPForR1CS<F, SM> {
/// when constructing linear combinations via `AHPForR1CS::construct_linear_combinations`.
pub trait EvaluationsProvider<F: PrimeField>: core::fmt::Debug {
/// Get the evaluation of linear combination `lc` at `point`.
fn get_lc_eval(&self, lc: &LinearCombination<F>, point: F) -> Result<F, AHPError>;
fn get_lc_eval(&self, lc: &LinearCombination<F>, point: F) -> Result<F>;
}

/// The `EvaluationsProvider` used by the verifier
impl<F: PrimeField> EvaluationsProvider<F> for crate::polycommit::sonic_pc::Evaluations<F> {
fn get_lc_eval(&self, lc: &LinearCombination<F>, point: F) -> Result<F, AHPError> {
fn get_lc_eval(&self, lc: &LinearCombination<F>, point: F) -> Result<F> {
let key = (lc.label.clone(), point);
self.get(&key).copied().ok_or_else(|| AHPError::MissingEval(lc.label.clone()))
self.get(&key).copied().ok_or_else(|| AHPError::MissingEval(lc.label.clone())).map_err(Into::into)
}
}

Expand All @@ -458,7 +465,7 @@ where
F: PrimeField,
T: Borrow<LabeledPolynomial<F>> + core::fmt::Debug,
{
fn get_lc_eval(&self, lc: &LinearCombination<F>, point: F) -> Result<F, AHPError> {
fn get_lc_eval(&self, lc: &LinearCombination<F>, point: F) -> Result<F> {
let mut eval = F::zero();
for (coeff, term) in lc.iter() {
let value = if let LCTerm::PolyLabel(label) = term {
Expand All @@ -468,7 +475,7 @@ where
.borrow()
.evaluate(point)
} else {
assert!(term.is_one());
ensure!(term.is_one());
F::one()
};
eval += &(*coeff * value)
Expand Down
Loading

0 comments on commit b93295c

Please sign in to comment.