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

feat(snark-wrapper): wrapper circuit with naive main gate #29

Merged
merged 3 commits into from
Oct 29, 2024
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
2 changes: 1 addition & 1 deletion crates/franklin-crypto/src/plonk/circuit/goldilocks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl<E: Engine> Hash for GoldilocksField<E> {
}
}

fn range_check_for_num_bits<E: Engine, CS: ConstraintSystem<E>>(cs: &mut CS, num: &Num<E>, num_bits: usize) -> Result<(), SynthesisError> {
pub fn range_check_for_num_bits<E: Engine, CS: ConstraintSystem<E>>(cs: &mut CS, num: &Num<E>, num_bits: usize) -> Result<(), SynthesisError> {
assert!(num_bits % 16 == 0);

if let Num::Constant(value) = num {
Expand Down
2 changes: 1 addition & 1 deletion crates/snark-wrapper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ description = "ZKsync snark wrapper"

[dependencies]
rescue_poseidon.workspace = true

serde = { version = "1", features = ["derive"] }
derivative = "2"
rand = "0.4"
1 change: 1 addition & 0 deletions crates/snark-wrapper/src/implementations/poseidon2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::boojum::field::PrimeField as BoojumPrimeField;

use derivative::*;

pub mod pow;
pub mod transcript;
pub mod tree_hasher;

Expand Down
59 changes: 59 additions & 0 deletions crates/snark-wrapper/src/implementations/poseidon2/pow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use crate::traits::pow::RecursivePoWRunner;

use super::*;
use rescue_poseidon::franklin_crypto::bellman::Field;

pub type ConcretePoseidon2SpongeGadget<E> = CircuitPoseidon2Sponge<E, 2, 3, 3, true>;

impl<E: Engine> RecursivePoWRunner<E> for ConcretePoseidon2SpongeGadget<E> {
fn verify_from_field_elements<CS: ConstraintSystem<E>>(
cs: &mut CS,
seed: Vec<GoldilocksField<E>>,
pow_challenge_le_bits: [Boolean; 64],
pow_bits: usize,
) -> Result<(Boolean, [GoldilocksField<E>; 2]), SynthesisError> {
let mut sponge = ConcretePoseidon2SpongeGadget::new();

for el in seed.iter() {
sponge.absorb_single_gl(cs, el)?;
}

// commit nonce
let mut lc = LinearCombination::zero();
let mut coeff = E::Fr::one();
for bit in pow_challenge_le_bits[..32].iter() {
lc.add_assign_boolean_with_coeff(bit, coeff.clone());
coeff.double();
}
let low = lc.into_num(cs)?;
let low = GoldilocksField::from_num(cs, low)?;
sponge.absorb_single_gl(cs, &low)?;

let mut lc = LinearCombination::zero();
let mut coeff = E::Fr::one();
for bit in pow_challenge_le_bits[32..].iter() {
lc.add_assign_boolean_with_coeff(bit, coeff.clone());
coeff.double();
}
let high = lc.into_num(cs)?;
let high = GoldilocksField::from_num(cs, high)?;
sponge.absorb_single_gl(cs, &high)?;

// get final pow challenge
let result = sponge.finalize(cs)?[0];

// verify that pow challenge has enough zeroes
let allocated_bools = result.into_bits_le(cs, None)?;

let mut lc = LinearCombination::zero();
let coeff = E::Fr::one();
for b in allocated_bools.iter().take(pow_bits) {
lc.add_assign_boolean_with_coeff(b, coeff);
}
let num_zeroes = lc.into_num(cs)?;
let result = num_zeroes.is_zero(cs)?;
Boolean::enforce_equal(cs, &result, &Boolean::constant(true))?;

Ok((result, [low, high]))
}
}
1 change: 1 addition & 0 deletions crates/snark-wrapper/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ use crate::franklin_crypto::plonk::circuit::boolean::Boolean;
use crate::franklin_crypto::plonk::circuit::goldilocks::*;

pub mod circuit;
pub mod pow;
pub mod transcript;
pub mod tree_hasher;
23 changes: 23 additions & 0 deletions crates/snark-wrapper/src/traits/pow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use super::*;

pub trait RecursivePoWRunner<E: Engine> {
fn verify_from_field_elements<CS: ConstraintSystem<E>>(
cs: &mut CS,
seed: Vec<GoldilocksField<E>>,
pow_challenge: [Boolean; 64],
pow_bits: usize,
) -> Result<(Boolean, [GoldilocksField<E>; 2]), SynthesisError>;
}

pub struct NoCircuitPow;

impl<E: Engine> RecursivePoWRunner<E> for NoCircuitPow {
fn verify_from_field_elements<CS: ConstraintSystem<E>>(
cs: &mut CS,
seed: Vec<GoldilocksField<E>>,
pow_challenge: [Boolean; 64],
pow_bits: usize,
) -> Result<(Boolean, [GoldilocksField<E>; 2]), SynthesisError> {
unimplemented!()
}
}
41 changes: 26 additions & 15 deletions crates/snark-wrapper/src/verifier/fri.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
use super::*;

use crate::traits::pow::RecursivePoWRunner;
use crate::traits::transcript::BoolsBuffer;
use crate::verifier_structs::allocated_queries::AllocatedSingleRoundQueries;

pub(crate) fn verify_fri_part<E: Engine, CS: ConstraintSystem<E> + 'static, H: CircuitGLTreeHasher<E>, TR: CircuitGLTranscript<E, CircuitCompatibleCap = H::CircuitOutput>>(
pub(crate) fn verify_fri_part<
E: Engine,
CS: ConstraintSystem<E> + 'static,
H: CircuitGLTreeHasher<E>,
TR: CircuitGLTranscript<E, CircuitCompatibleCap = H::CircuitOutput>,
POW: RecursivePoWRunner<E>,
>(
cs: &mut CS,
proof: &AllocatedProof<E, H>,
vk: &AllocatedVerificationKey<E, H>,
Expand All @@ -29,20 +36,24 @@ pub(crate) fn verify_fri_part<E: Engine, CS: ConstraintSystem<E> + 'static, H: C
transcript.witness_field_elements(cs, &proof.final_fri_monomials[0])?;
transcript.witness_field_elements(cs, &proof.final_fri_monomials[1])?;

assert_eq!(constants.new_pow_bits, 0, "PoW not supported yet");
// if new_pow_bits != 0 {
// log!("Doing PoW verification for {} bits", new_pow_bits);
// // log!("Prover gave challenge 0x{:016x}", proof.pow_challenge);

// // pull enough challenges from the transcript
// let mut num_challenges = 256 / F::CHAR_BITS;
// if num_challenges % F::CHAR_BITS != 0 {
// num_challenges += 1;
// }
// let _challenges: Vec<_> = transcript.get_multiple_challenges(cs, num_challenges);

// todo!()
// }
if constants.new_pow_bits != 0 {
// pull enough challenges from the transcript
let mut num_challenges = 256 / GL::CHAR_BITS;
if num_challenges % GL::CHAR_BITS != 0 {
num_challenges += 1;
}
let challenges: Vec<_> = transcript.get_multiple_challenges(cs, num_challenges as usize)?;
let (is_valid, pow_challenge_limbs) = POW::verify_from_field_elements(cs, challenges, proof.pow_challenge_le, constants.new_pow_bits)?;
match is_valid.get_value() {
Some(is_valid) => {
if is_valid == false {
println!("PoW challenge is invalid")
}
}
None => (),
}
transcript.witness_field_elements(cs, &pow_challenge_limbs)?;
}

let max_needed_bits = (fixed_parameters.domain_size * fixed_parameters.fri_lde_factor as u64).trailing_zeros() as usize;

Expand Down
69 changes: 57 additions & 12 deletions crates/snark-wrapper/src/verifier/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ use crate::franklin_crypto::plonk::circuit::goldilocks::GoldilocksField;
use crate::franklin_crypto::plonk::circuit::linear_combination::LinearCombination;
use crate::franklin_crypto::plonk::circuit::Assignment;

use crate::implementations::poseidon2::pow::ConcretePoseidon2SpongeGadget;
use crate::traits::circuit::*;
use crate::traits::transcript::CircuitGLTranscript;
use crate::traits::tree_hasher::CircuitGLTreeHasher;
use crate::traits::*;
use crate::verifier_structs::allocated_vk::AllocatedVerificationKey;
use crate::verifier_structs::challenges::{ChallengesHolder, EvaluationsHolder};
use crate::verifier_structs::constants::ConstantsHolder;
Expand All @@ -39,10 +41,11 @@ pub(crate) mod utils;

use first_step::*;
use fri::*;
use pow::RecursivePoWRunner;
use quotient_contributions::*;
use utils::*;

#[derive(Clone, Debug)]
#[derive(Clone, Debug, serde::Serialize)]
pub struct WrapperCircuit<
E: Engine,
HS: TreeHasher<GL, Output = E::Fr>,
Expand Down Expand Up @@ -91,7 +94,7 @@ impl<
let proof: AllocatedProof<E, H> = AllocatedProof::allocate_from_witness(cs, &self.witness, &verifier, &fixed_parameters, &proof_config)?;

// Verify proof
let correct = crate::verifier::verify::<E, CS, H, TR>(cs, self.transcript_params.clone(), &proof_config, &proof, &verifier, &fixed_parameters, &vk)?;
let correct = crate::verifier::verify::<E, CS, H, TR, ConcretePoseidon2SpongeGadget<E>>(cs, self.transcript_params.clone(), &proof_config, &proof, &verifier, &fixed_parameters, &vk)?;
Boolean::enforce_equal(cs, &correct, &Boolean::constant(true))?;

// Aggregate PI
Expand All @@ -101,13 +104,57 @@ impl<
}
}

pub fn verify<
#[derive(Clone)]
pub struct WrapperCircuitWidth3NoLookupNoCustomGate<
E: Engine,
CS: ConstraintSystem<E> + 'static,
H: CircuitGLTreeHasher<E>,
HS: TreeHasher<GL, Output = E::Fr>,
H: CircuitGLTreeHasher<E, CircuitOutput = Num<E>, NonCircuitSimulator = HS>,
TR: CircuitGLTranscript<E, CircuitCompatibleCap = H::CircuitOutput>,
// TODO POW
>(
PWF: ProofWrapperFunction<E>,
> {
pub witness: Option<Proof<GL, HS, GLExt2>>,
pub vk: VerificationKey<GL, H::NonCircuitSimulator>,
pub fixed_parameters: VerificationKeyCircuitGeometry,
pub transcript_params: TR::TranscriptParameters,
pub wrapper_function: PWF,
}

impl<
E: Engine,
HS: TreeHasher<GL, Output = E::Fr>,
H: CircuitGLTreeHasher<E, CircuitOutput = Num<E>, NonCircuitSimulator = HS>,
TR: CircuitGLTranscript<E, CircuitCompatibleCap = H::CircuitOutput>,
PWF: ProofWrapperFunction<E>,
> Circuit<E> for WrapperCircuitWidth3NoLookupNoCustomGate<E, HS, H, TR, PWF>
{
type MainGate = rescue_poseidon::franklin_crypto::bellman::plonk::better_better_cs::gates::naive_main_gate::NaiveMainGate;

fn declare_used_gates() -> Result<Vec<Box<dyn GateInternal<E>>>, SynthesisError> {
Ok(vec![Self::MainGate::default().into_internal()])
}

fn synthesize<CS: ConstraintSystem<E> + 'static>(&self, cs: &mut CS) -> Result<(), SynthesisError> {
// Prepare for proof verification
let verifier_builder = self.wrapper_function.builder_for_wrapper();
let verifier = verifier_builder.create_wrapper_verifier(cs);

let proof_config = self.wrapper_function.proof_config_for_compression_step();
let fixed_parameters = self.fixed_parameters.clone();

let vk = AllocatedVerificationKey::<E, H>::allocate_constant(&self.vk, &fixed_parameters);
let proof: AllocatedProof<E, H> = AllocatedProof::allocate_from_witness(cs, &self.witness, &verifier, &fixed_parameters, &proof_config)?;
// Verify proof
let correct = crate::verifier::verify::<E, CS, H, TR, ConcretePoseidon2SpongeGadget<E>>(cs, self.transcript_params.clone(), &proof_config, &proof, &verifier, &fixed_parameters, &vk)?;
Boolean::enforce_equal(cs, &correct, &Boolean::constant(true))?;

// Aggregate PI
let _pi = aggregate_public_inputs(cs, &proof.public_inputs)?;

Ok(())
}
}

pub fn verify<E: Engine, CS: ConstraintSystem<E> + 'static, H: CircuitGLTreeHasher<E>, TR: CircuitGLTranscript<E, CircuitCompatibleCap = H::CircuitOutput>, POW: RecursivePoWRunner<E>>(
cs: &mut CS,
transcript_params: TR::TranscriptParameters,
proof_config: &ProofConfig,
Expand All @@ -128,8 +175,7 @@ pub fn verify<
let public_input_opening_tuples = verify_first_step(cs, proof, vk, &mut challenges, &mut transcript, verifier, fixed_parameters, &constants)?;

validity_flags.extend(check_quotient_contributions_in_z(cs, proof, &challenges, verifier, fixed_parameters, &constants)?);

validity_flags.extend(verify_fri_part::<E, CS, H, TR>(
validity_flags.extend(verify_fri_part::<E, CS, H, TR, POW>(
cs,
proof,
vk,
Expand All @@ -155,10 +201,9 @@ fn aggregate_public_inputs<E: Engine, CS: ConstraintSystem<E>>(cs: &mut CS, publ
);

// Firstly we check that public inputs have correct size
use crate::franklin_crypto::plonk::circuit::bigint_new::enforce_range_check_using_bitop_table;
use rescue_poseidon::franklin_crypto::plonk::circuit::goldilocks::range_check_for_num_bits;
for pi in public_inputs.iter() {
let table = cs.get_table(BITWISE_LOGICAL_OPS_TABLE_NAME).unwrap();
enforce_range_check_using_bitop_table(cs, &pi.into_num().get_variable(), chunk_bit_size, table, false)?;
range_check_for_num_bits(cs, &pi.into_num(), 64)?;
}

// compute aggregated pi value
Expand Down
4 changes: 2 additions & 2 deletions crates/snark-wrapper/src/verifier_structs/allocated_proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub struct AllocatedProof<E: Engine, H: CircuitGLTreeHasher<E>> {

pub queries_per_fri_repetition: Vec<AllocatedSingleRoundQueries<E, H>>,

pub pow_challenge: [Boolean; 64],
pub pow_challenge_le: [Boolean; 64],
}

impl<E: Engine, HS: TreeHasher<GL, Output = E::Fr>, H: CircuitGLTreeHasher<E, CircuitOutput = Num<E>, NonCircuitSimulator = HS>> AllocatedProof<E, H> {
Expand Down Expand Up @@ -121,7 +121,7 @@ impl<E: Engine, HS: TreeHasher<GL, Output = E::Fr>, H: CircuitGLTreeHasher<E, Ci

queries_per_fri_repetition,

pow_challenge: pow_challenge_boolean,
pow_challenge_le: pow_challenge_boolean,
})
}
}
Expand Down
Loading