L2 — Deep into PLONK Aggregation circuit

synthesize function

synthesize logic is rather clear. The whole circuit implements following features:

let mut pairs_for_generator = vec![];
let mut pairs_for_x = vec![];

for proof_idx in 0..self.num_proofs_to_check {
let proof = &proof_witnesses[proof_idx];
let vk = &vk_witnesses[proof_idx];

let [pair_with_generator, pair_with_x] = aggregate_proof::<_, _, T, CS::Params, P, _, _>(

let mut sponge = StatefulRescueGadget::<E>::new(self.rescue_params);

for w in fs_witnesses.into_iter() {
sponge.absorb_single_value(cs, w, self.rescue_params)?;


let aggregation_challenge = sponge
.squeeze_out_single(cs, self.rescue_params)?
let mut scalars = vec![];

let mut current = aggregation_challenge.clone();
for _ in 1..self.num_proofs_to_check {
let new = current.mul(cs, &aggregation_challenge)?;

current = new;
let pair_with_generator = WP::multiexp(
let pair_with_x = WP::multiexp(
function verify_recursive(
Proof memory proof,
VerificationKey memory vk,
uint256 recursive_vks_root,
uint8 max_valid_index,
uint8[] memory recursive_vks_indexes,
uint256[] memory individual_vks_inputs,
uint256[16] memory subproofs_limbs
) internal view returns (bool) {
(uint256 recursive_input, PairingsBn254.G1Point[2] memory aggregated_g1s) = reconstruct_recursive_public_input(
recursive_vks_root, max_valid_index, recursive_vks_indexes,
individual_vks_inputs, subproofs_limbs

assert(recursive_input == proof.input_values[0]);

(bool valid, PairingsBn254.G1Point[2] memory recursive_proof_part) = aggregate_for_verification(proof, vk);
if (valid == false) {
return false;

// aggregated_g1s = inner
// recursive_proof_part = outer
PairingsBn254.G1Point[2] memory combined = combine_inner_and_outer(aggregated_g1s, recursive_proof_part);

valid = PairingsBn254.pairingProd2(combined[0], PairingsBn254.P2(), combined[1], vk.g2_x);

return valid;

Point calculation proof

Point calculation circuit is implemented in the franklin-crypto library located at src/plonk/circuit/curve/sw_affine.rs file.

pub fn multiexp<CS: ConstraintSystem<E>>(
cs: &mut CS,
scalars: &[Num::<E>],
points: &[Self],
bit_limit: Option<usize>
) -> Result<Self, SynthesisError> {
pub struct AffinePoint<'a, E: Engine, G: CurveAffine> where <G as CurveAffine>::Base: PrimeField {
pub x: FieldElement<'a, E, G::Base>,
pub y: FieldElement<'a, E, G::Base>,
pub value: Option<G>,

pub struct FieldElement<'a, E: Engine, F: PrimeField>{
// this is kind-of normal UintX limbs
pub binary_limbs: Vec<Limb<E>>,
// we can not multiply by power of modulus of our base field E,
// so we keep only one single limb
pub base_field_limb: Term<E>,

pub representation_params: &'a RnsParameters<E, F>,
pub value: Option<F>,

pub struct Limb<E: Engine> {
pub term: Term<E>,
pub max_value: BigUint,



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store


Trapdoor-Tech tries to connect the world with zero-knowledge proof technologies. zk-SNARK/STARK solution and proving acceleration are our first small steps :)