Skip to content

Files

Latest commit

Mar 18, 2025
e812b78 · Mar 18, 2025

History

History

plonk

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
Mar 18, 2025
Mar 18, 2025
Mar 18, 2025

README.md

Lambdaworks Plonk Prover

A fast implementation of the Plonk zk-protocol written in Rust. This is part of the Lambdaworks zero-knowledge framework. It includes a high-level API to seamlessly build your own circuits.

This prover is still in development and may contain bugs. It is not intended to be used in production yet.

Building a circuit

Starting with an example, the following code creates a circuit with two public inputs x, y and asserts x * e = y:

let system = &mut ConstraintSystem::<FrField>::new();
let x = system.new_public_input();
let y = system.new_public_input();
let e = system.new_variable();

let z = system.mul(&x, &e);    
system.assert_eq(&y, &z);;

By placing this logic under one function, one can create "gadgets" to abstract functionality.

/// A square and multiply implementation.
pub fn pow<F: IsPrimeField>(
    system: &mut ConstraintSystem<F>,
    base: Variable,
    exponent: Variable,
) -> Variable {
    let exponent_bits = system.new_u32(&exponent);
    let mut result = system.new_constant(FE::one());

    assert_eq!(exponent_bits.len(), 32);
    for (i, bit) in exponent_bits.iter().enumerate() {
        if i != 0 {
            result = system.mul(&result, &result);
        }
        let result_times_base = system.mul(&result, &base);
        result = system.if_else(bit, &result_times_base, &result);
    }
    result
}

The core operations supported by plonk and our prove system are:

mul(var1,var2)
add(var1,var2)
add_constant(var1,constant)
div(var1,var2)
// c1 * v1 + c2 * v2 + b = w
// hinted value can be w,v1, or v2
let w = linear_combination(&v1, c1, &v2, c2, b, Option(hint))

All the variables and constants are finite fields. Abstractions like integers are not implemented yet.

Generating a proof

Setup

A setup is needed in order to generate a proof for a new circuit. The following code generates a verifying key that will be used by both the prover and the verifier:

let common = CommonPreprocessedInput::from_constraint_system(&system, &ORDER_R_MINUS_1_ROOT_UNITY);
let srs = test_srs(common.n);
let kzg = KZG::new(srs); // The commitment scheme for plonk.
let verifying_key = setup(&common, &kzg);

Prover

First, we fix values for x and e and solve the constraint system:

let inputs = HashMap::from([(x, FieldElement::from(4)), (e, FieldElement::from(3))]);
let assignments = system.solve(inputs).unwrap();

Finally, we call the prover:

let witness = Witness::new(assignments, &system);
let public_inputs = system.public_input_values(&assignments);
let prover = Prover::new(kzg.clone(), TestRandomFieldGenerator {});
let proof = prover.prove(&witness, &public_inputs, &common, &verifying_key);

Verifying a proof

Just call the verifier:

let verifier = Verifier::new(kzg);
assert!(verifier.verify(&proof, &public_inputs, &common, &verifying_key));

More info

You can find more info in the documentation.