Skip to content

Commit 1856d4a

Browse files
authored
Merge pull request #2151 from AleoHQ/correct_divide_by_zero
[NCC FYM] Correctly panic when dividing by zero poly
2 parents ce4103f + 0ae3014 commit 1856d4a

File tree

3 files changed

+37
-29
lines changed

3 files changed

+37
-29
lines changed

algorithms/src/fft/polynomial/dense.rs

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414

1515
//! A polynomial represented in coefficient form.
1616
17+
use super::PolyMultiplier;
1718
use crate::fft::{EvaluationDomain, Evaluations, Polynomial};
1819
use snarkvm_fields::{Field, PrimeField};
1920
use snarkvm_utilities::{cfg_iter_mut, serialize::*};
2021

22+
use anyhow::Result;
2123
use num_traits::CheckedDiv;
2224
use rand::Rng;
2325
use std::{
@@ -31,8 +33,6 @@ use itertools::Itertools;
3133
#[cfg(not(feature = "serial"))]
3234
use rayon::prelude::*;
3335

34-
use super::PolyMultiplier;
35-
3636
/// Stores a polynomial in coefficient form.
3737
#[derive(Clone, PartialEq, Eq, Hash, Default, CanonicalSerialize, CanonicalDeserialize)]
3838
#[must_use]
@@ -156,7 +156,7 @@ impl<F: PrimeField> DensePolynomial<F> {
156156
pub fn divide_by_vanishing_poly(
157157
&self,
158158
domain: EvaluationDomain<F>,
159-
) -> Option<(DensePolynomial<F>, DensePolynomial<F>)> {
159+
) -> Result<(DensePolynomial<F>, DensePolynomial<F>)> {
160160
let self_poly = Polynomial::from(self);
161161
let vanishing_poly = Polynomial::from(domain.vanishing_polynomial());
162162
self_poly.divide_with_q_and_r(&vanishing_poly)
@@ -421,14 +421,14 @@ impl<F: Field> CheckedDiv for DensePolynomial<F> {
421421
let a: Polynomial<_> = self.into();
422422
let b: Polynomial<_> = divisor.into();
423423
match a.divide_with_q_and_r(&b) {
424-
Some((divisor, remainder)) => {
424+
Ok((divisor, remainder)) => {
425425
if remainder.is_zero() {
426426
Some(divisor)
427427
} else {
428428
None
429429
}
430430
}
431-
None => None,
431+
Err(_) => None,
432432
}
433433
}
434434
}
@@ -592,11 +592,9 @@ mod tests {
592592
for b_degree in 0..70 {
593593
let dividend = DensePolynomial::<Fr>::rand(a_degree, rng);
594594
let divisor = DensePolynomial::<Fr>::rand(b_degree, rng);
595-
if let Some((quotient, remainder)) =
596-
Polynomial::divide_with_q_and_r(&(&dividend).into(), &(&divisor).into())
597-
{
598-
assert_eq!(dividend, &(&divisor * &quotient) + &remainder)
599-
}
595+
let (quotient, remainder) =
596+
Polynomial::divide_with_q_and_r(&(&dividend).into(), &(&divisor).into()).unwrap();
597+
assert_eq!(dividend, &(&divisor * &quotient) + &remainder)
600598
}
601599
}
602600
}
@@ -615,6 +613,13 @@ mod tests {
615613
}
616614
}
617615

616+
#[test]
617+
fn divide_poly_by_zero() {
618+
let a = Polynomial::<Fr>::zero();
619+
let b = Polynomial::<Fr>::zero();
620+
assert!(a.divide_with_q_and_r(&b).is_err());
621+
}
622+
618623
#[test]
619624
fn mul_polynomials_random() {
620625
let rng = &mut TestRng::default();

algorithms/src/fft/polynomial/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
use crate::fft::{EvaluationDomain, Evaluations};
1818
use snarkvm_fields::{Field, PrimeField};
1919
use snarkvm_utilities::{cfg_iter_mut, serialize::*, SerializationError};
20+
use Polynomial::*;
2021

22+
use anyhow::{ensure, Result};
2123
use std::{borrow::Cow, convert::TryInto};
2224

23-
use Polynomial::*;
24-
2525
#[cfg(not(feature = "serial"))]
2626
use rayon::prelude::*;
2727

@@ -218,13 +218,13 @@ impl<'a, F: Field> Polynomial<'a, F> {
218218
}
219219

220220
/// Divide self by another (sparse or dense) polynomial, and returns the quotient and remainder.
221-
pub fn divide_with_q_and_r(&self, divisor: &Self) -> Option<(DensePolynomial<F>, DensePolynomial<F>)> {
221+
pub fn divide_with_q_and_r(&self, divisor: &Self) -> Result<(DensePolynomial<F>, DensePolynomial<F>)> {
222+
ensure!(!divisor.is_zero(), "Dividing by zero polynomial is undefined");
223+
222224
if self.is_zero() {
223-
Some((DensePolynomial::zero(), DensePolynomial::zero()))
224-
} else if divisor.is_zero() {
225-
panic!("Dividing by zero polynomial")
225+
Ok((DensePolynomial::zero(), DensePolynomial::zero()))
226226
} else if self.degree() < divisor.degree() {
227-
Some((DensePolynomial::zero(), self.clone().into()))
227+
Ok((DensePolynomial::zero(), self.clone().into()))
228228
} else {
229229
// Now we know that self.degree() >= divisor.degree();
230230
let mut quotient = vec![F::zero(); self.degree() - divisor.degree() + 1];
@@ -250,7 +250,7 @@ impl<'a, F: Field> Polynomial<'a, F> {
250250
remainder.coeffs.pop();
251251
}
252252
}
253-
Some((DensePolynomial::from_coefficients_vec(quotient), remainder))
253+
Ok((DensePolynomial::from_coefficients_vec(quotient), remainder))
254254
}
255255
}
256256
}

algorithms/src/snark/varuna/ahp/selectors.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,18 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
use super::verifier::QueryPoints;
1516
use crate::fft::{DensePolynomial, EvaluationDomain};
16-
use anyhow::{anyhow, ensure, Result};
1717
use snarkvm_fields::{batch_inversion, PrimeField};
1818
use snarkvm_utilities::{cfg_into_iter, cfg_iter_mut, serialize::*};
1919

20+
use anyhow::{ensure, Result};
2021
use itertools::Itertools;
2122
use std::collections::{BTreeMap, HashSet};
2223

2324
#[cfg(not(feature = "serial"))]
2425
use rayon::prelude::*;
2526

26-
use super::verifier::QueryPoints;
27-
2827
/// Precompute a batch of selectors at challenges. We batch:
2928
/// - constraint domain selectors at alpha
3029
/// - variable domain selectors at beta
@@ -88,11 +87,13 @@ pub(crate) fn apply_randomized_selector<F: PrimeField>(
8887
if !remainder_witness {
8988
// Substituting in s_i, we get that poly_i * s_i / v_H = poly_i / v_H * (H_i.size() / H.size());
9089
let selector_time = start_timer!(|| "Compute selector without remainder witness");
91-
let (mut h_i, remainder) =
92-
poly.divide_by_vanishing_poly(*src_domain).ok_or(anyhow::anyhow!("could not divide by vanishing poly"))?;
93-
ensure!(remainder.is_zero());
90+
91+
let (mut h_i, remainder) = poly.divide_by_vanishing_poly(*src_domain)?;
92+
ensure!(remainder.is_zero(), "Failed to divide by vanishing polynomial - non-zero remainder ({remainder:?})");
93+
9494
let multiplier = combiner * src_domain.size_as_field_element * target_domain.size_inv;
9595
cfg_iter_mut!(h_i.coeffs).for_each(|c| *c *= multiplier);
96+
9697
end_timer!(selector_time);
9798
Ok((h_i, None))
9899
} else {
@@ -105,14 +106,16 @@ pub(crate) fn apply_randomized_selector<F: PrimeField>(
105106
// (\sum_i{c_i*s_i*poly_i})/v_H = h_1*v_H + x_g_1
106107
// That's what we're computing here.
107108
let selector_time = start_timer!(|| "Compute selector with remainder witness");
109+
108110
let multiplier = combiner * src_domain.size_as_field_element * target_domain.size_inv;
109111
cfg_iter_mut!(poly.coeffs).for_each(|c| *c *= multiplier);
110-
let (h_i, mut xg_i) =
111-
poly.divide_by_vanishing_poly(*src_domain).ok_or(anyhow!("Could not divide by vanishing poly"))?;
112+
113+
let (h_i, mut xg_i) = poly.divide_by_vanishing_poly(*src_domain)?;
112114
xg_i = xg_i.mul_by_vanishing_poly(*target_domain);
113-
let (xg_i, remainder) =
114-
xg_i.divide_by_vanishing_poly(*src_domain).ok_or(anyhow!("Could not divide by vanishing poly"))?;
115-
ensure!(remainder.is_zero());
115+
116+
let (xg_i, remainder) = xg_i.divide_by_vanishing_poly(*src_domain)?;
117+
ensure!(remainder.is_zero(), "Failed to divide by vanishing polynomial - non-zero remainder ({remainder:?})");
118+
116119
end_timer!(selector_time);
117120
Ok((h_i, Some(xg_i)))
118121
}

0 commit comments

Comments
 (0)