Browse Source

add sum-check impl, not currently used by the rest of the repo

ivc-proofs
arnaucube 1 year ago
parent
commit
946fa1ce85
2 changed files with 310 additions and 1 deletions
  1. +1
    -1
      README.md
  2. +309
    -0
      src/sumcheck.rs

+ 1
- 1
README.md

@ -6,4 +6,4 @@ Implementation of [Nova](https://eprint.iacr.org/2021/370.pdf) using [arkworks-r
This repo is an ongoing implementation, the code will be dirty for a while and not optimized but just to understand and experiment with the internals of the scheme and try experimental combinations. This repo is an ongoing implementation, the code will be dirty for a while and not optimized but just to understand and experiment with the internals of the scheme and try experimental combinations.
Thanks to [Levs57](https://twitter.com/levs57), [Nalin Bhardwaj](https://twitter.com/nibnalin) and [Carlos](https://twitter.com/cperezz19) for clarifications on the Nova paper.
Thanks to [Levs57](https://twitter.com/levs57), [Nalin Bhardwaj](https://twitter.com/nibnalin) and [Carlos Pérez](https://twitter.com/cperezz19) for clarifications on the Nova paper.

+ 309
- 0
src/sumcheck.rs

@ -0,0 +1,309 @@
// Sum-check protocol initial implementation, not used by the rest of the repo but implemented as
// an exercise and might be used in the future.
use ark_ff::{BigInteger, PrimeField};
use ark_poly::{
multivariate::{SparsePolynomial, SparseTerm, Term},
univariate::DensePolynomial,
DenseMVPolynomial, DenseUVPolynomial, EvaluationDomain, GeneralEvaluationDomain, Polynomial,
SparseMultilinearExtension,
};
use ark_std::cfg_into_iter;
use ark_std::log2;
use ark_std::marker::PhantomData;
use ark_std::ops::Mul;
use ark_std::{rand::Rng, UniformRand};
pub struct SumCheck<
F: PrimeField,
UV: Polynomial<F> + DenseUVPolynomial<F>,
MV: Polynomial<F> + DenseMVPolynomial<F>,
> {
_f: PhantomData<F>,
_uv: PhantomData<UV>,
_mv: PhantomData<MV>,
}
impl<
F: PrimeField,
UV: Polynomial<F> + DenseUVPolynomial<F>,
MV: Polynomial<F> + DenseMVPolynomial<F>,
> SumCheck<F, UV, MV>
{
fn partial_evaluate(g: &MV, point: &[Option<F>]) -> UV {
assert!(point.len() >= g.num_vars(), "Invalid evaluation domain");
// TODO: add check: there can only be 1 'None' value in point
if g.is_zero() {
return UV::from_coefficients_vec(vec![F::zero()]);
}
// note: this can be parallelized with cfg_into_iter
let mut univ_terms: Vec<(F, SparseTerm)> = vec![];
for (coef, term) in g.terms().iter() {
// partial_evaluate each term
let mut new_coef = F::one();
let mut new_term = Vec::new();
for (var, power) in term.iter() {
match point[*var] {
Some(v) => {
if v.is_zero() {
new_coef = F::zero();
new_term = vec![];
break;
} else {
new_coef = new_coef * v.pow([(*power) as u64]);
}
}
_ => {
new_term.push((*var, *power));
}
};
}
let new_term = SparseTerm::new(new_term);
let new_coef = new_coef * coef;
univ_terms.push((new_coef, new_term));
}
let mv_poly: SparsePolynomial<F, SparseTerm> =
DenseMVPolynomial::<F>::from_coefficients_vec(g.num_vars(), univ_terms.clone());
let mut univ_coeffs: Vec<F> = vec![F::zero(); mv_poly.degree() + 1];
for (coef, term) in univ_terms {
if term.is_empty() {
univ_coeffs[0] += coef;
continue;
}
for (_, power) in term.iter() {
univ_coeffs[*power] += coef;
}
}
UV::from_coefficients_vec(univ_coeffs)
}
fn point_complete(challenges: Vec<F>, n_elems: usize, iter_num: usize) -> Vec<F> {
let p = Self::point(challenges, false, n_elems, iter_num);
// let mut r = Vec::new();
let mut r = vec![F::zero(); n_elems];
for i in 0..n_elems {
// r.push(p[i].unwrap());
r[i] = p[i].unwrap();
}
r
}
fn point(challenges: Vec<F>, none: bool, n_elems: usize, iter_num: usize) -> Vec<Option<F>> {
let mut n_vars = n_elems - challenges.len();
assert!(n_vars >= log2(iter_num + 1) as usize);
if none {
// WIP
if n_vars == 0 {
panic!("err");
}
n_vars -= 1;
}
let none_pos = if none {
challenges.len() + 1
} else {
challenges.len()
};
let mut p: Vec<Option<F>> = vec![None; n_elems];
for i in 0..challenges.len() {
p[i] = Some(challenges[i]);
}
for i in 0..n_vars {
let k = F::from(iter_num as u64).into_bigint().to_bytes_le();
let bit = k[(i / 8) as usize] & (1 << (i % 8));
if bit == 0 {
p[none_pos + i] = Some(F::zero());
} else {
p[none_pos + i] = Some(F::one());
}
}
p
}
pub fn prove(g: MV) -> (F, Vec<UV>, F)
where
<MV as Polynomial<F>>::Point: From<Vec<F>>,
{
let v = g.num_vars();
let r = vec![F::from(2_u32), F::from(3_u32), F::from(6_u32)]; // TMP will come from transcript
// compute H
let mut H = F::zero();
for i in 0..(2_u64.pow(v as u32) as usize) {
let p = Self::point_complete(vec![], v, i);
H = H + g.evaluate(&p.into());
}
let mut ss: Vec<UV> = Vec::new();
for i in 0..v {
let var_slots = v - 1 - i;
let n_points = 2_u64.pow(var_slots as u32) as usize;
let mut s_i = UV::zero();
let r_round = r.as_slice()[..i].to_vec();
for j in 0..n_points {
let point = Self::point(r_round.clone(), true, v, j);
s_i = s_i + Self::partial_evaluate(&g, &point);
}
ss.push(s_i);
}
let point_last = r;
let last_g_eval = g.evaluate(&point_last.into());
(H, ss, last_g_eval)
}
pub fn verify(proof: (F, Vec<UV>, F)) -> bool {
// let c: F, ss: Vec<UV>;
let (c, ss, last_g_eval) = proof;
let r = vec![F::from(2_u32), F::from(3_u32), F::from(6_u32)]; // TMP will come from transcript
for (i, s) in ss.iter().enumerate() {
// TODO check degree
if i == 0 {
if c != s.evaluate(&F::zero()) + s.evaluate(&F::one()) {
return false;
}
continue;
}
if ss[i - 1].evaluate(&r[i - 1]) != s.evaluate(&F::zero()) + s.evaluate(&F::one()) {
return false;
}
}
// last round
if ss[ss.len() - 1].evaluate(&r[r.len() - 1]) != last_g_eval {
return false;
}
true
}
}
#[cfg(test)]
mod tests {
use super::*;
use ark_bn254::Fr; // scalar field
#[test]
fn test_new_point() {
let f4 = Fr::from(4_u32);
let f1 = Fr::from(1);
let f0 = Fr::from(0);
type SC = SumCheck<Fr, DensePolynomial<Fr>, SparsePolynomial<Fr, SparseTerm>>;
let p = SC::point(vec![Fr::from(4_u32)], true, 5, 0);
assert_eq!(vec![Some(f4), None, Some(f0), Some(f0), Some(f0),], p);
let p = SC::point(vec![Fr::from(4_u32)], true, 5, 1);
assert_eq!(vec![Some(f4), None, Some(f1), Some(f0), Some(f0),], p);
let p = SC::point(vec![Fr::from(4_u32)], true, 5, 2);
assert_eq!(vec![Some(f4), None, Some(f0), Some(f1), Some(f0),], p);
let p = SC::point(vec![Fr::from(4_u32)], true, 5, 3);
assert_eq!(vec![Some(f4), None, Some(f1), Some(f1), Some(f0),], p);
let p = SC::point(vec![Fr::from(4_u32)], true, 5, 4);
assert_eq!(vec![Some(f4), None, Some(f0), Some(f0), Some(f1),], p);
// without None
let p = SC::point(vec![], false, 4, 0);
assert_eq!(vec![Some(f0), Some(f0), Some(f0), Some(f0),], p);
let p = SC::point(vec![Fr::from(4_u32)], false, 5, 0);
assert_eq!(vec![Some(f4), Some(f0), Some(f0), Some(f0), Some(f0),], p);
let p = SC::point(vec![Fr::from(4_u32)], false, 5, 1);
assert_eq!(vec![Some(f4), Some(f1), Some(f0), Some(f0), Some(f0),], p);
let p = SC::point(vec![Fr::from(4_u32)], false, 5, 3);
assert_eq!(vec![Some(f4), Some(f1), Some(f1), Some(f0), Some(f0),], p);
let p = SC::point(vec![Fr::from(4_u32)], false, 5, 4);
assert_eq!(vec![Some(f4), Some(f0), Some(f0), Some(f1), Some(f0),], p);
let p = SC::point(vec![Fr::from(4_u32)], false, 5, 10);
assert_eq!(vec![Some(f4), Some(f0), Some(f1), Some(f0), Some(f1),], p);
let p = SC::point(vec![Fr::from(4_u32)], false, 5, 15);
assert_eq!(vec![Some(f4), Some(f1), Some(f1), Some(f1), Some(f1),], p);
// let p = SC::point(vec![Fr::from(4_u32)], false, 4, 16); // TODO expect error
}
#[test]
fn test_partial_evaluate() {
// g(X_0, X_1, X_2) = 2 X_0^3 + X_0 X_2 + X_1 X_2
let terms = vec![
(Fr::from(2u32), SparseTerm::new(vec![(0_usize, 3)])),
(
Fr::from(1u32),
SparseTerm::new(vec![(0_usize, 1), (2_usize, 1)]),
),
(
Fr::from(1u32),
SparseTerm::new(vec![(1_usize, 1), (2_usize, 1)]),
),
];
let p = SparsePolynomial::from_coefficients_slice(3, &terms);
type SC = SumCheck<Fr, DensePolynomial<Fr>, SparsePolynomial<Fr, SparseTerm>>;
let e0 = SC::partial_evaluate(&p, &[Some(Fr::from(2_u32)), None, Some(Fr::from(0_u32))]);
assert_eq!(e0.coeffs(), vec![Fr::from(16_u32)]);
let e1 = SC::partial_evaluate(&p, &[Some(Fr::from(2_u32)), None, Some(Fr::from(1_u32))]);
assert_eq!(e1.coeffs(), vec![Fr::from(18_u32), Fr::from(1)]);
assert_eq!((e0 + e1).coeffs(), vec![Fr::from(34_u32), Fr::from(1)]);
}
#[test]
fn test_flow_hardcoded_values() {
let mut rng = ark_std::test_rng();
// let p = SparsePolynomial::<Fr, SparseTerm>::rand(deg, 3, &mut rng);
// let p = rand_poly(3, 3, &mut rng);
// g(X_0, X_1, X_2) = 2 X_0^3 + X_0 X_2 + X_1 X_2
let terms = vec![
(Fr::from(2u32), SparseTerm::new(vec![(0_usize, 3)])),
(
Fr::from(1u32),
SparseTerm::new(vec![(0_usize, 1), (2_usize, 1)]),
),
(
Fr::from(1u32),
SparseTerm::new(vec![(1_usize, 1), (2_usize, 1)]),
),
];
let p = SparsePolynomial::from_coefficients_slice(3, &terms);
// println!("p {:?}", p);
type SC = SumCheck<Fr, DensePolynomial<Fr>, SparsePolynomial<Fr, SparseTerm>>;
let proof = SC::prove(p);
assert_eq!(proof.0, Fr::from(12_u32));
println!("proof {:?}", proof);
let v = SC::verify(proof);
assert!(v);
}
#[test]
fn test_flow_rng() {
let mut rng = ark_std::test_rng();
let p = SparsePolynomial::<Fr, SparseTerm>::rand(3, 3, &mut rng);
println!("p {:?}", p);
type SC = SumCheck<Fr, DensePolynomial<Fr>, SparsePolynomial<Fr, SparseTerm>>;
let proof = SC::prove(p);
println!("proof.s len {:?}", proof.1.len());
let v = SC::verify(proof);
assert!(v);
}
}

Loading…
Cancel
Save