mirror of
https://github.com/arnaucube/Nova.git
synced 2026-01-11 08:31:29 +01:00
Switch RO to use the one in the Group trait (#54)
* switch to RO in the Group trait * simplify compression * absorb IO as bignum for relaxedR1CS
This commit is contained in:
@@ -333,6 +333,7 @@ mod tests {
|
||||
use crate::bellperson::{shape_cs::ShapeCS, solver::SatisfyingAssignment};
|
||||
type G1 = pasta_curves::pallas::Point;
|
||||
type G2 = pasta_curves::vesta::Point;
|
||||
use crate::constants::{BN_LIMB_WIDTH, BN_N_LIMBS};
|
||||
use crate::{
|
||||
bellperson::r1cs::{NovaShape, NovaWitness},
|
||||
commitments::CommitTrait,
|
||||
@@ -361,7 +362,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_verification_circuit() {
|
||||
// We experiment with 8 limbs of 32 bits each
|
||||
let params = NIFSVerifierCircuitParams::new(32, 8);
|
||||
let params = NIFSVerifierCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS);
|
||||
// The first circuit that verifies G2
|
||||
let poseidon_constants1: NovaPoseidonConstants<<G2 as Group>::Base> =
|
||||
NovaPoseidonConstants::new();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
errors::NovaError,
|
||||
traits::{AppendToTranscriptTrait, CompressedGroup, Group},
|
||||
traits::{AbsorbInROTrait, AppendToTranscriptTrait, CompressedGroup, Group, HashFuncTrait},
|
||||
};
|
||||
use core::{
|
||||
fmt::Debug,
|
||||
@@ -8,6 +8,7 @@ use core::{
|
||||
ops::{Add, AddAssign, Mul, MulAssign},
|
||||
};
|
||||
use digest::{ExtendableOutput, Input};
|
||||
use ff::Field;
|
||||
use merlin::Transcript;
|
||||
use sha3::Shake256;
|
||||
use std::io::Read;
|
||||
@@ -86,6 +87,19 @@ impl<G: Group> AppendToTranscriptTrait for Commitment<G> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Group> AbsorbInROTrait<G> for Commitment<G> {
|
||||
fn absorb_in_ro(&self, ro: &mut G::HashFunc) {
|
||||
let (x, y, is_infinity) = self.comm.to_coordinates();
|
||||
ro.absorb(x);
|
||||
ro.absorb(y);
|
||||
ro.absorb(if is_infinity {
|
||||
G::Base::one()
|
||||
} else {
|
||||
G::Base::zero()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CompressedGroup> AppendToTranscriptTrait for CompressedCommitment<C> {
|
||||
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript) {
|
||||
transcript.append_message(label, self.comm.as_bytes());
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
pub(crate) const NUM_CHALLENGE_BITS: usize = 128;
|
||||
pub(crate) const NUM_HASH_BITS: usize = 250;
|
||||
pub(crate) const BN_LIMB_WIDTH: usize = 32;
|
||||
pub(crate) const BN_N_LIMBS: usize = 8;
|
||||
|
||||
@@ -99,6 +99,20 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// interepret scalar as base
|
||||
pub fn scalar_as_base<G: Group>(input: G::Scalar) -> G::Base {
|
||||
let input_bits = input.to_le_bits();
|
||||
let mut mult = G::Base::one();
|
||||
let mut val = G::Base::zero();
|
||||
for bit in input_bits {
|
||||
if bit {
|
||||
val += mult;
|
||||
}
|
||||
mult = mult + mult;
|
||||
}
|
||||
val
|
||||
}
|
||||
|
||||
/// Allocate bignat a constant
|
||||
pub fn alloc_bignat_constant<F: PrimeField, CS: ConstraintSystem<F>>(
|
||||
mut cs: CS,
|
||||
|
||||
62
src/lib.rs
62
src/lib.rs
@@ -16,12 +16,11 @@ pub mod traits;
|
||||
|
||||
use commitments::CompressedCommitment;
|
||||
use errors::NovaError;
|
||||
use merlin::Transcript;
|
||||
use r1cs::{
|
||||
R1CSGens, R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness,
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
use traits::{AppendToTranscriptTrait, ChallengeTrait, Group};
|
||||
use traits::{AbsorbInROTrait, Group, HashFuncConstantsTrait, HashFuncTrait};
|
||||
|
||||
/// A SNARK that holds the proof of a step of an incremental computation
|
||||
pub struct StepSNARK<G: Group> {
|
||||
@@ -30,10 +29,6 @@ pub struct StepSNARK<G: Group> {
|
||||
}
|
||||
|
||||
impl<G: Group> StepSNARK<G> {
|
||||
fn protocol_name() -> &'static [u8] {
|
||||
b"NovaStepSNARK"
|
||||
}
|
||||
|
||||
/// Takes as input a Relaxed R1CS instance-witness tuple `(U1, W1)` and
|
||||
/// an R1CS instance-witness tuple `(U2, W2)` with the same structure `shape`
|
||||
/// and defined with respect to the same `gens`, and outputs
|
||||
@@ -47,7 +42,6 @@ impl<G: Group> StepSNARK<G> {
|
||||
W1: &RelaxedR1CSWitness<G>,
|
||||
U2: &R1CSInstance<G>,
|
||||
W2: &R1CSWitness<G>,
|
||||
transcript: &mut Transcript,
|
||||
) -> Result<
|
||||
(
|
||||
StepSNARK<G>,
|
||||
@@ -55,24 +49,27 @@ impl<G: Group> StepSNARK<G> {
|
||||
),
|
||||
NovaError,
|
||||
> {
|
||||
// append the protocol name to the transcript
|
||||
transcript.append_message(b"protocol-name", StepSNARK::<G>::protocol_name());
|
||||
// initialize a new RO
|
||||
let mut ro = G::HashFunc::new(<<G as traits::Group>::HashFunc as HashFuncTrait<
|
||||
G::Base,
|
||||
G::Scalar,
|
||||
>>::Constants::new());
|
||||
|
||||
// append S to the transcript
|
||||
S.append_to_transcript(b"S", transcript);
|
||||
S.absorb_in_ro(&mut ro);
|
||||
|
||||
// append U1 and U2 to transcript
|
||||
U1.append_to_transcript(b"U1", transcript);
|
||||
U2.append_to_transcript(b"U2", transcript);
|
||||
U1.absorb_in_ro(&mut ro);
|
||||
U2.absorb_in_ro(&mut ro);
|
||||
|
||||
// compute a commitment to the cross-term
|
||||
let (T, comm_T) = S.commit_T(gens, U1, W1, U2, W2)?;
|
||||
|
||||
// append `comm_T` to the transcript and obtain a challenge
|
||||
comm_T.append_to_transcript(b"comm_T", transcript);
|
||||
comm_T.absorb_in_ro(&mut ro);
|
||||
|
||||
// compute a challenge from the transcript
|
||||
let r = G::Scalar::challenge(b"r", transcript);
|
||||
// compute a challenge from the RO
|
||||
let r = ro.get_challenge();
|
||||
|
||||
// fold the instance using `r` and `comm_T`
|
||||
let U = U1.fold(U2, &comm_T, &r)?;
|
||||
@@ -83,7 +80,7 @@ impl<G: Group> StepSNARK<G> {
|
||||
// return the folded instance and witness
|
||||
Ok((
|
||||
StepSNARK {
|
||||
comm_T,
|
||||
comm_T: comm_T.compress(),
|
||||
_p: Default::default(),
|
||||
},
|
||||
(U, W),
|
||||
@@ -100,26 +97,29 @@ impl<G: Group> StepSNARK<G> {
|
||||
S: &R1CSShape<G>,
|
||||
U1: &RelaxedR1CSInstance<G>,
|
||||
U2: &R1CSInstance<G>,
|
||||
transcript: &mut Transcript,
|
||||
) -> Result<RelaxedR1CSInstance<G>, NovaError> {
|
||||
// append the protocol name to the transcript
|
||||
transcript.append_message(b"protocol-name", StepSNARK::<G>::protocol_name());
|
||||
// initialize a new RO
|
||||
let mut ro = G::HashFunc::new(<<G as traits::Group>::HashFunc as HashFuncTrait<
|
||||
G::Base,
|
||||
G::Scalar,
|
||||
>>::Constants::new());
|
||||
|
||||
// append S to the transcript
|
||||
S.append_to_transcript(b"S", transcript);
|
||||
S.absorb_in_ro(&mut ro);
|
||||
|
||||
// append U1 and U2 to transcript
|
||||
U1.append_to_transcript(b"U1", transcript);
|
||||
U2.append_to_transcript(b"U2", transcript);
|
||||
U1.absorb_in_ro(&mut ro);
|
||||
U2.absorb_in_ro(&mut ro);
|
||||
|
||||
// append `comm_T` to the transcript and obtain a challenge
|
||||
self.comm_T.append_to_transcript(b"comm_T", transcript);
|
||||
let comm_T = self.comm_T.decompress()?;
|
||||
comm_T.absorb_in_ro(&mut ro);
|
||||
|
||||
// compute a challenge from the transcript
|
||||
let r = G::Scalar::challenge(b"r", transcript);
|
||||
// compute a challenge from the RO
|
||||
let r = ro.get_challenge();
|
||||
|
||||
// fold the instance using `r` and `comm_T`
|
||||
let U = U1.fold(U2, &self.comm_T, &r)?;
|
||||
let U = U1.fold(U2, &comm_T, &r)?;
|
||||
|
||||
// return the folded instance
|
||||
Ok(U)
|
||||
@@ -239,14 +239,12 @@ mod tests {
|
||||
let mut r_U = RelaxedR1CSInstance::default(gens, shape);
|
||||
|
||||
// produce a step SNARK with (W1, U1) as the first incoming witness-instance pair
|
||||
let mut prover_transcript = Transcript::new(b"StepSNARKExample");
|
||||
let res = StepSNARK::prove(gens, shape, &r_U, &r_W, U1, W1, &mut prover_transcript);
|
||||
let res = StepSNARK::prove(gens, shape, &r_U, &r_W, U1, W1);
|
||||
assert!(res.is_ok());
|
||||
let (step_snark, (_U, W)) = res.unwrap();
|
||||
|
||||
// verify the step SNARK with U1 as the first incoming instance
|
||||
let mut verifier_transcript = Transcript::new(b"StepSNARKExample");
|
||||
let res = step_snark.verify(shape, &r_U, U1, &mut verifier_transcript);
|
||||
let res = step_snark.verify(shape, &r_U, U1);
|
||||
assert!(res.is_ok());
|
||||
let U = res.unwrap();
|
||||
|
||||
@@ -257,12 +255,12 @@ mod tests {
|
||||
r_U = U;
|
||||
|
||||
// produce a step SNARK with (W2, U2) as the second incoming witness-instance pair
|
||||
let res = StepSNARK::prove(gens, shape, &r_U, &r_W, U2, W2, &mut prover_transcript);
|
||||
let res = StepSNARK::prove(gens, shape, &r_U, &r_W, U2, W2);
|
||||
assert!(res.is_ok());
|
||||
let (step_snark, (_U, W)) = res.unwrap();
|
||||
|
||||
// verify the step SNARK with U1 as the first incoming instance
|
||||
let res = step_snark.verify(shape, &r_U, U2, &mut verifier_transcript);
|
||||
let res = step_snark.verify(shape, &r_U, U2);
|
||||
assert!(res.is_ok());
|
||||
let U = res.unwrap();
|
||||
|
||||
|
||||
121
src/r1cs.rs
121
src/r1cs.rs
@@ -1,11 +1,13 @@
|
||||
//! This module defines R1CS related types and a folding scheme for Relaxed R1CS
|
||||
#![allow(clippy::type_complexity)]
|
||||
use super::{
|
||||
commitments::{CommitGens, CommitTrait, Commitment, CompressedCommitment},
|
||||
constants::NUM_HASH_BITS,
|
||||
commitments::{CommitGens, CommitTrait, Commitment},
|
||||
constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_HASH_BITS},
|
||||
errors::NovaError,
|
||||
traits::{AppendToTranscriptTrait, Group},
|
||||
gadgets::utils::scalar_as_base,
|
||||
traits::{AbsorbInROTrait, AppendToTranscriptTrait, Group, HashFuncTrait},
|
||||
};
|
||||
use bellperson_nonnative::{mp::bignat::nat_to_limbs, util::convert::f_to_nat};
|
||||
use ff::{Field, PrimeField};
|
||||
use flate2::{write::ZlibEncoder, Compression};
|
||||
use itertools::concat;
|
||||
@@ -255,13 +257,7 @@ impl<G: Group> R1CSShape<G> {
|
||||
W1: &RelaxedR1CSWitness<G>,
|
||||
U2: &R1CSInstance<G>,
|
||||
W2: &R1CSWitness<G>,
|
||||
) -> Result<
|
||||
(
|
||||
Vec<G::Scalar>,
|
||||
CompressedCommitment<G::CompressedGroupElement>,
|
||||
),
|
||||
NovaError,
|
||||
> {
|
||||
) -> Result<(Vec<G::Scalar>, Commitment<G>), NovaError> {
|
||||
let (AZ_1, BZ_1, CZ_1) = {
|
||||
let Z1 = concat(vec![W1.W.clone(), vec![U1.u], U1.X.clone()]);
|
||||
self.multiply_vec(&Z1)?
|
||||
@@ -291,24 +287,13 @@ impl<G: Group> R1CSShape<G> {
|
||||
.map(|(((a, b), c), d)| *a + *b - *c - *d)
|
||||
.collect::<Vec<G::Scalar>>();
|
||||
|
||||
let comm_T = T.commit(&gens.gens_E).compress();
|
||||
let comm_T = T.commit(&gens.gens_E);
|
||||
|
||||
Ok((T, comm_T))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct R1CSShapeSerialized {
|
||||
num_cons: usize,
|
||||
num_vars: usize,
|
||||
num_io: usize,
|
||||
A: Vec<(usize, usize, Vec<u8>)>,
|
||||
B: Vec<(usize, usize, Vec<u8>)>,
|
||||
C: Vec<(usize, usize, Vec<u8>)>,
|
||||
}
|
||||
|
||||
impl<G: Group> AppendToTranscriptTrait for R1CSShape<G> {
|
||||
fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) {
|
||||
/// returns the digest of R1CSShape
|
||||
fn get_digest(&self) -> G::Scalar {
|
||||
let shape_serialized = R1CSShapeSerialized {
|
||||
num_cons: self.num_cons,
|
||||
num_vars: self.num_vars,
|
||||
@@ -340,27 +325,47 @@ impl<G: Group> AppendToTranscriptTrait for R1CSShape<G> {
|
||||
hasher.input(&shape_bytes);
|
||||
let shape_digest = hasher.result();
|
||||
|
||||
let shape_digest_trun = {
|
||||
// truncate the digest to 250 bits
|
||||
let bv = (0..NUM_HASH_BITS).map(|i| {
|
||||
let (byte_pos, bit_pos) = (i / 8, i % 8);
|
||||
let bit = (shape_digest[byte_pos] >> bit_pos) & 1;
|
||||
bit == 1
|
||||
});
|
||||
// truncate the digest to 250 bits
|
||||
let bv = (0..NUM_HASH_BITS).map(|i| {
|
||||
let (byte_pos, bit_pos) = (i / 8, i % 8);
|
||||
let bit = (shape_digest[byte_pos] >> bit_pos) & 1;
|
||||
bit == 1
|
||||
});
|
||||
|
||||
// turn the bit vector into a scalar
|
||||
let mut res = G::Scalar::zero();
|
||||
let mut coeff = G::Scalar::one();
|
||||
for bit in bv {
|
||||
if bit {
|
||||
res += coeff;
|
||||
}
|
||||
coeff += coeff;
|
||||
// turn the bit vector into a scalar
|
||||
let mut res = G::Scalar::zero();
|
||||
let mut coeff = G::Scalar::one();
|
||||
for bit in bv {
|
||||
if bit {
|
||||
res += coeff;
|
||||
}
|
||||
res
|
||||
};
|
||||
coeff += coeff;
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
shape_digest_trun.append_to_transcript(b"R1CSShape", transcript);
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct R1CSShapeSerialized {
|
||||
num_cons: usize,
|
||||
num_vars: usize,
|
||||
num_io: usize,
|
||||
A: Vec<(usize, usize, Vec<u8>)>,
|
||||
B: Vec<(usize, usize, Vec<u8>)>,
|
||||
C: Vec<(usize, usize, Vec<u8>)>,
|
||||
}
|
||||
|
||||
impl<G: Group> AppendToTranscriptTrait for R1CSShape<G> {
|
||||
fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) {
|
||||
self
|
||||
.get_digest()
|
||||
.append_to_transcript(b"R1CSShape", transcript);
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Group> AbsorbInROTrait<G> for R1CSShape<G> {
|
||||
fn absorb_in_ro(&self, ro: &mut G::HashFunc) {
|
||||
ro.absorb(scalar_as_base::<G>(self.get_digest()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,6 +410,15 @@ impl<G: Group> AppendToTranscriptTrait for R1CSInstance<G> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Group> AbsorbInROTrait<G> for R1CSInstance<G> {
|
||||
fn absorb_in_ro(&self, ro: &mut G::HashFunc) {
|
||||
self.comm_W.absorb_in_ro(ro);
|
||||
for x in &self.X {
|
||||
ro.absorb(scalar_as_base::<G>(*x));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Group> RelaxedR1CSWitness<G> {
|
||||
/// Produces a default RelaxedR1CSWitness given an R1CSShape
|
||||
pub fn default(S: &R1CSShape<G>) -> RelaxedR1CSWitness<G> {
|
||||
@@ -465,10 +479,9 @@ impl<G: Group> RelaxedR1CSInstance<G> {
|
||||
pub fn fold(
|
||||
&self,
|
||||
U2: &R1CSInstance<G>,
|
||||
comm_T: &CompressedCommitment<G::CompressedGroupElement>,
|
||||
comm_T: &Commitment<G>,
|
||||
r: &G::Scalar,
|
||||
) -> Result<RelaxedR1CSInstance<G>, NovaError> {
|
||||
let comm_T_unwrapped = comm_T.decompress()?;
|
||||
let (X1, u1, comm_W_1, comm_E_1) =
|
||||
(&self.X, &self.u, &self.comm_W.clone(), &self.comm_E.clone());
|
||||
let (X2, comm_W_2) = (&U2.X, &U2.comm_W);
|
||||
@@ -493,7 +506,7 @@ impl<G: Group> RelaxedR1CSInstance<G> {
|
||||
.map(|(a, b)| *a + *r * *b)
|
||||
.collect::<Vec<G::Scalar>>();
|
||||
let comm_W = comm_W_1 + comm_W_2 * r;
|
||||
let comm_E = *comm_E_1 + comm_T_unwrapped * *r;
|
||||
let comm_E = *comm_E_1 + *comm_T * *r;
|
||||
let u = *u1 + *r;
|
||||
|
||||
Ok(RelaxedR1CSInstance {
|
||||
@@ -511,7 +524,23 @@ impl<G: Group> AppendToTranscriptTrait for RelaxedR1CSInstance<G> {
|
||||
fn append_to_transcript(&self, _label: &'static [u8], transcript: &mut Transcript) {
|
||||
self.comm_W.append_to_transcript(b"comm_W", transcript);
|
||||
self.comm_E.append_to_transcript(b"comm_E", transcript);
|
||||
self.X.append_to_transcript(b"X", transcript);
|
||||
self.u.append_to_transcript(b"u", transcript);
|
||||
self.X.append_to_transcript(b"X", transcript);
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Group> AbsorbInROTrait<G> for RelaxedR1CSInstance<G> {
|
||||
fn absorb_in_ro(&self, ro: &mut G::HashFunc) {
|
||||
self.comm_W.absorb_in_ro(ro);
|
||||
self.comm_E.absorb_in_ro(ro);
|
||||
ro.absorb(scalar_as_base::<G>(self.u));
|
||||
|
||||
// absorb each element of self.X in bignum format
|
||||
for x in &self.X {
|
||||
let limbs: Vec<G::Scalar> = nat_to_limbs(&f_to_nat(x), BN_LIMB_WIDTH, BN_N_LIMBS).unwrap();
|
||||
for limb in limbs {
|
||||
ro.absorb(scalar_as_base::<G>(limb));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,12 @@ pub trait AppendToTranscriptTrait {
|
||||
fn append_to_transcript(&self, label: &'static [u8], transcript: &mut Transcript);
|
||||
}
|
||||
|
||||
/// A helper trait to absorb different objects in RO
|
||||
pub trait AbsorbInROTrait<G: Group> {
|
||||
/// Absorbs the value in the provided RO
|
||||
fn absorb_in_ro(&self, ro: &mut G::HashFunc);
|
||||
}
|
||||
|
||||
/// A helper trait to generate challenges using a transcript object
|
||||
pub trait ChallengeTrait {
|
||||
/// Returns a Scalar representing the challenge using the transcript
|
||||
|
||||
Reference in New Issue
Block a user