Consensys / gnark

gnark is a fast zk-SNARK library that offers a high-level API to design circuits. The library is open source and developed under the Apache 2.0 license
https://hackmd.io/@gnark
Apache License 2.0
1.43k stars 368 forks source link

The benchmark between Plonk and Groth16 problem #374

Closed LBruyne closed 2 years ago

LBruyne commented 2 years ago

I'm wondering if the PLONK implementation has a optimization space.

This is a benchmark result from AZTEC Team.

MiMC Winner: PLONK
TurboPLONK performs at extraordinary speed on MiMC, outpacing even Groth16 by a factor of ~2.5x

image

We can see that PLONK perforrms better when proving a MiMC hash circuit.

However, in the current implementation of this repo, I obtain the result on the contrary. The benchmark displays that Groth16 has a better performance.

Have I made a mistake?

Here is the result: image

And here is the benchmark I run:

type MimcHashCircuit struct {
    PreImage  frontend.Variable
    HashValue frontend.Variable `gnark:",public"`
}

// Define hashValue = mimc(preImage)
func (circuit *MimcHashCircuit) Define(api frontend.API) error {
    // hash function
    _mimc, _ := mimc.NewMiMC(api)
    _mimc.Write(circuit.PreImage)
    api.AssertIsEqual(circuit.HashValue, _mimc.Sum())
    return nil
}

func Benchmark_MimcHashGrot16Circuit(b *testing.B) {
    var circuit MimcHashCircuit
    // compile to get r1cs
    _r1cs, _ := frontend.Compile(ecc.BN254, r1cs.NewBuilder, &circuit)

    // Setup
    pk, _, _ := groth16.Setup(_r1cs)

    // Prove
    b.ResetTimer()
    validWitness, _ := frontend.NewWitness(&MimcHashCircuit{
        PreImage:  frontend.Variable("16130099170765464552823636852555369511329944820189892919423002775646948828469"),
        HashValue: frontend.Variable("8674594860895598770446879254410848023850744751986836044725552747672873438975"),
    }, ecc.BN254)
    _, _ = groth16.Prove(_r1cs, pk, validWitness)
    b.StopTimer()
}

func Benchmark_MimcHashPlonkCircuit(b *testing.B) {
    var circuit MimcHashCircuit
    // compile to get r1cs
    _ccs, _ := frontend.Compile(ecc.BN254, scs.NewBuilder, &circuit)

    // Setup
    _r1cs := _ccs.(*cs.SparseR1CS)
    srs, _ := test.NewKZGSRS(_r1cs)
    pk, _, _ := plonk.Setup(_ccs, srs)

    // Prove
    b.ResetTimer()
    validWitness, _ := frontend.NewWitness(&MimcHashCircuit{
        PreImage:  frontend.Variable("16130099170765464552823636852555369511329944820189892919423002775646948828469"),
        HashValue: frontend.Variable("8674594860895598770446879254410848023850744751986836044725552747672873438975"),
    }, ecc.BN254)
    _, _ = plonk.Prove(_r1cs, pk, validWitness)
    b.StopTimer()
}
gbotrel commented 2 years ago

Hi -- so, PlonK implemented in gnark is "as in the paper". The one you reference is "TurboPLONK". There are couple of work that did build upon PlonK the past year or so (TurboPlonk, UltraPlonk, and recently HyperPlonk).

On a "vanilla plonk" circuit, Groth16 will always outperform Plonk prover.

gbotrel commented 2 years ago

(also, it is hard to benchmark across implementation and proof system :-) )

LBruyne commented 2 years ago

Thank you!