Browse Source

hash of public parameters in the transcript (#168)

main
Srinath Setty 1 year ago
committed by GitHub
parent
commit
b28aaf70a8
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 133 additions and 142 deletions
  1. +3
    -2
      src/circuit.rs
  2. +57
    -16
      src/lib.rs
  3. +33
    -11
      src/nifs.rs
  4. +3
    -97
      src/r1cs.rs
  5. +19
    -7
      src/spartan/mod.rs
  6. +18
    -9
      src/spartan/pp.rs

+ 3
- 2
src/circuit.rs

@ -378,6 +378,7 @@ mod tests {
use crate::constants::{BN_LIMB_WIDTH, BN_N_LIMBS}; use crate::constants::{BN_LIMB_WIDTH, BN_N_LIMBS};
use crate::{ use crate::{
bellperson::r1cs::{NovaShape, NovaWitness}, bellperson::r1cs::{NovaShape, NovaWitness},
gadgets::utils::scalar_as_base,
provider::poseidon::PoseidonConstantsCircuit, provider::poseidon::PoseidonConstantsCircuit,
traits::{circuit::TrivialTestCircuit, ROConstantsTrait}, traits::{circuit::TrivialTestCircuit, ROConstantsTrait},
}; };
@ -420,7 +421,7 @@ mod tests {
let zero1 = <<G2 as Group>::Base as Field>::ZERO; let zero1 = <<G2 as Group>::Base as Field>::ZERO;
let mut cs1: SatisfyingAssignment<G1> = SatisfyingAssignment::new(); let mut cs1: SatisfyingAssignment<G1> = SatisfyingAssignment::new();
let inputs1: NovaAugmentedCircuitInputs<G2> = NovaAugmentedCircuitInputs::new( let inputs1: NovaAugmentedCircuitInputs<G2> = NovaAugmentedCircuitInputs::new(
shape2.get_digest(),
scalar_as_base::<G1>(zero1), // pass zero for testing
zero1, zero1,
vec![zero1], vec![zero1],
None, None,
@ -444,7 +445,7 @@ mod tests {
let zero2 = <<G1 as Group>::Base as Field>::ZERO; let zero2 = <<G1 as Group>::Base as Field>::ZERO;
let mut cs2: SatisfyingAssignment<G2> = SatisfyingAssignment::new(); let mut cs2: SatisfyingAssignment<G2> = SatisfyingAssignment::new();
let inputs2: NovaAugmentedCircuitInputs<G1> = NovaAugmentedCircuitInputs::new( let inputs2: NovaAugmentedCircuitInputs<G1> = NovaAugmentedCircuitInputs::new(
shape1.get_digest(),
scalar_as_base::<G2>(zero2), // pass zero for testing
zero2, zero2,
vec![zero2], vec![zero2],
None, None,

+ 57
- 16
src/lib.rs

@ -36,10 +36,12 @@ use constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_
use core::marker::PhantomData; use core::marker::PhantomData;
use errors::NovaError; use errors::NovaError;
use ff::Field; use ff::Field;
use flate2::{write::ZlibEncoder, Compression};
use gadgets::utils::scalar_as_base; use gadgets::utils::scalar_as_base;
use nifs::NIFS; use nifs::NIFS;
use r1cs::{R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness}; use r1cs::{R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha3::{Digest, Sha3_256};
use traits::{ use traits::{
circuit::StepCircuit, circuit::StepCircuit,
commitment::{CommitmentEngineTrait, CommitmentTrait}, commitment::{CommitmentEngineTrait, CommitmentTrait},
@ -69,6 +71,7 @@ where
r1cs_shape_secondary: R1CSShape<G2>, r1cs_shape_secondary: R1CSShape<G2>,
augmented_circuit_params_primary: NovaAugmentedCircuitParams, augmented_circuit_params_primary: NovaAugmentedCircuitParams,
augmented_circuit_params_secondary: NovaAugmentedCircuitParams, augmented_circuit_params_secondary: NovaAugmentedCircuitParams,
digest: G1::Scalar, // digest of everything else with this field set to G1::Scalar::ZERO
_p_c1: PhantomData<C1>, _p_c1: PhantomData<C1>,
_p_c2: PhantomData<C2>, _p_c2: PhantomData<C2>,
} }
@ -119,7 +122,7 @@ where
let _ = circuit_secondary.synthesize(&mut cs); let _ = circuit_secondary.synthesize(&mut cs);
let (r1cs_shape_secondary, ck_secondary) = cs.r1cs_shape(); let (r1cs_shape_secondary, ck_secondary) = cs.r1cs_shape();
Self {
let mut pp = Self {
F_arity_primary, F_arity_primary,
F_arity_secondary, F_arity_secondary,
ro_consts_primary, ro_consts_primary,
@ -132,9 +135,15 @@ where
r1cs_shape_secondary, r1cs_shape_secondary,
augmented_circuit_params_primary, augmented_circuit_params_primary,
augmented_circuit_params_secondary, augmented_circuit_params_secondary,
digest: G1::Scalar::ZERO,
_p_c1: Default::default(), _p_c1: Default::default(),
_p_c2: Default::default(), _p_c2: Default::default(),
}
};
// set the digest in pp
pp.digest = compute_digest::<G1, PublicParams<G1, G2, C1, C2>>(&pp);
pp
} }
/// Returns the number of constraints in the primary and secondary circuits /// Returns the number of constraints in the primary and secondary circuits
@ -205,7 +214,7 @@ where
// base case for the primary // base case for the primary
let mut cs_primary: SatisfyingAssignment<G1> = SatisfyingAssignment::new(); let mut cs_primary: SatisfyingAssignment<G1> = SatisfyingAssignment::new();
let inputs_primary: NovaAugmentedCircuitInputs<G2> = NovaAugmentedCircuitInputs::new( let inputs_primary: NovaAugmentedCircuitInputs<G2> = NovaAugmentedCircuitInputs::new(
pp.r1cs_shape_secondary.get_digest(),
scalar_as_base::<G1>(pp.digest),
G1::Scalar::ZERO, G1::Scalar::ZERO,
z0_primary.clone(), z0_primary.clone(),
None, None,
@ -228,7 +237,7 @@ where
// base case for the secondary // base case for the secondary
let mut cs_secondary: SatisfyingAssignment<G2> = SatisfyingAssignment::new(); let mut cs_secondary: SatisfyingAssignment<G2> = SatisfyingAssignment::new();
let inputs_secondary: NovaAugmentedCircuitInputs<G1> = NovaAugmentedCircuitInputs::new( let inputs_secondary: NovaAugmentedCircuitInputs<G1> = NovaAugmentedCircuitInputs::new(
pp.r1cs_shape_primary.get_digest(),
pp.digest,
G2::Scalar::ZERO, G2::Scalar::ZERO,
z0_secondary.clone(), z0_secondary.clone(),
None, None,
@ -294,6 +303,7 @@ where
let (nifs_secondary, (r_U_secondary, r_W_secondary)) = NIFS::prove( let (nifs_secondary, (r_U_secondary, r_W_secondary)) = NIFS::prove(
&pp.ck_secondary, &pp.ck_secondary,
&pp.ro_consts_secondary, &pp.ro_consts_secondary,
&scalar_as_base::<G1>(pp.digest),
&pp.r1cs_shape_secondary, &pp.r1cs_shape_secondary,
&r_snark.r_U_secondary, &r_snark.r_U_secondary,
&r_snark.r_W_secondary, &r_snark.r_W_secondary,
@ -303,7 +313,7 @@ where
let mut cs_primary: SatisfyingAssignment<G1> = SatisfyingAssignment::new(); let mut cs_primary: SatisfyingAssignment<G1> = SatisfyingAssignment::new();
let inputs_primary: NovaAugmentedCircuitInputs<G2> = NovaAugmentedCircuitInputs::new( let inputs_primary: NovaAugmentedCircuitInputs<G2> = NovaAugmentedCircuitInputs::new(
pp.r1cs_shape_secondary.get_digest(),
scalar_as_base::<G1>(pp.digest),
G1::Scalar::from(r_snark.i as u64), G1::Scalar::from(r_snark.i as u64),
z0_primary, z0_primary,
Some(r_snark.zi_primary.clone()), Some(r_snark.zi_primary.clone()),
@ -328,6 +338,7 @@ where
let (nifs_primary, (r_U_primary, r_W_primary)) = NIFS::prove( let (nifs_primary, (r_U_primary, r_W_primary)) = NIFS::prove(
&pp.ck_primary, &pp.ck_primary,
&pp.ro_consts_primary, &pp.ro_consts_primary,
&pp.digest,
&pp.r1cs_shape_primary, &pp.r1cs_shape_primary,
&r_snark.r_U_primary, &r_snark.r_U_primary,
&r_snark.r_W_primary, &r_snark.r_W_primary,
@ -337,7 +348,7 @@ where
let mut cs_secondary: SatisfyingAssignment<G2> = SatisfyingAssignment::new(); let mut cs_secondary: SatisfyingAssignment<G2> = SatisfyingAssignment::new();
let inputs_secondary: NovaAugmentedCircuitInputs<G1> = NovaAugmentedCircuitInputs::new( let inputs_secondary: NovaAugmentedCircuitInputs<G1> = NovaAugmentedCircuitInputs::new(
pp.r1cs_shape_primary.get_digest(),
pp.digest,
G2::Scalar::from(r_snark.i as u64), G2::Scalar::from(r_snark.i as u64),
z0_secondary, z0_secondary,
Some(r_snark.zi_secondary.clone()), Some(r_snark.zi_secondary.clone()),
@ -414,7 +425,7 @@ where
pp.ro_consts_secondary.clone(), pp.ro_consts_secondary.clone(),
NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_primary, NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_primary,
); );
hasher.absorb(scalar_as_base::<G2>(pp.r1cs_shape_secondary.get_digest()));
hasher.absorb(pp.digest);
hasher.absorb(G1::Scalar::from(num_steps as u64)); hasher.absorb(G1::Scalar::from(num_steps as u64));
for e in &z0_primary { for e in &z0_primary {
hasher.absorb(*e); hasher.absorb(*e);
@ -428,7 +439,7 @@ where
pp.ro_consts_primary.clone(), pp.ro_consts_primary.clone(),
NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_secondary, NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_secondary,
); );
hasher2.absorb(scalar_as_base::<G1>(pp.r1cs_shape_primary.get_digest()));
hasher2.absorb(scalar_as_base::<G1>(pp.digest));
hasher2.absorb(G2::Scalar::from(num_steps as u64)); hasher2.absorb(G2::Scalar::from(num_steps as u64));
for e in &z0_secondary { for e in &z0_secondary {
hasher2.absorb(*e); hasher2.absorb(*e);
@ -531,8 +542,7 @@ where
F_arity_secondary: usize, F_arity_secondary: usize,
ro_consts_primary: ROConstants<G1>, ro_consts_primary: ROConstants<G1>,
ro_consts_secondary: ROConstants<G2>, ro_consts_secondary: ROConstants<G2>,
r1cs_shape_primary_digest: G1::Scalar,
r1cs_shape_secondary_digest: G2::Scalar,
digest: G1::Scalar,
vk_primary: S1::VerifierKey, vk_primary: S1::VerifierKey,
vk_secondary: S2::VerifierKey, vk_secondary: S2::VerifierKey,
_p_c1: PhantomData<C1>, _p_c1: PhantomData<C1>,
@ -602,8 +612,7 @@ where
F_arity_secondary: pp.F_arity_secondary, F_arity_secondary: pp.F_arity_secondary,
ro_consts_primary: pp.ro_consts_primary.clone(), ro_consts_primary: pp.ro_consts_primary.clone(),
ro_consts_secondary: pp.ro_consts_secondary.clone(), ro_consts_secondary: pp.ro_consts_secondary.clone(),
r1cs_shape_primary_digest: pp.r1cs_shape_primary.get_digest(),
r1cs_shape_secondary_digest: pp.r1cs_shape_secondary.get_digest(),
digest: pp.digest,
vk_primary, vk_primary,
vk_secondary, vk_secondary,
_p_c1: Default::default(), _p_c1: Default::default(),
@ -625,6 +634,7 @@ where
NIFS::prove( NIFS::prove(
&pp.ck_primary, &pp.ck_primary,
&pp.ro_consts_primary, &pp.ro_consts_primary,
&pp.digest,
&pp.r1cs_shape_primary, &pp.r1cs_shape_primary,
&recursive_snark.r_U_primary, &recursive_snark.r_U_primary,
&recursive_snark.r_W_primary, &recursive_snark.r_W_primary,
@ -637,6 +647,7 @@ where
NIFS::prove( NIFS::prove(
&pp.ck_secondary, &pp.ck_secondary,
&pp.ro_consts_secondary, &pp.ro_consts_secondary,
&scalar_as_base::<G1>(pp.digest),
&pp.r1cs_shape_secondary, &pp.r1cs_shape_secondary,
&recursive_snark.r_U_secondary, &recursive_snark.r_U_secondary,
&recursive_snark.r_W_secondary, &recursive_snark.r_W_secondary,
@ -709,7 +720,7 @@ where
vk.ro_consts_secondary.clone(), vk.ro_consts_secondary.clone(),
NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * vk.F_arity_primary, NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * vk.F_arity_primary,
); );
hasher.absorb(scalar_as_base::<G2>(vk.r1cs_shape_secondary_digest));
hasher.absorb(vk.digest);
hasher.absorb(G1::Scalar::from(num_steps as u64)); hasher.absorb(G1::Scalar::from(num_steps as u64));
for e in z0_primary { for e in z0_primary {
hasher.absorb(e); hasher.absorb(e);
@ -723,7 +734,7 @@ where
vk.ro_consts_primary.clone(), vk.ro_consts_primary.clone(),
NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * vk.F_arity_secondary, NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * vk.F_arity_secondary,
); );
hasher2.absorb(scalar_as_base::<G1>(vk.r1cs_shape_primary_digest));
hasher2.absorb(scalar_as_base::<G1>(vk.digest));
hasher2.absorb(G2::Scalar::from(num_steps as u64)); hasher2.absorb(G2::Scalar::from(num_steps as u64));
for e in z0_secondary { for e in z0_secondary {
hasher2.absorb(e); hasher2.absorb(e);
@ -748,13 +759,13 @@ where
// fold the running instance and last instance to get a folded instance // fold the running instance and last instance to get a folded instance
let f_U_primary = self.nifs_primary.verify( let f_U_primary = self.nifs_primary.verify(
&vk.ro_consts_primary, &vk.ro_consts_primary,
&vk.r1cs_shape_primary_digest,
&vk.digest,
&self.r_U_primary, &self.r_U_primary,
&self.l_u_primary, &self.l_u_primary,
)?; )?;
let f_U_secondary = self.nifs_secondary.verify( let f_U_secondary = self.nifs_secondary.verify(
&vk.ro_consts_secondary, &vk.ro_consts_secondary,
&vk.r1cs_shape_secondary_digest,
&scalar_as_base::<G1>(vk.digest),
&self.r_U_secondary, &self.r_U_secondary,
&self.l_u_secondary, &self.l_u_secondary,
)?; )?;
@ -781,6 +792,36 @@ type Commitment = <::CE as CommitmentEngineTrait>::Commitment;
type CompressedCommitment<G> = <<<G as Group>::CE as CommitmentEngineTrait<G>>::Commitment as CommitmentTrait<G>>::CompressedCommitment; type CompressedCommitment<G> = <<<G as Group>::CE as CommitmentEngineTrait<G>>::Commitment as CommitmentTrait<G>>::CompressedCommitment;
type CE<G> = <G as Group>::CE; 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 mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
bincode::serialize_into(&mut encoder, o).unwrap();
let pp_bytes = encoder.finish().unwrap();
// convert pp_bytes into a short digest
let mut hasher = Sha3_256::new();
hasher.input(&pp_bytes);
let digest = hasher.result();
// truncate the digest to NUM_HASH_BITS bits
let bv = (0..NUM_HASH_BITS).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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

+ 33
- 11
src/nifs.rs

@ -32,9 +32,11 @@ impl NIFS {
/// a folded Relaxed R1CS instance-witness tuple `(U, W)` of the same shape `shape`, /// a folded Relaxed R1CS instance-witness tuple `(U, W)` of the same shape `shape`,
/// with the guarantee that the folded witness `W` satisfies the folded instance `U` /// with the guarantee that the folded witness `W` satisfies the folded instance `U`
/// if and only if `W1` satisfies `U1` and `W2` satisfies `U2`. /// if and only if `W1` satisfies `U1` and `W2` satisfies `U2`.
#[allow(clippy::too_many_arguments)]
pub fn prove( pub fn prove(
ck: &CommitmentKey<G>, ck: &CommitmentKey<G>,
ro_consts: &ROConstants<G>, ro_consts: &ROConstants<G>,
pp_digest: &G::Scalar,
S: &R1CSShape<G>, S: &R1CSShape<G>,
U1: &RelaxedR1CSInstance<G>, U1: &RelaxedR1CSInstance<G>,
W1: &RelaxedR1CSWitness<G>, W1: &RelaxedR1CSWitness<G>,
@ -44,8 +46,8 @@ impl NIFS {
// initialize a new RO // initialize a new RO
let mut ro = G::RO::new(ro_consts.clone(), NUM_FE_FOR_RO); let mut ro = G::RO::new(ro_consts.clone(), NUM_FE_FOR_RO);
// append S to the transcript
S.absorb_in_ro(&mut ro);
// append the digest of pp to the transcript
ro.absorb(scalar_as_base::<G>(*pp_digest));
// append U1 and U2 to transcript // append U1 and U2 to transcript
U1.absorb_in_ro(&mut ro); U1.absorb_in_ro(&mut ro);
@ -84,15 +86,15 @@ impl NIFS {
pub fn verify( pub fn verify(
&self, &self,
ro_consts: &ROConstants<G>, ro_consts: &ROConstants<G>,
S_digest: &G::Scalar,
pp_digest: &G::Scalar,
U1: &RelaxedR1CSInstance<G>, U1: &RelaxedR1CSInstance<G>,
U2: &R1CSInstance<G>, U2: &R1CSInstance<G>,
) -> Result<RelaxedR1CSInstance<G>, NovaError> { ) -> Result<RelaxedR1CSInstance<G>, NovaError> {
// initialize a new RO // initialize a new RO
let mut ro = G::RO::new(ro_consts.clone(), NUM_FE_FOR_RO); let mut ro = G::RO::new(ro_consts.clone(), NUM_FE_FOR_RO);
// append the digest of S to the transcript
ro.absorb(scalar_as_base::<G>(*S_digest));
// append the digest of pp to the transcript
ro.absorb(scalar_as_base::<G>(*pp_digest));
// append U1 and U2 to transcript // append U1 and U2 to transcript
U1.absorb_in_ro(&mut ro); U1.absorb_in_ro(&mut ro);
@ -192,12 +194,23 @@ mod tests {
assert!(shape.is_sat(&ck, &U2, &W2).is_ok()); assert!(shape.is_sat(&ck, &U2, &W2).is_ok());
// execute a sequence of folds // execute a sequence of folds
execute_sequence(&ck, &ro_consts, &shape, &U1, &W1, &U2, &W2);
execute_sequence(
&ck,
&ro_consts,
&<G as Group>::Scalar::ZERO,
&shape,
&U1,
&W1,
&U2,
&W2,
);
} }
#[allow(clippy::too_many_arguments)]
fn execute_sequence( fn execute_sequence(
ck: &CommitmentKey<G>, ck: &CommitmentKey<G>,
ro_consts: &<<G as Group>::RO as ROTrait<<G as Group>::Base, <G as Group>::Scalar>>::Constants, ro_consts: &<<G as Group>::RO as ROTrait<<G as Group>::Base, <G as Group>::Scalar>>::Constants,
pp_digest: &<G as Group>::Scalar,
shape: &R1CSShape<G>, shape: &R1CSShape<G>,
U1: &R1CSInstance<G>, U1: &R1CSInstance<G>,
W1: &R1CSWitness<G>, W1: &R1CSWitness<G>,
@ -209,12 +222,12 @@ mod tests {
let mut r_U = RelaxedR1CSInstance::default(ck, shape); let mut r_U = RelaxedR1CSInstance::default(ck, shape);
// produce a step SNARK with (W1, U1) as the first incoming witness-instance pair // produce a step SNARK with (W1, U1) as the first incoming witness-instance pair
let res = NIFS::prove(ck, ro_consts, shape, &r_U, &r_W, U1, W1);
let res = NIFS::prove(ck, ro_consts, pp_digest, shape, &r_U, &r_W, U1, W1);
assert!(res.is_ok()); assert!(res.is_ok());
let (nifs, (_U, W)) = res.unwrap(); let (nifs, (_U, W)) = res.unwrap();
// verify the step SNARK with U1 as the first incoming instance // verify the step SNARK with U1 as the first incoming instance
let res = nifs.verify(ro_consts, &shape.get_digest(), &r_U, U1);
let res = nifs.verify(ro_consts, pp_digest, &r_U, U1);
assert!(res.is_ok()); assert!(res.is_ok());
let U = res.unwrap(); let U = res.unwrap();
@ -225,12 +238,12 @@ mod tests {
r_U = U; r_U = U;
// produce a step SNARK with (W2, U2) as the second incoming witness-instance pair // produce a step SNARK with (W2, U2) as the second incoming witness-instance pair
let res = NIFS::prove(ck, ro_consts, shape, &r_U, &r_W, U2, W2);
let res = NIFS::prove(ck, ro_consts, pp_digest, shape, &r_U, &r_W, U2, W2);
assert!(res.is_ok()); assert!(res.is_ok());
let (nifs, (_U, W)) = res.unwrap(); let (nifs, (_U, W)) = res.unwrap();
// verify the step SNARK with U1 as the first incoming instance // verify the step SNARK with U1 as the first incoming instance
let res = nifs.verify(ro_consts, &shape.get_digest(), &r_U, U2);
let res = nifs.verify(ro_consts, pp_digest, &r_U, U2);
assert!(res.is_ok()); assert!(res.is_ok());
let U = res.unwrap(); let U = res.unwrap();
@ -349,6 +362,15 @@ mod tests {
let (_O, U2, W2) = rand_inst_witness_generator(&ck, &O); let (_O, U2, W2) = rand_inst_witness_generator(&ck, &O);
// execute a sequence of folds // execute a sequence of folds
execute_sequence(&ck, &ro_consts, &S, &U1, &W1, &U2, &W2);
execute_sequence(
&ck,
&ro_consts,
&<G as Group>::Scalar::ZERO,
&S,
&U1,
&W1,
&U2,
&W2,
);
} }
} }

+ 3
- 97
src/r1cs.rs

@ -1,7 +1,7 @@
//! This module defines R1CS related types and a folding scheme for Relaxed R1CS //! This module defines R1CS related types and a folding scheme for Relaxed R1CS
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
use crate::{ use crate::{
constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_HASH_BITS},
constants::{BN_LIMB_WIDTH, BN_N_LIMBS},
errors::NovaError, errors::NovaError,
gadgets::{ gadgets::{
nonnative::{bignat::nat_to_limbs, util::f_to_nat}, nonnative::{bignat::nat_to_limbs, util::f_to_nat},
@ -14,11 +14,9 @@ use crate::{
}; };
use core::{cmp::max, marker::PhantomData}; use core::{cmp::max, marker::PhantomData};
use ff::Field; use ff::Field;
use flate2::{write::ZlibEncoder, Compression};
use itertools::concat; use itertools::concat;
use rayon::prelude::*; use rayon::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sha3::{Digest, Sha3_256};
/// Public parameters for a given R1CS /// Public parameters for a given R1CS
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
@ -36,7 +34,6 @@ pub struct R1CSShape {
pub(crate) A: Vec<(usize, usize, G::Scalar)>, pub(crate) A: Vec<(usize, usize, G::Scalar)>,
pub(crate) B: Vec<(usize, usize, G::Scalar)>, pub(crate) B: Vec<(usize, usize, G::Scalar)>,
pub(crate) C: Vec<(usize, usize, G::Scalar)>, pub(crate) C: Vec<(usize, usize, G::Scalar)>,
digest: G::Scalar, // digest of the rest of R1CSShape
} }
/// A type that holds a witness for a given R1CS instance /// A type that holds a witness for a given R1CS instance
@ -126,19 +123,14 @@ impl R1CSShape {
return Err(NovaError::OddInputLength); return Err(NovaError::OddInputLength);
} }
let digest = Self::compute_digest(num_cons, num_vars, num_io, A, B, C);
let shape = R1CSShape {
Ok(R1CSShape {
num_cons, num_cons,
num_vars, num_vars,
num_io, num_io,
A: A.to_owned(), A: A.to_owned(),
B: B.to_owned(), B: B.to_owned(),
C: C.to_owned(), C: C.to_owned(),
digest,
};
Ok(shape)
})
} }
pub fn multiply_vec( pub fn multiply_vec(
@ -303,67 +295,6 @@ impl R1CSShape {
Ok((T, comm_T)) Ok((T, comm_T))
} }
/// returns the digest of R1CSShape
pub fn get_digest(&self) -> G::Scalar {
self.digest
}
fn compute_digest(
num_cons: usize,
num_vars: usize,
num_io: usize,
A: &[(usize, usize, G::Scalar)],
B: &[(usize, usize, G::Scalar)],
C: &[(usize, usize, G::Scalar)],
) -> G::Scalar {
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
struct R1CSShapeWithoutDigest<G: Group> {
num_cons: usize,
num_vars: usize,
num_io: usize,
A: Vec<(usize, usize, G::Scalar)>,
B: Vec<(usize, usize, G::Scalar)>,
C: Vec<(usize, usize, G::Scalar)>,
}
let shape = R1CSShapeWithoutDigest::<G> {
num_cons,
num_vars,
num_io,
A: A.to_vec(),
B: B.to_vec(),
C: C.to_vec(),
};
// obtain a vector of bytes representing the R1CS shape
let mut encoder = ZlibEncoder::new(Vec::new(), Compression::default());
bincode::serialize_into(&mut encoder, &shape).unwrap();
let shape_bytes = encoder.finish().unwrap();
// convert shape_bytes into a short digest
let mut hasher = Sha3_256::new();
hasher.input(&shape_bytes);
let digest = hasher.result();
// 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 = (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;
}
res
}
/// Pads the R1CSShape so that the number of variables is a power of two /// Pads the R1CSShape so that the number of variables is a power of two
/// Renumbers variables to accomodate padded variables /// Renumbers variables to accomodate padded variables
pub fn pad(&self) -> Self { pub fn pad(&self) -> Self {
@ -378,8 +309,6 @@ impl R1CSShape {
// check if the number of variables are as expected, then // check if the number of variables are as expected, then
// we simply set the number of constraints to the next power of two // we simply set the number of constraints to the next power of two
if self.num_vars == m { if self.num_vars == m {
let digest = Self::compute_digest(m, self.num_vars, self.num_io, &self.A, &self.B, &self.C);
return R1CSShape { return R1CSShape {
num_cons: m, num_cons: m,
num_vars: m, num_vars: m,
@ -387,7 +316,6 @@ impl R1CSShape {
A: self.A.clone(), A: self.A.clone(),
B: self.B.clone(), B: self.B.clone(),
C: self.C.clone(), C: self.C.clone(),
digest,
}; };
} }
@ -414,15 +342,6 @@ impl R1CSShape {
let B_padded = apply_pad(&self.B); let B_padded = apply_pad(&self.B);
let C_padded = apply_pad(&self.C); let C_padded = apply_pad(&self.C);
let digest = Self::compute_digest(
num_cons_padded,
num_vars_padded,
self.num_io,
&A_padded,
&B_padded,
&C_padded,
);
R1CSShape { R1CSShape {
num_cons: num_cons_padded, num_cons: num_cons_padded,
num_vars: num_vars_padded, num_vars: num_vars_padded,
@ -430,23 +349,10 @@ impl R1CSShape {
A: A_padded, A: A_padded,
B: B_padded, B: B_padded,
C: C_padded, C: C_padded,
digest,
} }
} }
} }
impl<G: Group> TranscriptReprTrait<G> for R1CSShape<G> {
fn to_transcript_bytes(&self) -> Vec<u8> {
self.get_digest().to_transcript_bytes()
}
}
impl<G: Group> AbsorbInROTrait<G> for R1CSShape<G> {
fn absorb_in_ro(&self, ro: &mut G::RO) {
ro.absorb(scalar_as_base::<G>(self.get_digest()));
}
}
impl<G: Group> R1CSWitness<G> { impl<G: Group> R1CSWitness<G> {
/// A method to create a witness object using a vector of scalars /// A method to create a witness object using a vector of scalars
pub fn new(S: &R1CSShape<G>, W: &[G::Scalar]) -> Result<R1CSWitness<G>, NovaError> { pub fn new(S: &R1CSShape<G>, W: &[G::Scalar]) -> Result<R1CSWitness<G>, NovaError> {

+ 19
- 7
src/spartan/mod.rs

@ -6,6 +6,7 @@ pub mod pp;
mod sumcheck; mod sumcheck;
use crate::{ use crate::{
compute_digest,
errors::NovaError, errors::NovaError,
r1cs::{R1CSShape, RelaxedR1CSInstance, RelaxedR1CSWitness}, r1cs::{R1CSShape, RelaxedR1CSInstance, RelaxedR1CSWitness},
traits::{ traits::{
@ -129,6 +130,7 @@ impl PolyEvalInstance {
pub struct ProverKey<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> { pub struct ProverKey<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> {
pk_ee: EE::ProverKey, pk_ee: EE::ProverKey,
S: R1CSShape<G>, S: R1CSShape<G>,
vk_digest: G::Scalar, // digest of the verifier's key
} }
/// A type that represents the verifier's key /// A type that represents the verifier's key
@ -137,6 +139,7 @@ pub struct ProverKey> {
pub struct VerifierKey<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> { pub struct VerifierKey<G: Group, EE: EvaluationEngineTrait<G, CE = G::CE>> {
vk_ee: EE::VerifierKey, vk_ee: EE::VerifierKey,
S: R1CSShape<G>, S: R1CSShape<G>,
digest: G::Scalar,
} }
/// A succinct proof of knowledge of a witness to a relaxed R1CS instance /// A succinct proof of knowledge of a witness to a relaxed R1CS instance
@ -169,12 +172,21 @@ impl> RelaxedR1CSSNARKTrait
let S = S.pad(); let S = S.pad();
let vk = VerifierKey {
vk_ee,
S: S.clone(),
let vk = {
let mut vk = VerifierKey {
vk_ee,
S: S.clone(),
digest: G::Scalar::ZERO,
};
vk.digest = compute_digest::<G, VerifierKey<G, EE>>(&vk);
vk
}; };
let pk = ProverKey { pk_ee, S };
let pk = ProverKey {
pk_ee,
S,
vk_digest: vk.digest,
};
Ok((pk, vk)) Ok((pk, vk))
} }
@ -195,8 +207,8 @@ impl> RelaxedR1CSSNARKTrait
assert_eq!(pk.S.num_io.next_power_of_two(), pk.S.num_io); assert_eq!(pk.S.num_io.next_power_of_two(), pk.S.num_io);
assert!(pk.S.num_io < pk.S.num_vars); assert!(pk.S.num_io < pk.S.num_vars);
// append the digest of R1CS matrices and the RelaxedR1CSInstance to the transcript
transcript.absorb(b"S", &pk.S);
// append the digest of vk (which includes R1CS matrices) and the RelaxedR1CSInstance to the transcript
transcript.absorb(b"vk", &pk.vk_digest);
transcript.absorb(b"U", U); transcript.absorb(b"U", U);
// compute the full satisfying assignment by concatenating W.W, U.u, and U.X // compute the full satisfying assignment by concatenating W.W, U.u, and U.X
@ -437,7 +449,7 @@ impl> RelaxedR1CSSNARKTrait
let mut transcript = G::TE::new(b"RelaxedR1CSSNARK"); let mut transcript = G::TE::new(b"RelaxedR1CSSNARK");
// append the digest of R1CS matrices and the RelaxedR1CSInstance to the transcript // append the digest of R1CS matrices and the RelaxedR1CSInstance to the transcript
transcript.absorb(b"S", &vk.S);
transcript.absorb(b"vk", &vk.digest);
transcript.absorb(b"U", U); transcript.absorb(b"U", U);
let (num_rounds_x, num_rounds_y) = ( let (num_rounds_x, num_rounds_y) = (

+ 18
- 9
src/spartan/pp.rs

@ -6,6 +6,7 @@ use crate::{
shape_cs::ShapeCS, shape_cs::ShapeCS,
solver::SatisfyingAssignment, solver::SatisfyingAssignment,
}, },
compute_digest,
errors::NovaError, errors::NovaError,
r1cs::{R1CSShape, RelaxedR1CSInstance, RelaxedR1CSWitness}, r1cs::{R1CSShape, RelaxedR1CSInstance, RelaxedR1CSWitness},
spartan::{ spartan::{
@ -736,6 +737,7 @@ pub struct ProverKey> {
S: R1CSShape<G>, S: R1CSShape<G>,
S_repr: R1CSShapeSparkRepr<G>, S_repr: R1CSShapeSparkRepr<G>,
S_comm: R1CSShapeSparkCommitment<G>, S_comm: R1CSShapeSparkCommitment<G>,
vk_digest: G::Scalar, // digest of verifier's key
} }
/// A type that represents the verifier's key /// A type that represents the verifier's key
@ -746,6 +748,7 @@ pub struct VerifierKey> {
num_vars: usize, num_vars: usize,
vk_ee: EE::VerifierKey, vk_ee: EE::VerifierKey,
S_comm: R1CSShapeSparkCommitment<G>, S_comm: R1CSShapeSparkCommitment<G>,
digest: G::Scalar,
} }
/// A succinct proof of knowledge of a witness to a relaxed R1CS instance /// A succinct proof of knowledge of a witness to a relaxed R1CS instance
@ -938,11 +941,16 @@ impl> RelaxedR1CSSNARKTrait
let S_repr = R1CSShapeSparkRepr::new(&S); let S_repr = R1CSShapeSparkRepr::new(&S);
let S_comm = S_repr.commit(ck); let S_comm = S_repr.commit(ck);
let vk = VerifierKey {
num_cons: S.num_cons,
num_vars: S.num_vars,
S_comm: S_comm.clone(),
vk_ee,
let vk = {
let mut vk = VerifierKey {
num_cons: S.num_cons,
num_vars: S.num_vars,
S_comm: S_comm.clone(),
vk_ee,
digest: G::Scalar::ZERO,
};
vk.digest = compute_digest::<G, VerifierKey<G, EE>>(&vk);
vk
}; };
let pk = ProverKey { let pk = ProverKey {
@ -950,6 +958,7 @@ impl> RelaxedR1CSSNARKTrait
S, S,
S_repr, S_repr,
S_comm, S_comm,
vk_digest: vk.digest,
}; };
Ok((pk, vk)) Ok((pk, vk))
@ -974,8 +983,8 @@ impl> RelaxedR1CSSNARKTrait
assert_eq!(pk.S.num_io.next_power_of_two(), pk.S.num_io); assert_eq!(pk.S.num_io.next_power_of_two(), pk.S.num_io);
assert!(pk.S.num_io < pk.S.num_vars); assert!(pk.S.num_io < pk.S.num_vars);
// append the commitment to R1CS matrices and the RelaxedR1CSInstance to the transcript
transcript.absorb(b"C", &pk.S_comm);
// append the verifier key (which includes commitment to R1CS matrices) and the RelaxedR1CSInstance to the transcript
transcript.absorb(b"vk", &pk.vk_digest);
transcript.absorb(b"U", U); transcript.absorb(b"U", U);
// compute the full satisfying assignment by concatenating W.W, U.u, and U.X // compute the full satisfying assignment by concatenating W.W, U.u, and U.X
@ -1568,8 +1577,8 @@ impl> RelaxedR1CSSNARKTrait
let mut transcript = G::TE::new(b"RelaxedR1CSSNARK"); let mut transcript = G::TE::new(b"RelaxedR1CSSNARK");
let mut u_vec: Vec<PolyEvalInstance<G>> = Vec::new(); let mut u_vec: Vec<PolyEvalInstance<G>> = Vec::new();
// append the commitment to R1CS matrices and the RelaxedR1CSInstance to the transcript
transcript.absorb(b"C", &vk.S_comm);
// append the verifier key (including commitment to R1CS matrices) and the RelaxedR1CSInstance to the transcript
transcript.absorb(b"vk", &vk.digest);
transcript.absorb(b"U", U); transcript.absorb(b"U", U);
let comm_Az = Commitment::<G>::decompress(&self.comm_Az)?; let comm_Az = Commitment::<G>::decompress(&self.comm_Az)?;

Loading…
Cancel
Save