Skip to content

Commit

Permalink
feat(snark-wrapper): wrapper circuit with naive main gate (#29)
Browse files Browse the repository at this point in the history
This PR declares a version of wrapper circuit that works with constraint
system that has no custom gate and lookup functionality.

## Prerequisite
- [PR #28]
  • Loading branch information
saitima authored Oct 29, 2024
1 parent f44dd45 commit 235d0c8
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 31 deletions.
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

0 comments on commit 235d0c8

Please sign in to comment.