#zk-snarks #zero-knowledge #crypto

no-std plonkup

A pure-Rust implementation of the PLONK ZK-Proof algorithm

1 unstable release

0.9.2 Feb 8, 2022
0.1.0 Jun 15, 2022

#2117 in Cryptography

MPL-2.0 license

10K SLoC


Build Status Repository Documentation Code Coverage GitHub Issues License

This is a pure Rust implementation of the PlonkUp proving system over BLS12-381. More details on this proving system can be found in this paper.

This library contains a modularized implementation of KZG10 as the default polynomial commitment scheme.

DISCLAIMER: This library is currently unstable and still needs to go through an exhaustive security analysis. Use at your own risk.


use plonkup::prelude::*;
use rand_core::OsRng;

// Implement a circuit that checks:
// 1) a + b = c where C is a PI
// 2) a <= 2^6
// 3) b <= 2^5
// 4) a * b = d where D is a PI
// 5) JubJub::GENERATOR * e(JubJubScalar) = f where F is a Public Input
#[derive(Debug, Default)]
pub struct TestCircuit {
    a: BlsScalar,
    b: BlsScalar,
    c: BlsScalar,
    d: BlsScalar,
    e: JubJubScalar,
    f: JubJubAffine,

impl Circuit for TestCircuit {
    const CIRCUIT_ID: [u8; 32] = [0xff; 32];
    fn gadget(
        &mut self,
        composer: &mut TurboComposer,
    ) -> Result<(), Error> {
        let a = composer.append_witness(self.a);
        let b = composer.append_witness(self.b);

        // Make first constraint a + b = c
        let constraint = Constraint::new()


        // Check that a and b are in range
        composer.component_range(a, 1 << 6);
        composer.component_range(b, 1 << 5);

        // Make second constraint a * b = d
        let constraint = Constraint::new()


        let e = composer.append_witness(self.e);
        let scalar_mul_result = composer
            .component_mul_generator(e, dusk_jubjub::GENERATOR_EXTENDED);

        // Apply the constraint
        composer.assert_equal_public_point(scalar_mul_result, self.f);

    fn public_inputs(&self) -> Vec<PublicInputValue> {
        vec![self.c.into(), self.d.into(), self.f.into()]

    fn padded_gates(&self) -> usize {
        1 << 11

// Now let's use the Circuit we've just implemented!

let pp = PublicParameters::setup(1 << 12, &mut OsRng).unwrap();
// Initialize the circuit
let mut circuit = TestCircuit::default();
// Compile/preproces the circuit
let (pk, vd) = circuit.compile(&pp).unwrap();

// Prover POV
let proof = {
    let mut circuit = TestCircuit {
        a: BlsScalar::from(20u64),
        b: BlsScalar::from(5u64),
        c: BlsScalar::from(25u64),
        d: BlsScalar::from(100u64),
        e: JubJubScalar::from(2u64),
        f: JubJubAffine::from(
            dusk_jubjub::GENERATOR_EXTENDED * JubJubScalar::from(2u64),
    circuit.prove(&pp, &pk, b"Test", &mut OsRng).unwrap()

// Verifier POV
let public_inputs: Vec<PublicInputValue> = vec![
        dusk_jubjub::GENERATOR_EXTENDED * JubJubScalar::from(2u64),


Benchmarks taken on Apple M1 For a circuit-size of 2^16 constraints/gates:

  • Proving time: 17.392s
  • Verification time: 10.475ms. (This time will not vary depending on the circuit-size.)

For more results, please run cargo bench to get a full report of benchmarks in respect of constraint numbers.


  • Reference implementation AztecProtocol/Barretenberg
  • FFT Module and KZG10 Module were taken and modified from zexe/zcash and scipr-lab, respectively.


This code is licensed under Mozilla Public License Version 2.0 (MPL-2.0). Please see LICENSE for further info.


Implementation designed by the dusk team.


  • If you want to contribute to this repository/project please, check CONTRIBUTING.md
  • If you want to report a bug or request a new feature addition, please open an issue on this repository.


~87K SLoC