Prerequisites
You need a current Rust toolchain, Git, and Make. Cryptographic examples and tests should be run in release mode; the root Makefile carries the supported feature combinations.
Run the maintained examples
git clone https://github.com/dusk-network/zk-tools.git
cd zk-tools
make examplesThis builds and runs both proof-system examples in release mode with the supported BLST backend. The examples prove the same basic idea through different compilation paths.
Write one reusable circuit
A circuit implements Circuit generically over ComposerBackend. Private values are witnesses; values the verifier is expected to know are public inputs.
use dusk_zk_composer::prelude::*;
#[derive(Default)]
struct ProductCircuit {
left: BlsScalar,
right: BlsScalar,
result: BlsScalar,
}
impl Circuit for ProductCircuit {
fn circuit<B: ComposerBackend>(
&self,
composer: &mut Composer<B>,
) -> Result<(), CircuitError> {
let left = composer.append_witness(self.left);
let right = composer.append_witness(self.right);
let product = composer.gate_mul(
Constraint::new().mult(1).a(left).b(right),
);
let result = composer.append_public(self.result);
composer.assert_equal(product, result);
Ok(())
}
}This statement means: “I know left and right whose product equals the public result.” The Rust values are not the proof by themselves—the constraints are.
Choose a proof system
Groth16
Groth16 uses a circuit-specific trusted setup. Setup compiles ProductCircuit::default(), binds its canonical R1CS shape, and creates a matched prover and verifier.
let (prover, verifier) =
dusk_groth16::Compiler::trusted_setup::<ProductCircuit, _>(&mut OsRng)?;
let (proof, public_inputs) = prover.prove(&mut OsRng, &circuit)?;
let expected_public_inputs = [BlsScalar::from(42u64)];
assert_eq!(public_inputs.as_slice(), expected_public_inputs.as_slice());
verifier.verify(&proof, &expected_public_inputs)?;PLONK
PLONK compiles the circuit against reusable KZG public parameters and binds a transcript label to the compiled circuit.
let pp = PublicParameters::setup(1 << 8, &mut OsRng)?;
let (prover, verifier) =
dusk_plonk::Compiler::compile::<ProductCircuit>(&pp, b"product-circuit")?;
let (proof, public_inputs) = prover.prove(&mut OsRng, &circuit)?;
let expected_public_inputs = [BlsScalar::from(42u64)];
assert_eq!(public_inputs.as_slice(), expected_public_inputs.as_slice());
verifier.verify(&proof, &expected_public_inputs)?;Where to go next
- Learn why circuit shape must not depend on witness values.
- Compare setup and compatibility boundaries across proof systems.
- Try the Poseidon, Merkle, and Schnorr examples.
- Generate a BLS12-381 Groth16 verifier for Solidity.