use crate::boojum::cs::implementations::proof::Proof;
use crate::boojum::cs::implementations::prover::ProofConfig;
use crate::boojum::cs::implementations::utils::domain_generator_for_size;
use crate::boojum::cs::implementations::verifier::VerificationKey;
use crate::boojum::cs::implementations::verifier::VerificationKeyCircuitGeometry;
use crate::boojum::cs::oracle::TreeHasher;
use crate::boojum::cs::LookupParameters;
use crate::boojum::field::goldilocks::{GoldilocksExt2 as GLExt2, GoldilocksField as GL};
use crate::boojum::field::traits::field_like::PrimeFieldLike;
use crate::boojum::field::Field as BoojumField;
use crate::boojum::field::PrimeField as BoojumPrimeField;
use crate::franklin_crypto::bellman::pairing::Engine;
use crate::franklin_crypto::bellman::plonk::better_better_cs::cs::ConstraintSystem;
use crate::franklin_crypto::bellman::plonk::better_better_cs::cs::*;
use crate::franklin_crypto::bellman::plonk::better_better_cs::gates::selector_optimized_with_d_next::SelectorOptimizedWidth4MainGateWithDNext;
use crate::franklin_crypto::bellman::{Field, PrimeField, PrimeFieldRepr, SynthesisError};
use crate::franklin_crypto::plonk::circuit::allocated_num::{AllocatedNum, Num};
use crate::franklin_crypto::plonk::circuit::bigint_new::BITWISE_LOGICAL_OPS_TABLE_NAME;
use crate::franklin_crypto::plonk::circuit::boolean::Boolean;
use crate::franklin_crypto::plonk::circuit::custom_rescue_gate::Rescue5CustomGate;
use crate::franklin_crypto::plonk::circuit::goldilocks::prime_field_like::{GoldilocksAsFieldWrapper, GoldilocksExtAsFieldWrapper};
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;
use crate::verifier_structs::{allocated_proof::*, *};
mod first_step;
mod fri;
mod quotient_contributions;
pub(crate) mod utils;
use first_step::*;
use fri::*;
use pow::RecursivePoWRunner;
use quotient_contributions::*;
use utils::*;
#[derive(Clone, Debug, serde::Serialize)]
pub struct WrapperCircuit<
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>,
> {
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 WrapperCircuit<E, HS, H, TR, PWF>
{
type MainGate = SelectorOptimizedWidth4MainGateWithDNext;
fn declare_used_gates() -> Result<Vec<Box<dyn GateInternal<E>>>, SynthesisError> {
Ok(vec![Self::MainGate::default().into_internal(), Rescue5CustomGate::default().into_internal()])
}
fn synthesize<CS: ConstraintSystem<E> + 'static>(&self, cs: &mut CS) -> Result<(), SynthesisError> {
let columns3 = vec![PolyIdentifier::VariablesPolynomial(0), PolyIdentifier::VariablesPolynomial(1), PolyIdentifier::VariablesPolynomial(2)];
let name = BITWISE_LOGICAL_OPS_TABLE_NAME;
let bitwise_logic_table = LookupTableApplication::new(name, TwoKeysOneValueBinopTable::<E, XorBinop>::new(8, name), columns3.clone(), None, true);
cs.add_table(bitwise_logic_table).unwrap();
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)?;
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))?;
let _pi = aggregate_public_inputs(cs, &proof.public_inputs)?;
Ok(())
}
}
#[derive(Clone)]
pub struct WrapperCircuitWidth3NoLookupNoCustomGate<
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>,
> {
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> {
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)?;
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))?;
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,
proof: &AllocatedProof<E, H>,
verifier: &WrapperVerifier<E, CS>,
fixed_parameters: &VerificationKeyCircuitGeometry,
vk: &AllocatedVerificationKey<E, H>,
) -> Result<Boolean, SynthesisError> {
let mut validity_flags = Vec::with_capacity(256);
let mut transcript = TR::new(cs, transcript_params)?;
let mut challenges = ChallengesHolder::new(cs);
let constants = ConstantsHolder::generate(proof_config, verifier, fixed_parameters);
assert_eq!(fixed_parameters.cap_size, vk.setup_merkle_tree_cap.len());
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, POW>(
cs,
proof,
vk,
&mut challenges,
&mut transcript,
public_input_opening_tuples,
verifier,
fixed_parameters,
&constants,
)?);
let correct = smart_and(cs, &validity_flags)?;
Ok(correct)
}
fn aggregate_public_inputs<E: Engine, CS: ConstraintSystem<E>>(cs: &mut CS, public_inputs: &[GoldilocksField<E>]) -> Result<Num<E>, SynthesisError> {
let chunk_bit_size = (GL::CAPACITY_BITS / 8) * 8;
assert!(
public_inputs.len() * chunk_bit_size <= E::Fr::CAPACITY as usize,
"scalar field capacity is not enough to fit all public inputs"
);
use rescue_poseidon::franklin_crypto::plonk::circuit::goldilocks::range_check_for_num_bits;
for pi in public_inputs.iter() {
range_check_for_num_bits(cs, &pi.into_num(), 64)?;
}
let mut tmp = E::Fr::one();
let mut shift_repr = <E::Fr as PrimeField>::Repr::from(1);
shift_repr.shl(chunk_bit_size as u32);
let shift = E::Fr::from_repr(shift_repr).unwrap();
let mut lc = LinearCombination::<E>::zero();
for pi in public_inputs.iter().rev() {
lc.add_assign_number_with_coeff(&pi.into_num(), tmp);
tmp.mul_assign(&shift);
}
let pi = Num::Variable(AllocatedNum::alloc_input(cs, || Ok(*lc.get_value().get()?))?);
let mut minus_one = E::Fr::one();
minus_one.negate();
lc.add_assign_number_with_coeff(&pi, minus_one);
lc.enforce_zero(cs)?;
Ok(pi)
}