Skip to content

Commit

Permalink
support for secp/secq in spartan2, ported from Nova
Browse files Browse the repository at this point in the history
  • Loading branch information
srinathsetty committed Sep 29, 2023
1 parent 880541e commit 3e85963
Show file tree
Hide file tree
Showing 16 changed files with 498 additions and 671 deletions.
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ flate2 = "1.0"
bitvec = "1.0"
byteorder = "1.4.3"
thiserror = "1.0"
halo2curves = { version = "0.1.0", features = ["derive_serde"] }
halo2curves = { version = "0.4.0", features = ["derive_serde"] }
group = "0.13.0"
once_cell = "1.18.0"

[target.'cfg(any(target_arch = "x86_64", target_arch = "aarch64"))'.dependencies]
pasta-msm = { version = "0.1.4" }
Expand Down
42 changes: 11 additions & 31 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

// private modules
mod bellpepper;
mod constants;
mod digest;
mod r1cs;

// public modules
Expand All @@ -29,10 +31,8 @@ use crate::bellpepper::{
use bellpepper_core::{Circuit, ConstraintSystem};
use core::marker::PhantomData;
use errors::SpartanError;
use ff::Field;
use r1cs::{R1CSShape, RelaxedR1CSInstance, RelaxedR1CSWitness};
use serde::{Deserialize, Serialize};
use sha3::{Digest, Sha3_256};
use traits::{
commitment::{CommitmentEngineTrait, CommitmentTrait},
snark::RelaxedR1CSSNARKTrait,
Expand Down Expand Up @@ -111,7 +111,7 @@ impl<G: Group, S: RelaxedR1CSSNARKTrait<G>, C: Circuit<G::Scalar>> SNARK<G, S, C
);

// prove the instance using Spartan
let snark = S::prove(&pk.ck, &pk.pk, &pk.S, &u_relaxed, &w_relaxed)?;
let snark = S::prove(&pk.ck, &pk.pk, &u_relaxed, &w_relaxed)?;

Ok(SNARK {
comm_W: u.comm_W,
Expand All @@ -138,37 +138,10 @@ type Commitment<G> = <<G as Group>::CE as CommitmentEngineTrait<G>>::Commitment;
type CompressedCommitment<G> = <<<G as Group>::CE as CommitmentEngineTrait<G>>::Commitment as CommitmentTrait<G>>::CompressedCommitment;
type CE<G> = <G as Group>::CE;

fn compute_digest<G: Group, T: Serialize>(o: &T) -> G::Scalar {
// obtain a vector of bytes representing public parameters
let bytes = bincode::serialize(o).unwrap();
// convert pp_bytes into a short digest
let mut hasher = Sha3_256::new();
hasher.update(&bytes);
let digest = hasher.finalize();

// truncate the digest to NUM_HASH_BITS bits
let bv = (0..250).map(|i| {
let (byte_pos, bit_pos) = (i / 8, i % 8);
let bit = (digest[byte_pos] >> bit_pos) & 1;
bit == 1
});

// turn the bit vector into a scalar
let mut digest = G::Scalar::ZERO;
let mut coeff = G::Scalar::ONE;
for bit in bv {
if bit {
digest += coeff;
}
coeff += coeff;
}
digest
}

#[cfg(test)]
mod tests {
use super::*;
use crate::provider::bn256_grumpkin::bn256;
use crate::provider::{bn256_grumpkin::bn256, secp_secq::secp256k1};
use bellpepper_core::{num::AllocatedNum, ConstraintSystem, SynthesisError};
use core::marker::PhantomData;
use ff::PrimeField;
Expand Down Expand Up @@ -227,6 +200,13 @@ mod tests {
type S2pp = crate::spartan::ppsnark::RelaxedR1CSSNARK<G2, EE2>;
test_snark_with::<G2, S2>();
test_snark_with::<G2, S2pp>();

type G3 = secp256k1::Point;
type EE3 = crate::provider::ipa_pc::EvaluationEngine<G3>;
type S3 = crate::spartan::snark::RelaxedR1CSSNARK<G3, EE3>;
type S3pp = crate::spartan::ppsnark::RelaxedR1CSSNARK<G3, EE3>;
test_snark_with::<G3, S3>();
test_snark_with::<G3, S3pp>();
}

fn test_snark_with<G: Group, S: RelaxedR1CSSNARKTrait<G>>() {
Expand Down
148 changes: 5 additions & 143 deletions src/provider/bn256_grumpkin.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
//! This module implements the Spartan traits for bn256::Point, bn256::Scalar, grumpkin::Point, grumpkin::Scalar.
//! This module implements the Spartan traits for `bn256::Point`, `bn256::Scalar`, `grumpkin::Point`, `grumpkin::Scalar`.
use crate::{
impl_traits,
provider::{cpu_best_multiexp, keccak::Keccak256Transcript, pedersen::CommitmentEngine},
traits::{CompressedGroup, Group, PrimeFieldExt, TranscriptReprTrait},
};
use digest::{ExtendableOutput, Update};
use ff::{FromUniformBytes, PrimeField};
use group::{cofactor::CofactorCurveAffine, Curve, Group as AnotherGroup, GroupEncoding};
use num_bigint::BigInt;
use num_traits::Num;
use pasta_curves::{
self,
arithmetic::{CurveAffine, CurveExt},
group::{cofactor::CofactorCurveAffine, Curve, Group as AnotherGroup, GroupEncoding},
};
// Remove this when https://github.com/zcash/pasta_curves/issues/41 resolves
use pasta_curves::arithmetic::{CurveAffine, CurveExt};
use rayon::prelude::*;
use sha3::Shake256;
use std::io::Read;
Expand All @@ -37,143 +36,6 @@ pub mod grumpkin {
};
}

// This implementation behaves in ways specific to the bn256/grumpkin curves in:
// - to_coordinates,
// - vartime_multiscalar_mul, where it does not call into accelerated implementations.
macro_rules! impl_traits {
(
$name:ident,
$name_compressed:ident,
$name_curve:ident,
$name_curve_affine:ident,
$order_str:literal
) => {
impl Group for $name::Point {
type Base = $name::Base;
type Scalar = $name::Scalar;
type CompressedGroupElement = $name_compressed;
type PreprocessedGroupElement = $name::Affine;
type TE = Keccak256Transcript<Self>;
type CE = CommitmentEngine<Self>;

fn vartime_multiscalar_mul(
scalars: &[Self::Scalar],
bases: &[Self::PreprocessedGroupElement],
) -> Self {
cpu_best_multiexp(scalars, bases)
}

fn preprocessed(&self) -> Self::PreprocessedGroupElement {
self.to_affine()
}

fn compress(&self) -> Self::CompressedGroupElement {
self.to_bytes()
}

fn from_label(label: &'static [u8], n: usize) -> Vec<Self::PreprocessedGroupElement> {
let mut shake = Shake256::default();
shake.update(label);
let mut reader = shake.finalize_xof();
let mut uniform_bytes_vec = Vec::new();
for _ in 0..n {
let mut uniform_bytes = [0u8; 32];
reader.read_exact(&mut uniform_bytes).unwrap();
uniform_bytes_vec.push(uniform_bytes);
}
let gens_proj: Vec<$name_curve> = (0..n)
.collect::<Vec<usize>>()
.into_par_iter()
.map(|i| {
let hash = $name_curve::hash_to_curve("from_uniform_bytes");
hash(&uniform_bytes_vec[i])
})
.collect();

let num_threads = rayon::current_num_threads();
if gens_proj.len() > num_threads {
let chunk = (gens_proj.len() as f64 / num_threads as f64).ceil() as usize;
(0..num_threads)
.collect::<Vec<usize>>()
.into_par_iter()
.map(|i| {
let start = i * chunk;
let end = if i == num_threads - 1 {
gens_proj.len()
} else {
core::cmp::min((i + 1) * chunk, gens_proj.len())
};
if end > start {
let mut gens = vec![$name_curve_affine::identity(); end - start];
<Self as Curve>::batch_normalize(&gens_proj[start..end], &mut gens);
gens
} else {
vec![]
}
})
.collect::<Vec<Vec<$name_curve_affine>>>()
.into_par_iter()
.flatten()
.collect()
} else {
let mut gens = vec![$name_curve_affine::identity(); n];
<Self as Curve>::batch_normalize(&gens_proj, &mut gens);
gens
}
}

fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
let coordinates = self.to_affine().coordinates();
if coordinates.is_some().unwrap_u8() == 1
// The bn256/grumpkin convention is to define and return the identity point's affine encoding (not None)
&& (Self::PreprocessedGroupElement::identity() != self.to_affine())
{
(*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
} else {
(Self::Base::zero(), Self::Base::zero(), true)
}
}

fn get_curve_params() -> (Self::Base, Self::Base, BigInt) {
let A = $name::Point::a();
let B = $name::Point::b();
let order = BigInt::from_str_radix($order_str, 16).unwrap();

(A, B, order)
}

fn zero() -> Self {
$name::Point::identity()
}

fn get_generator() -> Self {
$name::Point::generator()
}
}

impl PrimeFieldExt for $name::Scalar {
fn from_uniform(bytes: &[u8]) -> Self {
let bytes_arr: [u8; 64] = bytes.try_into().unwrap();
$name::Scalar::from_uniform_bytes(&bytes_arr)
}
}

impl<G: Group> TranscriptReprTrait<G> for $name_compressed {
fn to_transcript_bytes(&self) -> Vec<u8> {
self.as_ref().to_vec()
}
}

impl CompressedGroup for $name_compressed {
type GroupElement = $name::Point;

fn decompress(&self) -> Option<$name::Point> {
Some($name_curve::from_bytes(&self).unwrap())
}
}
};
}

impl<G: Group> TranscriptReprTrait<G> for grumpkin::Base {
fn to_transcript_bytes(&self) -> Vec<u8> {
self.to_repr().to_vec()
Expand Down
41 changes: 14 additions & 27 deletions src/provider/ipa_pc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use crate::{
errors::SpartanError,
provider::pedersen::CommitmentKeyExtTrait,
spartan::polynomial::EqPolynomial,
spartan::polys::eq::EqPolynomial,
traits::{
commitment::{CommitmentEngineTrait, CommitmentTrait},
evaluation::EvaluationEngineTrait,
Expand Down Expand Up @@ -32,13 +32,6 @@ pub struct VerifierKey<G: Group> {
ck_s: CommitmentKey<G>,
}

/// Provides an implementation of a polynomial evaluation argument
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct EvaluationArgument<G: Group> {
ipa: InnerProductArgument<G>,
}

/// Provides an implementation of a polynomial evaluation engine using IPA
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct EvaluationEngine<G: Group> {
Expand All @@ -48,23 +41,21 @@ pub struct EvaluationEngine<G: Group> {
impl<G> EvaluationEngineTrait<G> for EvaluationEngine<G>
where
G: Group,
CommitmentKey<G>: CommitmentKeyExtTrait<G, CE = G::CE>,
CommitmentKey<G>: CommitmentKeyExtTrait<G>,
{
type CE = G::CE;
type ProverKey = ProverKey<G>;
type VerifierKey = VerifierKey<G>;
type EvaluationArgument = EvaluationArgument<G>;
type EvaluationArgument = InnerProductArgument<G>;

fn setup(
ck: &<Self::CE as CommitmentEngineTrait<G>>::CommitmentKey,
ck: &<<G as Group>::CE as CommitmentEngineTrait<G>>::CommitmentKey,
) -> (Self::ProverKey, Self::VerifierKey) {
let pk = ProverKey {
ck_s: G::CE::setup(b"ipa", 1),
};
let ck_c = G::CE::setup(b"ipa", 1);

let pk = ProverKey { ck_s: ck_c.clone() };
let vk = VerifierKey {
ck_v: ck.clone(),
ck_s: G::CE::setup(b"ipa", 1),
ck_s: ck_c,
};

(pk, vk)
Expand All @@ -82,9 +73,7 @@ where
let u = InnerProductInstance::new(comm, &EqPolynomial::new(point.to_vec()).evals(), eval);
let w = InnerProductWitness::new(poly);

Ok(EvaluationArgument {
ipa: InnerProductArgument::prove(ck, &pk.ck_s, &u, &w, transcript)?,
})
InnerProductArgument::prove(ck, &pk.ck_s, &u, &w, transcript)
}

/// A method to verify purported evaluations of a batch of polynomials
Expand All @@ -98,7 +87,7 @@ where
) -> Result<(), SpartanError> {
let u = InnerProductInstance::new(comm, &EqPolynomial::new(point.to_vec()).evals(), eval);

arg.ipa.verify(
arg.verify(
&vk.ck_v,
&vk.ck_s,
(2_usize).pow(point.len() as u32),
Expand Down Expand Up @@ -165,19 +154,18 @@ impl<G: Group> InnerProductWitness<G> {
/// An inner product argument
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(bound = "")]
struct InnerProductArgument<G: Group> {
pub struct InnerProductArgument<G: Group> {
L_vec: Vec<CompressedCommitment<G>>,
R_vec: Vec<CompressedCommitment<G>>,
a_hat: G::Scalar,
_p: PhantomData<G>,
}

impl<G> InnerProductArgument<G>
where
G: Group,
CommitmentKey<G>: CommitmentKeyExtTrait<G, CE = G::CE>,
CommitmentKey<G>: CommitmentKeyExtTrait<G>,
{
fn protocol_name() -> &'static [u8] {
const fn protocol_name() -> &'static [u8] {
b"IPA"
}

Expand Down Expand Up @@ -275,7 +263,7 @@ where
let mut a_vec = W.a_vec.to_vec();
let mut b_vec = U.b_vec.to_vec();
let mut ck = ck;
for _i in 0..(U.b_vec.len() as f64).log2() as usize {
for _i in 0..usize::try_from(U.b_vec.len().ilog2()).unwrap() {
let (L, R, a_vec_folded, b_vec_folded, ck_folded) =
prove_inner(&a_vec, &b_vec, &ck, transcript)?;
L_vec.push(L);
Expand All @@ -290,7 +278,6 @@ where
L_vec,
R_vec,
a_hat: a_vec[0],
_p: Default::default(),
})
}

Expand Down Expand Up @@ -374,7 +361,7 @@ where
let mut s = vec![G::Scalar::ZERO; n];
s[0] = {
let mut v = G::Scalar::ONE;
for r_inverse_i in &r_inverse {
for r_inverse_i in r_inverse {
v *= r_inverse_i;
}
v
Expand Down
Loading

0 comments on commit 3e85963

Please sign in to comment.