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 369 forks source link

Deserialization of pk fails Plonk Prover #720

Closed tumberger closed 1 year ago

tumberger commented 1 year ago

Serialization and Deserialization of the prover key leads to the an error in the Plonk Prover. For recreation of the issue, please find the following complete example:

package main

import (
    "os"

    "github.com/consensys/gnark-crypto/ecc/bn254"
    "github.com/consensys/gnark/backend/plonk"
    "github.com/consensys/gnark/frontend"
    "github.com/consensys/gnark/frontend/cs/scs"
    "github.com/consensys/gnark/test"
)

// Circuit defines a simple circuit
// x**3 + x + 5 == y
type CubicCircuit struct {
    // struct tags on a variable is optional
    // default uses variable name and secret visibility.
    X frontend.Variable `gnark:"x"`
    Y frontend.Variable `gnark:",public"`
}

// Define declares the circuit constraints
// x**3 + x + 5 == y
func (circuit *CubicCircuit) Define(api frontend.API) error {
    x3 := api.Mul(circuit.X, circuit.X, circuit.X)
    api.AssertIsEqual(circuit.Y, api.Add(x3, circuit.X, 5))
    return nil
}

func main() {
    ccs, err := frontend.Compile(bn254.ID.ScalarField(), scs.NewBuilder, &CubicCircuit{})
    if err != nil {
        panic("Error in compilation of constraint system!")
    }

    srs, err := test.NewKZGSRS(ccs)
    if err != nil {
        panic("Error in KZG")
    }

    pk, _, err := plonk.Setup(ccs, srs)
    if err != nil {
        panic("Error in Setup")
    }

    // SERIALIZATION + DESERIALIZATION BEGIN

    // Open the file in write mode for pk
    fPK, err := os.OpenFile("pk.dat", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
    if err != nil {
        panic("Failed to open file for writing: " + err.Error())
    }
    defer fPK.Close()

    _, err = pk.WriteTo(fPK)
    if err != nil {
        panic("Failed to write pk to file: " + err.Error())
    }

    pk_deserialized := plonk.NewProvingKey(bn254.ID)

    // Read PK
    f, err := os.Open("pk.dat")
    if err != nil {
        panic("Failed to open file: " + err.Error())
    }

    _, err = pk_deserialized.ReadFrom(f)
    if err != nil {
        panic("Failed to read from file: " + err.Error())
    }

    // SERIALIZATION + DESERIALIZATION   END

    witness := CubicCircuit{}
    witness.X = "3"
    witness.Y = "35"

    w, err := frontend.NewWitness(&witness, bn254.ID.ScalarField())

    _, err = plonk.Prove(ccs, pk_deserialized, w)

    if err != nil {
        panic("Proving Fails! " + err.Error())
    }

}

If executed with pk, it succeeds, if executed with pk_deserialized, it fails.

For version

github.com/consensys/gnark v0.8.0 github.com/consensys/gnark-crypto v0.9.1

the following error is returned:

goroutine 964 [running]: github.com/consensys/gnark-crypto/ecc/bn254/fr/kzg.Commit({0xc00029e400?, 0x17e6b70?, 0xc000121f70?}, 0x1060ff9?, {0xc000121f70?, 0xc000121f00?, 0x0?}) /Users/serious/golang/pkg/mod/github.com/consensys/gnark-crypto@v0.9.1/ecc/bn254/fr/kzg/kzg.go:119 +0x60 github.com/consensys/gnark/internal/backend/bn254/plonk.commitToLRO.func1() /Users/serious/golang/pkg/mod/github.com/consensys/gnark@v0.8.0/internal/backend/bn254/plonk/prove.go:455 +0x7f created by github.com/consensys/gnark/internal/backend/bn254/plonk.commitToLRO /Users/serious/golang/pkg/mod/github.com/consensys/gnark@v0.8.0/internal/backend/bn254/plonk/prove.go:454 +0x1e5 panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x1235280]

goroutine 1 [running]: github.com/consensys/gnark-crypto/ecc/bn254/fr/kzg.Commit({0xc00029ec00?, 0x20?, 0xc000612260?}, 0xc00059cc10?, {0xc00059cba8?, 0xc00059cbd8?, 0xc00059cbb8?}) /Users/serious/golang/pkg/mod/github.com/consensys/gnark-crypto@v0.9.1/ecc/bn254/fr/kzg/kzg.go:119 +0x60 github.com/consensys/gnark/internal/backend/bn254/plonk.commitToLRO({0xc00029e400, 0xa, 0x20}, {0xc00029e800, 0xa, 0x20}, {0xc00029ec00, 0xa, 0x20}, 0xc000242000, ...) /Users/serious/golang/pkg/mod/github.com/consensys/gnark@v0.8.0/internal/backend/bn254/plonk/prove.go:462 +0x31b github.com/consensys/gnark/internal/backend/bn254/plonk.Prove(0xc0001f4780, 0xc000604480, {0xc000294140?, 0x2, 0x2}, {0x0, 0xc00058a330, {{0x1596a10, 0xc000017bd0}, 0xff, ...}}) /Users/serious/golang/pkg/mod/github.com/consensys/gnark@v0.8.0/internal/backend/bn254/plonk/prove.go:111 +0xc74 github.com/consensys/gnark/backend/plonk.Prove({0x159b130?, 0xc0001f4780?}, {0x15976c8?, 0xc000604480?}, {0x1599778, 0xc000592018}, {0x0?, 0xc0000061a0?, 0x200000003?})

For the develop branch, i.e. the following version,

github.com/consensys/gnark v0.7.2-0.20230605143745-6ec0af711b06 github.com/consensys/gnark-crypto v0.11.1-0.20230508024855-0cd4994b7f0b

the error returned is

09:41:14 DBG constraint system solver done nbConstraints=4 took=0.092125 panic: Proving Fails! invalid polynomial size (larger than SRS or == 0)

I suppose this is related to the logic for compressed/uncompressed serialization and deserialization. I noticed that for groth16, the API exposes both WriteTo and WriteRawTo. With uncompressed serialization for groth16 (WriteRawTo) the above example works fine.

gbotrel commented 1 year ago

thanks for reporting; can reproduce on develop, seems the KZG part is not serialized.

Also seems that at some point we regressed on the unit tests; CI should have caught that...