Skip to content

Commit

Permalink
hash of public parameters in the transcript (#168)
Browse files Browse the repository at this point in the history
  • Loading branch information
srinathsetty authored May 18, 2023
1 parent f16fa1e commit b28aaf7
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 142 deletions.
5 changes: 3 additions & 2 deletions src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ mod tests {
use crate::constants::{BN_LIMB_WIDTH, BN_N_LIMBS};
use crate::{
bellperson::r1cs::{NovaShape, NovaWitness},
gadgets::utils::scalar_as_base,
provider::poseidon::PoseidonConstantsCircuit,
traits::{circuit::TrivialTestCircuit, ROConstantsTrait},
};
Expand Down Expand Up @@ -420,7 +421,7 @@ mod tests {
let zero1 = <<G2 as Group>::Base as Field>::ZERO;
let mut cs1: SatisfyingAssignment<G1> = SatisfyingAssignment::new();
let inputs1: NovaAugmentedCircuitInputs<G2> = NovaAugmentedCircuitInputs::new(
shape2.get_digest(),
scalar_as_base::<G1>(zero1), // pass zero for testing
zero1,
vec![zero1],
None,
Expand All @@ -444,7 +445,7 @@ mod tests {
let zero2 = <<G1 as Group>::Base as Field>::ZERO;
let mut cs2: SatisfyingAssignment<G2> = SatisfyingAssignment::new();
let inputs2: NovaAugmentedCircuitInputs<G1> = NovaAugmentedCircuitInputs::new(
shape1.get_digest(),
scalar_as_base::<G2>(zero2), // pass zero for testing
zero2,
vec![zero2],
None,
Expand Down
73 changes: 57 additions & 16 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 errors::NovaError;
use ff::Field;
use flate2::{write::ZlibEncoder, Compression};
use gadgets::utils::scalar_as_base;
use nifs::NIFS;
use r1cs::{R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness};
use serde::{Deserialize, Serialize};
use sha3::{Digest, Sha3_256};
use traits::{
circuit::StepCircuit,
commitment::{CommitmentEngineTrait, CommitmentTrait},
Expand Down Expand Up @@ -69,6 +71,7 @@ where
r1cs_shape_secondary: R1CSShape<G2>,
augmented_circuit_params_primary: 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_c2: PhantomData<C2>,
}
Expand Down Expand Up @@ -119,7 +122,7 @@ where
let _ = circuit_secondary.synthesize(&mut cs);
let (r1cs_shape_secondary, ck_secondary) = cs.r1cs_shape();

Self {
let mut pp = Self {
F_arity_primary,
F_arity_secondary,
ro_consts_primary,
Expand All @@ -132,9 +135,15 @@ where
r1cs_shape_secondary,
augmented_circuit_params_primary,
augmented_circuit_params_secondary,
digest: G1::Scalar::ZERO,
_p_c1: 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
Expand Down Expand Up @@ -205,7 +214,7 @@ where
// base case for the primary
let mut cs_primary: SatisfyingAssignment<G1> = SatisfyingAssignment::new();
let inputs_primary: NovaAugmentedCircuitInputs<G2> = NovaAugmentedCircuitInputs::new(
pp.r1cs_shape_secondary.get_digest(),
scalar_as_base::<G1>(pp.digest),
G1::Scalar::ZERO,
z0_primary.clone(),
None,
Expand All @@ -228,7 +237,7 @@ where
// base case for the secondary
let mut cs_secondary: SatisfyingAssignment<G2> = SatisfyingAssignment::new();
let inputs_secondary: NovaAugmentedCircuitInputs<G1> = NovaAugmentedCircuitInputs::new(
pp.r1cs_shape_primary.get_digest(),
pp.digest,
G2::Scalar::ZERO,
z0_secondary.clone(),
None,
Expand Down Expand Up @@ -294,6 +303,7 @@ where
let (nifs_secondary, (r_U_secondary, r_W_secondary)) = NIFS::prove(
&pp.ck_secondary,
&pp.ro_consts_secondary,
&scalar_as_base::<G1>(pp.digest),
&pp.r1cs_shape_secondary,
&r_snark.r_U_secondary,
&r_snark.r_W_secondary,
Expand All @@ -303,7 +313,7 @@ where

let mut cs_primary: SatisfyingAssignment<G1> = SatisfyingAssignment::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),
z0_primary,
Some(r_snark.zi_primary.clone()),
Expand All @@ -328,6 +338,7 @@ where
let (nifs_primary, (r_U_primary, r_W_primary)) = NIFS::prove(
&pp.ck_primary,
&pp.ro_consts_primary,
&pp.digest,
&pp.r1cs_shape_primary,
&r_snark.r_U_primary,
&r_snark.r_W_primary,
Expand All @@ -337,7 +348,7 @@ where

let mut cs_secondary: SatisfyingAssignment<G2> = SatisfyingAssignment::new();
let inputs_secondary: NovaAugmentedCircuitInputs<G1> = NovaAugmentedCircuitInputs::new(
pp.r1cs_shape_primary.get_digest(),
pp.digest,
G2::Scalar::from(r_snark.i as u64),
z0_secondary,
Some(r_snark.zi_secondary.clone()),
Expand Down Expand Up @@ -414,7 +425,7 @@ where
pp.ro_consts_secondary.clone(),
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));
for e in &z0_primary {
hasher.absorb(*e);
Expand All @@ -428,7 +439,7 @@ where
pp.ro_consts_primary.clone(),
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));
for e in &z0_secondary {
hasher2.absorb(*e);
Expand Down Expand Up @@ -531,8 +542,7 @@ where
F_arity_secondary: usize,
ro_consts_primary: ROConstants<G1>,
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_secondary: S2::VerifierKey,
_p_c1: PhantomData<C1>,
Expand Down Expand Up @@ -602,8 +612,7 @@ where
F_arity_secondary: pp.F_arity_secondary,
ro_consts_primary: pp.ro_consts_primary.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_secondary,
_p_c1: Default::default(),
Expand All @@ -625,6 +634,7 @@ where
NIFS::prove(
&pp.ck_primary,
&pp.ro_consts_primary,
&pp.digest,
&pp.r1cs_shape_primary,
&recursive_snark.r_U_primary,
&recursive_snark.r_W_primary,
Expand All @@ -637,6 +647,7 @@ where
NIFS::prove(
&pp.ck_secondary,
&pp.ro_consts_secondary,
&scalar_as_base::<G1>(pp.digest),
&pp.r1cs_shape_secondary,
&recursive_snark.r_U_secondary,
&recursive_snark.r_W_secondary,
Expand Down Expand Up @@ -709,7 +720,7 @@ where
vk.ro_consts_secondary.clone(),
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));
for e in z0_primary {
hasher.absorb(e);
Expand All @@ -723,7 +734,7 @@ where
vk.ro_consts_primary.clone(),
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));
for e in z0_secondary {
hasher2.absorb(e);
Expand All @@ -748,13 +759,13 @@ where
// fold the running instance and last instance to get a folded instance
let f_U_primary = self.nifs_primary.verify(
&vk.ro_consts_primary,
&vk.r1cs_shape_primary_digest,
&vk.digest,
&self.r_U_primary,
&self.l_u_primary,
)?;
let f_U_secondary = self.nifs_secondary.verify(
&vk.ro_consts_secondary,
&vk.r1cs_shape_secondary_digest,
&scalar_as_base::<G1>(vk.digest),
&self.r_U_secondary,
&self.l_u_secondary,
)?;
Expand All @@ -781,6 +792,36 @@ 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 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)]
mod tests {
use super::*;
Expand Down
44 changes: 33 additions & 11 deletions src/nifs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ impl<G: Group> NIFS<G> {
/// 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`
/// if and only if `W1` satisfies `U1` and `W2` satisfies `U2`.
#[allow(clippy::too_many_arguments)]
pub fn prove(
ck: &CommitmentKey<G>,
ro_consts: &ROConstants<G>,
pp_digest: &G::Scalar,
S: &R1CSShape<G>,
U1: &RelaxedR1CSInstance<G>,
W1: &RelaxedR1CSWitness<G>,
Expand All @@ -44,8 +46,8 @@ impl<G: Group> NIFS<G> {
// initialize a new 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
U1.absorb_in_ro(&mut ro);
Expand Down Expand Up @@ -84,15 +86,15 @@ impl<G: Group> NIFS<G> {
pub fn verify(
&self,
ro_consts: &ROConstants<G>,
S_digest: &G::Scalar,
pp_digest: &G::Scalar,
U1: &RelaxedR1CSInstance<G>,
U2: &R1CSInstance<G>,
) -> Result<RelaxedR1CSInstance<G>, NovaError> {
// initialize a new 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
U1.absorb_in_ro(&mut ro);
Expand Down Expand Up @@ -192,12 +194,23 @@ mod tests {
assert!(shape.is_sat(&ck, &U2, &W2).is_ok());

// 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(
ck: &CommitmentKey<G>,
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>,
U1: &R1CSInstance<G>,
W1: &R1CSWitness<G>,
Expand All @@ -209,12 +222,12 @@ mod tests {
let mut r_U = RelaxedR1CSInstance::default(ck, shape);

// 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());
let (nifs, (_U, W)) = res.unwrap();

// 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());
let U = res.unwrap();

Expand All @@ -225,12 +238,12 @@ mod tests {
r_U = U;

// 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());
let (nifs, (_U, W)) = res.unwrap();

// 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());
let U = res.unwrap();

Expand Down Expand Up @@ -349,6 +362,15 @@ mod tests {
let (_O, U2, W2) = rand_inst_witness_generator(&ck, &O);

// 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,
);
}
}
Loading

0 comments on commit b28aaf7

Please sign in to comment.