Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hash of public parameters in the transcript #168

Merged
merged 1 commit into from
May 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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