diff --git a/gnarkx/succinct/build.go b/gnarkx/succinct/build.go index cea3e9ad6..e40fb233a 100644 --- a/gnarkx/succinct/build.go +++ b/gnarkx/succinct/build.go @@ -81,7 +81,6 @@ func (build *CircuitBuild) Export() { fmt.Println("Failed to export solidity verifier:", err) return } - } // ImportCircuitBuild imports the R1CS, proving key, and verifying key from files. diff --git a/go.mod b/go.mod index 80d7dadbf..f5fbbdf88 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,10 @@ require ( github.com/consensys/gnark v0.9.1 github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb github.com/ethereum/go-ethereum v1.12.0 + github.com/pkg/errors v0.9.1 + github.com/rs/zerolog v1.31.0 github.com/stretchr/testify v1.8.4 - github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231013210054-89b5a01e4b4b + github.com/succinctlabs/gnark-plonky2-verifier v0.0.3 ) require ( @@ -16,6 +18,7 @@ require ( github.com/blang/semver/v4 v4.0.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-ignition-verifier v0.0.0-20230527014722-10693546ab33 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect @@ -31,7 +34,6 @@ require ( github.com/mattn/go-isatty v0.0.19 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rs/zerolog v1.31.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/tklauser/go-sysconf v0.3.5 // indirect github.com/tklauser/numcpus v0.2.2 // indirect diff --git a/go.sum b/go.sum index 7da9703ee..c71890c23 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,6 @@ github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIO github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/bits-and-blooms/bitset v1.9.0 h1:g1YivPG8jOtrN013Fe8OBXubkiTwvm7/vG2vXz03ANU= -github.com/bits-and-blooms/bitset v1.9.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -20,14 +18,12 @@ github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoG github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark v0.9.0 h1:OoOr0Q771mQINVdP3s1AF2Rs1y8gtXhWVkadz/9KmZc= -github.com/consensys/gnark v0.9.0/go.mod h1:Sy9jJjIaGJFfNeupyNOR9Ei2IbAB6cfCO78DfG27YvM= github.com/consensys/gnark v0.9.1 h1:aTwBp5469MY/2jNrf4ABrqHRW3+JytfkADdw4ZBY7T0= github.com/consensys/gnark v0.9.1/go.mod h1:udWvWGXnfBE7mn7BsNoGAvZDnUhcONBEtNijvVjfY80= -github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= -github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb h1:f0BMgIjhZy4lSRHCXFbQst85f5agZAjtDMixQqBWNpc= github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/consensys/gnark-ignition-verifier v0.0.0-20230527014722-10693546ab33 h1:z42ewLaLxoTYeQ17arcF4WExZc/eSaN3YVlF7eEaPt4= +github.com/consensys/gnark-ignition-verifier v0.0.0-20230527014722-10693546ab33/go.mod h1:JdKor28c/KR4BbznP88bz8AAvnCgovzrB3KWsiR7lwk= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -100,10 +96,8 @@ github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231012010246-940c81b212ec h1:xMlCVbabspLQ/sukwyu1WQzznh476x4O2/ee5X8igZ0= -github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231012010246-940c81b212ec/go.mod h1:33fqngzJywBvG2tiETIPCFUCnRGkyTOybblVB9M7aOs= -github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231013210054-89b5a01e4b4b h1:kcqBBMCEhDG6cNKAosD+7OG97jkyih9/G7GuNMrW6A8= -github.com/succinctlabs/gnark-plonky2-verifier v0.0.0-20231013210054-89b5a01e4b4b/go.mod h1:33fqngzJywBvG2tiETIPCFUCnRGkyTOybblVB9M7aOs= +github.com/succinctlabs/gnark-plonky2-verifier v0.0.3 h1:hlciV1yXKJ8aJE1IOHp7Ptt2a53PWeig4Vam1g+JoT0= +github.com/succinctlabs/gnark-plonky2-verifier v0.0.3/go.mod h1:c144MdRU1b0w/khA+lTrTFGcRHiKp1obwv8VGv/LQzI= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= @@ -116,7 +110,6 @@ golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/plonky2x/.gitignore b/plonky2x/.gitignore index 19df4a90a..ffbe4e7f8 100644 --- a/plonky2x/.gitignore +++ b/plonky2x/.gitignore @@ -1,4 +1,5 @@ verifier-build/ core/wrapped/ *tar.gz -verifier-build-groth16/ \ No newline at end of file +verifier-build-groth16/ +proof_with_witness.json \ No newline at end of file diff --git a/plonky2x/verifier/circuit.go b/plonky2x/verifier/circuit.go deleted file mode 100644 index d542d2311..000000000 --- a/plonky2x/verifier/circuit.go +++ /dev/null @@ -1,154 +0,0 @@ -package main - -import ( - "fmt" - "math/big" - "os" - "time" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend/plonk" - "github.com/consensys/gnark/constraint" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/frontend/cs/scs" - "github.com/consensys/gnark/logger" - "github.com/consensys/gnark/test" - "github.com/succinctlabs/gnark-plonky2-verifier/types" - "github.com/succinctlabs/gnark-plonky2-verifier/variables" - "github.com/succinctlabs/gnark-plonky2-verifier/verifier" -) - -type Plonky2xVerifierCircuit struct { - // A digest of the plonky2x circuit that is being verified. - VerifierDigest frontend.Variable `gnark:"verifierDigest,public"` - - // The input hash is the hash of all onchain inputs into the function. - InputHash frontend.Variable `gnark:"inputHash,public"` - - // The output hash is the hash of all outputs from the function. - OutputHash frontend.Variable `gnark:"outputHash,public"` - - // Private inputs to the circuit - ProofWithPis variables.ProofWithPublicInputs - VerifierData variables.VerifierOnlyCircuitData - - // Circuit configuration that is not part of the circuit itself. - CommonCircuitData types.CommonCircuitData `gnark:"-"` -} - -func (c *Plonky2xVerifierCircuit) Define(api frontend.API) error { - // initialize the verifier chip - verifierChip := verifier.NewVerifierChip(api, c.CommonCircuitData) - // verify the plonky2 proof - verifierChip.Verify(c.ProofWithPis.Proof, c.ProofWithPis.PublicInputs, c.VerifierData) - - // We assume that the publicInputs have 64 bytes - // publicInputs[0:32] is a big-endian representation of a SHA256 hash that has been truncated to 253 bits. - // Note that this truncation happens in the `WrappedCircuit` when computing the `input_hash` - // The reason for truncation is that we only want 1 public input on-chain for the input hash - // to save on gas costs - publicInputs := c.ProofWithPis.PublicInputs - - if len(publicInputs) != 64 { - return fmt.Errorf("expected 64 public inputs, got %d", len(publicInputs)) - } - - inputDigest := frontend.Variable(0) - for i := 0; i < 32; i++ { - pubByte := publicInputs[31-i].Limb - inputDigest = api.Add(inputDigest, api.Mul(pubByte, frontend.Variable(new(big.Int).Lsh(big.NewInt(1), uint(8*i))))) - - } - api.AssertIsEqual(c.InputHash, inputDigest) - - outputDigest := frontend.Variable(0) - for i := 0; i < 32; i++ { - pubByte := publicInputs[63-i].Limb - outputDigest = api.Add(outputDigest, api.Mul(pubByte, frontend.Variable(new(big.Int).Lsh(big.NewInt(1), uint(8*i))))) - } - api.AssertIsEqual(c.OutputHash, outputDigest) - - // We have to assert that the VerifierData we verified the proof with - // matches the VerifierDigest public input. - api.AssertIsEqual(c.VerifierDigest, c.VerifierData.CircuitDigest) - - return nil -} - -func CompileVerifierCircuit(dummyCircuitPath string) (constraint.ConstraintSystem, plonk.ProvingKey, plonk.VerifyingKey, error) { - log := logger.Logger() - verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData( - types.ReadVerifierOnlyCircuitData(dummyCircuitPath + "/verifier_only_circuit_data.json"), - ) - proofWithPis := variables.DeserializeProofWithPublicInputs( - types.ReadProofWithPublicInputs(dummyCircuitPath + "/proof_with_public_inputs.json"), - ) - commonCircuitData := types.ReadCommonCircuitData(dummyCircuitPath + "/common_circuit_data.json") - - circuit := Plonky2xVerifierCircuit{ - ProofWithPis: proofWithPis, - VerifierData: verifierOnlyCircuitData, - VerifierDigest: new(frontend.Variable), - InputHash: new(frontend.Variable), - OutputHash: new(frontend.Variable), - CommonCircuitData: commonCircuitData, - } - r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), scs.NewBuilder, &circuit) - if err != nil { - return nil, nil, nil, fmt.Errorf("failed to compile circuit: %w", err) - } - - log.Info().Msg("Running circuit setup") - start := time.Now() - srs, err := test.NewKZGSRS(r1cs) - if err != nil { - panic(err) - } - pk, vk, err := plonk.Setup(r1cs, srs) - if err != nil { - return nil, nil, nil, err - } - elapsed := time.Since(start) - log.Info().Msg("Successfully ran circuit setup, time: " + elapsed.String()) - - return r1cs, pk, vk, nil -} - -func SaveVerifierCircuit(path string, r1cs constraint.ConstraintSystem, pk plonk.ProvingKey, vk plonk.VerifyingKey) error { - log := logger.Logger() - os.MkdirAll(path, 0755) - log.Info().Msg("Saving circuit constraints to " + path + "/r1cs.bin") - r1csFile, err := os.Create(path + "/r1cs.bin") - if err != nil { - return fmt.Errorf("failed to create r1cs file: %w", err) - } - start := time.Now() - r1cs.WriteTo(r1csFile) - r1csFile.Close() - elapsed := time.Since(start) - log.Debug().Msg("Successfully saved circuit constraints, time: " + elapsed.String()) - - log.Info().Msg("Saving proving key to " + path + "/pk.bin") - pkFile, err := os.Create(path + "/pk.bin") - if err != nil { - return fmt.Errorf("failed to create pk file: %w", err) - } - start = time.Now() - pk.WriteRawTo(pkFile) - pkFile.Close() - elapsed = time.Since(start) - log.Debug().Msg("Successfully saved proving key, time: " + elapsed.String()) - - log.Info().Msg("Saving verifying key to " + path + "/vk.bin") - vkFile, err := os.Create(path + "/vk.bin") - if err != nil { - return fmt.Errorf("failed to create vk file: %w", err) - } - start = time.Now() - vk.WriteRawTo(vkFile) - vkFile.Close() - elapsed = time.Since(start) - log.Info().Msg("Successfully saved verifying key, time: " + elapsed.String()) - - return nil -} diff --git a/plonky2x/verifier/cli.go b/plonky2x/verifier/cli.go index 2d59cf318..f2a0a4226 100644 --- a/plonky2x/verifier/cli.go +++ b/plonky2x/verifier/cli.go @@ -1,15 +1,12 @@ package main import ( - "bufio" _ "embed" "flag" - "fmt" "os" - "strings" - "github.com/consensys/gnark/backend/plonk" "github.com/consensys/gnark/logger" + "github.com/succinctlabs/succinctx/plonky2x/verifier/system" ) func main() { @@ -17,112 +14,65 @@ func main() { dataPath := flag.String("data", "", "data directory") proofFlag := flag.Bool("prove", false, "create a proof") verifyFlag := flag.Bool("verify", false, "verify a proof") - compileFlag := flag.Bool("compile", false, "Compile and save the universal verifier circuit") + compileFlag := flag.Bool("compile", false, "compile and save the universal verifier circuit") + exportFlag := flag.Bool("export", false, "export the Solidity verifier") + systemFlag := flag.String("system", "plonk", "proving system to use (plonk, groth16)") contractFlag := flag.Bool("contract", true, "Generate solidity contract") flag.Parse() - log := logger.Logger() + logger := logger.Logger() if *circuitPath == "" { - log.Info().Msg("no circuitPath flag found, so user must input circuitPath via stdin") + logger.Info().Msg("no circuitPath flag found, so user must input circuitPath via stdin") } - if *dataPath == "" { - log.Error().Msg("please specify a path to data dir (where the compiled gnark circuit data will be)") + logger.Error().Msg("please specify a path to data dir (where the compiled gnark circuit data will be)") os.Exit(1) } + logger.Debug().Msg("Circuit path: " + *circuitPath) + logger.Debug().Msg("Data path: " + *dataPath) - log.Debug().Msg("Circuit path: " + *circuitPath) - log.Debug().Msg("Data path: " + *dataPath) + var s system.ProvingSystem + if *systemFlag == "groth16" { + // https://github.com/succinctlabs/gnark-plonky2-verifier/blob/c01f530fe1d0107cc20da226cfec541ece9fb882/goldilocks/base.go#L131 + os.Setenv("USE_BIT_DECOMPOSITION_RANGE_CHECK", "true") + s = system.NewGroth16System(logger, *circuitPath, *dataPath) + } else if *systemFlag == "plonk" { + s = system.NewPlonkSystem(logger, *circuitPath, *dataPath) + } else { + logger.Error().Msg("invalid proving system") + os.Exit(1) + } if *compileFlag { - log.Info().Msg("compiling verifier circuit") - r1cs, pk, vk, err := CompileVerifierCircuit("./data/dummy") + err := s.Compile() if err != nil { - log.Error().Msg("failed to compile verifier circuit:" + err.Error()) + logger.Error().Msg("failed to compile circuit:" + err.Error()) os.Exit(1) } - err = SaveVerifierCircuit(*dataPath, r1cs, pk, vk) - if err != nil { - log.Error().Msg("failed to save verifier circuit:" + err.Error()) - os.Exit(1) - } - - if *contractFlag { - log.Info().Msg("generating solidity contract") - err := ExportIFunctionVerifierSolidity(*dataPath, vk) - if err != nil { - log.Error().Msg("failed to generate solidity contract:" + err.Error()) - os.Exit(1) - } - } } if *proofFlag { - log.Info().Msg("loading the plonk proving key, circuit data and verifying key") - r1cs, pk, err := LoadProverData(*dataPath) - if err != nil { - log.Err(err).Msg("failed to load the verifier circuit") - os.Exit(1) - } - vk, err := LoadVerifierKey(*dataPath) - if err != nil { - log.Err(err).Msg("failed to load the verifier key") - os.Exit(1) - } - - // If the circuitPath is "" and not provided as part of the CLI flags, then we wait - // for user input. - if *circuitPath == "" { - log.Info().Msg("Waiting for user to provide circuitPath from stdin") - reader := bufio.NewReader(os.Stdin) - str, err := reader.ReadString('\n') - if err != nil { - log.Err(err).Msg("failed to parse the user provided circuitPath") - } - trimmed := strings.TrimSuffix(str, "\n") - circuitPath = &trimmed - } - - log.Info().Msg(fmt.Sprintf("Generating the proof with circuitPath %s", *circuitPath)) - proof, publicWitness, err := Prove(*circuitPath, r1cs, pk) - if err != nil { - log.Err(err).Msg("failed to create the proof") - os.Exit(1) - } - - log.Info().Msg("Verifying proof") - err = plonk.Verify(proof, vk, publicWitness) + err := s.Prove() if err != nil { - log.Err(err).Msg("failed to verify proof") + logger.Error().Msg("failed to create proof:" + err.Error()) os.Exit(1) } - log.Info().Msg("Successfully verified proof") } if *verifyFlag { - log.Info().Msg("loading the proof, verifying key and public inputs") - vk, err := LoadVerifierKey(*dataPath) + err := s.Verify() if err != nil { - log.Err(err).Msg("failed to load the verifier key") - os.Exit(1) - } - publicWitness, err := LoadPublicWitness(*circuitPath) - if err != nil { - log.Err(err).Msg("failed to load the public witness") + logger.Error().Msg("failed to verify proof:" + err.Error()) os.Exit(1) } + } - proof, err := LoadProof() - if err != nil { - log.Err(err).Msg("failed to load the proof") - os.Exit(1) - } - err = plonk.Verify(proof, vk, publicWitness) + if *exportFlag || *contractFlag { + err := s.Export() if err != nil { - log.Err(err).Msg("failed to verify proof") + logger.Error().Msg("failed to export verifier:" + err.Error()) os.Exit(1) } - log.Info().Msg("Successfully verified proof") } } diff --git a/plonky2x/verifier/prover.go b/plonky2x/verifier/prover.go deleted file mode 100644 index 8430466fe..000000000 --- a/plonky2x/verifier/prover.go +++ /dev/null @@ -1,186 +0,0 @@ -package main - -import ( - "bufio" - "encoding/json" - "fmt" - "math/big" - "os" - "time" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend/plonk" - plonk_bn254 "github.com/consensys/gnark/backend/plonk/bn254" - "github.com/ethereum/go-ethereum/common/hexutil" - - "github.com/consensys/gnark/backend/witness" - "github.com/consensys/gnark/constraint" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/logger" - gnark_verifier_types "github.com/succinctlabs/gnark-plonky2-verifier/types" - "github.com/succinctlabs/gnark-plonky2-verifier/variables" - - "github.com/succinctlabs/succinctx/gnarkx/types" -) - -func LoadProverData(path string) (constraint.ConstraintSystem, plonk.ProvingKey, error) { - log := logger.Logger() - r1csFile, err := os.Open(path + "/r1cs.bin") - if err != nil { - return nil, nil, fmt.Errorf("failed to open r1cs file: %w", err) - } - r1cs := plonk.NewCS(ecc.BN254) - start := time.Now() - r1csReader := bufio.NewReader(r1csFile) - _, err = r1cs.ReadFrom(r1csReader) - if err != nil { - return nil, nil, fmt.Errorf("failed to read r1cs file: %w", err) - } - r1csFile.Close() - elapsed := time.Since(start) - log.Debug().Msg("Successfully loaded constraint system, time: " + elapsed.String()) - - pkFile, err := os.Open(path + "/pk.bin") - if err != nil { - return nil, nil, fmt.Errorf("failed to open pk file: %w", err) - } - pk := plonk.NewProvingKey(ecc.BN254) - start = time.Now() - pkReader := bufio.NewReader(pkFile) - _, err = pk.ReadFrom(pkReader) - if err != nil { - return nil, nil, fmt.Errorf("failed to read pk file: %w", err) - } - pkFile.Close() - elapsed = time.Since(start) - log.Debug().Msg("Successfully loaded proving key, time: " + elapsed.String()) - - return r1cs, pk, nil -} - -func GetInputHashOutputHash(proofWithPis gnark_verifier_types.ProofWithPublicInputsRaw) (*big.Int, *big.Int) { - publicInputs := proofWithPis.PublicInputs - if len(publicInputs) != 64 { - panic("publicInputs must be 64 bytes") - } - publicInputsBytes := make([]byte, 64) - for i, v := range publicInputs { - publicInputsBytes[i] = byte(v & 0xFF) - } - inputHash := new(big.Int).SetBytes(publicInputsBytes[0:32]) - outputHash := new(big.Int).SetBytes(publicInputsBytes[32:64]) - if inputHash.BitLen() > 253 { - panic("inputHash must be at most 253 bits") - } - if outputHash.BitLen() > 253 { - panic("outputHash must be at most 253 bits") - } - return inputHash, outputHash -} - -func Prove(circuitPath string, r1cs constraint.ConstraintSystem, pk plonk.ProvingKey) (plonk.Proof, witness.Witness, error) { - log := logger.Logger() - - verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData( - gnark_verifier_types.ReadVerifierOnlyCircuitData(circuitPath + "/verifier_only_circuit_data.json"), - ) - proofWithPis := gnark_verifier_types.ReadProofWithPublicInputs(circuitPath + "/proof_with_public_inputs.json") - proofWithPisVariable := variables.DeserializeProofWithPublicInputs(proofWithPis) - - inputHash, outputHash := GetInputHashOutputHash(proofWithPis) - - // Circuit assignment - assignment := &Plonky2xVerifierCircuit{ - ProofWithPis: proofWithPisVariable, - VerifierData: verifierOnlyCircuitData, - VerifierDigest: verifierOnlyCircuitData.CircuitDigest, - InputHash: frontend.Variable(inputHash), - OutputHash: frontend.Variable(outputHash), - } - - log.Debug().Msg("Generating witness") - start := time.Now() - witness, err := frontend.NewWitness(assignment, ecc.BN254.ScalarField()) - if err != nil { - return nil, nil, fmt.Errorf("failed to generate witness: %w", err) - } - elapsed := time.Since(start) - log.Debug().Msg("Successfully generated witness, time: " + elapsed.String()) - - log.Debug().Msg("Creating proof") - start = time.Now() - proof, err := plonk.Prove(r1cs, pk, witness) - if err != nil { - return nil, nil, fmt.Errorf("failed to create proof: %w", err) - } - elapsed = time.Since(start) - log.Info().Msg("Successfully created proof, time: " + elapsed.String()) - - _proof := proof.(*plonk_bn254.Proof) - log.Info().Msg("Saving proof to proof.json") - jsonProof, err := json.Marshal(types.ProofResult{ - // Output will be filled in by plonky2x CLI - Output: []byte{}, - Proof: _proof.MarshalSolidity(), - }) - if err != nil { - return nil, nil, fmt.Errorf("failed to marshal proof: %w", err) - } - proofFile, err := os.Create("proof.json") - if err != nil { - return nil, nil, fmt.Errorf("failed to create proof file: %w", err) - } - _, err = proofFile.Write(jsonProof) - if err != nil { - return nil, nil, fmt.Errorf("failed to write proof file: %w", err) - } - proofFile.Close() - log.Info().Msg("Successfully saved proof") - - // Write proof with all the public inputs and save to disk. - jsonProofWithWitness, err := json.Marshal(struct { - InputHash hexutil.Bytes `json:"input_hash"` - OutputHash hexutil.Bytes `json:"output_hash"` - VerifierDigest hexutil.Bytes `json:"verifier_digest"` - Proof hexutil.Bytes `json:"proof"` - }{ - InputHash: inputHash.Bytes(), - OutputHash: outputHash.Bytes(), - VerifierDigest: (verifierOnlyCircuitData.CircuitDigest).(*big.Int).Bytes(), - Proof: _proof.MarshalSolidity(), - }) - if err != nil { - return nil, nil, fmt.Errorf("failed to marshal proof with witness: %w", err) - } - proofFile, err = os.Create("proof_with_witness.json") - if err != nil { - return nil, nil, fmt.Errorf("failed to create proof_with_witness file: %w", err) - } - _, err = proofFile.Write(jsonProofWithWitness) - if err != nil { - return nil, nil, fmt.Errorf("failed to write proof_with_witness file: %w", err) - } - proofFile.Close() - log.Info().Msg("Proof with witness") - log.Info().Msg(string(jsonProofWithWitness)) - log.Info().Msg("Successfully saved proof_with_witness") - - publicWitness, err := witness.Public() - if err != nil { - return nil, nil, fmt.Errorf("failed to get public witness: %w", err) - } - - log.Info().Msg("Saving public witness to public_witness.bin") - witnessFile, err := os.Create("public_witness.bin") - if err != nil { - return nil, nil, fmt.Errorf("failed to create public witness file: %w", err) - } - _, err = publicWitness.WriteTo(witnessFile) - if err != nil { - return nil, nil, fmt.Errorf("failed to write public witness file: %w", err) - } - witnessFile.Close() - log.Info().Msg("Successfully saved public witness") - - return proof, publicWitness, nil -} diff --git a/plonky2x/verifier/system/circuit.go b/plonky2x/verifier/system/circuit.go new file mode 100644 index 000000000..096c1bad4 --- /dev/null +++ b/plonky2x/verifier/system/circuit.go @@ -0,0 +1,68 @@ +package system + +import ( + "fmt" + "math/big" + + "github.com/consensys/gnark/frontend" + "github.com/succinctlabs/gnark-plonky2-verifier/types" + "github.com/succinctlabs/gnark-plonky2-verifier/variables" + "github.com/succinctlabs/gnark-plonky2-verifier/verifier" +) + +type VerifierCircuit struct { + // A digest of the plonky2x circuit that is being verified. + VerifierDigest frontend.Variable `gnark:"verifierDigest,public"` + + // The input hash is the hash of all onchain inputs into the function. + InputHash frontend.Variable `gnark:"inputHash,public"` + + // The output hash is the hash of all outputs from the function. + OutputHash frontend.Variable `gnark:"outputHash,public"` + + // Private inputs to the circuit + ProofWithPis variables.ProofWithPublicInputs + VerifierData variables.VerifierOnlyCircuitData + + // Circuit configuration that is not part of the circuit itself. + CommonCircuitData types.CommonCircuitData `gnark:"-"` +} + +func (c *VerifierCircuit) Define(api frontend.API) error { + // initialize the verifier chip + verifierChip := verifier.NewVerifierChip(api, c.CommonCircuitData) + // verify the plonky2 proofD + verifierChip.Verify(c.ProofWithPis.Proof, c.ProofWithPis.PublicInputs, c.VerifierData) + + // We assume that the publicInputs have 64 bytes + // publicInputs[0:32] is a big-endian representation of a SHA256 hash that has been truncated to 253 bits. + // Note that this truncation happens in the `WrappedCircuit` when computing the `input_hash` + // The reason for truncation is that we only want 1 public input on-chain for the input hash + // to save on gas costs + publicInputs := c.ProofWithPis.PublicInputs + + if len(publicInputs) != 64 { + return fmt.Errorf("expected 64 public inputs, got %d", len(publicInputs)) + } + + inputDigest := frontend.Variable(0) + for i := 0; i < 32; i++ { + pubByte := publicInputs[31-i].Limb + inputDigest = api.Add(inputDigest, api.Mul(pubByte, frontend.Variable(new(big.Int).Lsh(big.NewInt(1), uint(8*i))))) + + } + api.AssertIsEqual(c.InputHash, inputDigest) + + outputDigest := frontend.Variable(0) + for i := 0; i < 32; i++ { + pubByte := publicInputs[63-i].Limb + outputDigest = api.Add(outputDigest, api.Mul(pubByte, frontend.Variable(new(big.Int).Lsh(big.NewInt(1), uint(8*i))))) + } + api.AssertIsEqual(c.OutputHash, outputDigest) + + // We have to assert that the VerifierData we verified the proof with + // matches the VerifierDigest public input. + api.AssertIsEqual(c.VerifierDigest, c.VerifierData.CircuitDigest) + + return nil +} diff --git a/plonky2x/verifier/verifier_test.go b/plonky2x/verifier/system/circuit_test.go similarity index 95% rename from plonky2x/verifier/verifier_test.go rename to plonky2x/verifier/system/circuit_test.go index 8d36b77eb..71c43a655 100644 --- a/plonky2x/verifier/verifier_test.go +++ b/plonky2x/verifier/system/circuit_test.go @@ -1,4 +1,4 @@ -package main +package system import ( "testing" @@ -16,8 +16,8 @@ func TestPlonky2xVerifierCircuit(t *testing.T) { assert := test.NewAssert(t) testCase := func(option int64) error { - dummyCircuitPath := "./data/dummy" - circuitPath := "./data/test_circuit" + dummyCircuitPath := "../data/dummy" + circuitPath := "../data/test_circuit" verifierOnlyCircuitDataDummy := variables.DeserializeVerifierOnlyCircuitData( types.ReadVerifierOnlyCircuitData(dummyCircuitPath + "/verifier_only_circuit_data.json"), @@ -27,7 +27,7 @@ func TestPlonky2xVerifierCircuit(t *testing.T) { ) commonCircuitDataDummy := types.ReadCommonCircuitData(dummyCircuitPath + "/common_circuit_data.json") - circuit := Plonky2xVerifierCircuit{ + circuit := VerifierCircuit{ ProofWithPis: proofWithPisDummy, VerifierData: verifierOnlyCircuitDataDummy, VerifierDigest: frontend.Variable(0), // Can be empty for defining the circuit @@ -44,7 +44,7 @@ func TestPlonky2xVerifierCircuit(t *testing.T) { proofWithPisVariable := variables.DeserializeProofWithPublicInputs(proofWithPis) - witness := Plonky2xVerifierCircuit{ + witness := VerifierCircuit{ ProofWithPis: proofWithPisVariable, VerifierData: verifierOnlyCircuitData, VerifierDigest: verifierOnlyCircuitData.CircuitDigest, diff --git a/plonky2x/verifier/system/groth16.go b/plonky2x/verifier/system/groth16.go new file mode 100644 index 000000000..efc879071 --- /dev/null +++ b/plonky2x/verifier/system/groth16.go @@ -0,0 +1,469 @@ +package system + +import ( + "bufio" + "bytes" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "math/big" + "os" + "strings" + "time" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/backend/groth16" + groth16_bn254 "github.com/consensys/gnark/backend/groth16/bn254" + "github.com/consensys/gnark/backend/witness" + "github.com/consensys/gnark/constraint" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pkg/errors" + "github.com/rs/zerolog" + + gnark_verifier_types "github.com/succinctlabs/gnark-plonky2-verifier/types" + "github.com/succinctlabs/gnark-plonky2-verifier/variables" +) + +type Groth16System struct { + logger zerolog.Logger + circuitPath string + dataPath string +} + +func NewGroth16System(logger zerolog.Logger, circuitPath string, dataPath string) *Groth16System { + return &Groth16System{ + logger: logger, + circuitPath: circuitPath, + dataPath: dataPath, + } +} + +func (s *Groth16System) Compile() error { + s.logger.Info().Msg("starting compiling verifier circuit") + + r1cs, pk, vk, err := s.CompileVerifierCircuit() + if err != nil { + return errors.Wrap(err, "compile verifier circuit") + } + + err = s.SaveVerifierCircuit(r1cs, pk, vk) + if err != nil { + return errors.Wrap(err, "save verifier circuit") + } + + s.logger.Info().Msg("successfully compiled verifier circuit") + + return nil +} + +func (s *Groth16System) Prove() error { + s.logger.Info().Msg("starting prove -- loading verifier circuit and proving key") + + r1cs, err := s.LoadCircuit() + if err != nil { + return errors.Wrap(err, "load the verifier circuit") + } + pk, err := s.LoadProvingKey() + if err != nil { + return errors.Wrap(err, "load the proving key") + } + + // If the circuitPath is "" and not provided as part of the CLI flags, then we wait + // for user input. + if s.circuitPath == "" { + s.logger.Info().Msg("no circuitPath flag found, so user must input circuitPath via stdin") + reader := bufio.NewReader(os.Stdin) + str, err := reader.ReadString('\n') + if err != nil { + return errors.Wrap(err, "read circuitPath from stdin") + } + trimmed := strings.TrimSuffix(str, "\n") + s.circuitPath = trimmed + } + + s.logger.Info().Msgf("generating proof with circuit path %v", s.circuitPath) + _, _, err = s.ProveCircuit(r1cs, pk) + if err != nil { + return errors.Wrap(err, "create proof") + } + + s.logger.Info().Msg("successfully created proof") + + return nil +} + +func (s *Groth16System) Verify() error { + s.logger.Info().Msg("starting verify -- loading verifier key, public witness, and proof") + + vk, err := s.LoadVerifierKey() + if err != nil { + return errors.Wrap(err, "load verifier key") + } + + proof, err := s.LoadProof() + if err != nil { + return errors.Wrap(err, "load proof") + } + + publicWitness, err := s.LoadPublicWitness() + if err != nil { + return errors.Wrap(err, "load public witness") + } + + err = groth16.Verify(proof, vk, publicWitness) + if err != nil { + return errors.Wrap(err, "verify proof") + } + + s.logger.Info().Msg("successfully verified proof") + + return nil +} + +func (s *Groth16System) Export() error { + s.logger.Info().Msg("starting export -- loading verifier key and exporting Verifier solidity") + + vk, err := s.LoadVerifierKey() + if err != nil { + return errors.Wrap(err, "load verifier key") + } + + err = s.ExportVerifierJSON(vk) + if err != nil { + return errors.Wrap(err, "export Verifier JSON") + } + + err = s.ExportVerifierSolidity(vk) + if err != nil { + return errors.Wrap(err, "export Verifier solidity") + } + + s.logger.Info().Msg("successfully exported Verifier solidity") + + return nil +} + +func (s *Groth16System) CompileVerifierCircuit() (constraint.ConstraintSystem, groth16.ProvingKey, groth16.VerifyingKey, error) { + verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData( + gnark_verifier_types.ReadVerifierOnlyCircuitData(s.circuitPath + "/verifier_only_circuit_data.json"), + ) + proofWithPis := variables.DeserializeProofWithPublicInputs( + gnark_verifier_types.ReadProofWithPublicInputs(s.circuitPath + "/proof_with_public_inputs.json"), + ) + commonCircuitData := gnark_verifier_types.ReadCommonCircuitData(s.circuitPath + "/common_circuit_data.json") + + circuit := VerifierCircuit{ + ProofWithPis: proofWithPis, + VerifierData: verifierOnlyCircuitData, + VerifierDigest: new(frontend.Variable), + InputHash: new(frontend.Variable), + OutputHash: new(frontend.Variable), + CommonCircuitData: commonCircuitData, + } + r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit) + if err != nil { + return nil, nil, nil, errors.Wrap(err, "compile verifier circuit") + } + + s.logger.Info().Msg("Running circuit setup") + start := time.Now() + pk, vk, err := groth16.Setup(r1cs) + if err != nil { + return nil, nil, nil, err + } + elapsed := time.Since(start) + s.logger.Info().Msg("Successfully ran circuit setup in " + elapsed.String()) + + return r1cs, pk, vk, nil +} + +func (s *Groth16System) SaveVerifierCircuit(r1cs constraint.ConstraintSystem, pk groth16.ProvingKey, vk groth16.VerifyingKey) error { + os.MkdirAll(s.dataPath, 0755) + + r1csFile, err := os.Create(s.dataPath + "/r1cs.bin") + if err != nil { + return errors.Wrap(err, "create r1cs file") + } + r1cs.WriteTo(r1csFile) + r1csFile.Close() + s.logger.Info().Msg("Successfully saved circuit constraints to r1cs.bin") + + s.logger.Info().Msg("Saving proving key to pk.bin") + pkFile, err := os.Create(s.dataPath + "/pk.bin") + if err != nil { + return errors.Wrap(err, "create pk file") + } + pk.WriteRawTo(pkFile) + pkFile.Close() + s.logger.Info().Msg("Successfully saved proving key to pk.bin") + + vkFile, err := os.Create(s.dataPath + "/vk.bin") + if err != nil { + return errors.Wrap(err, "create vk file") + } + vk.WriteRawTo(vkFile) + vkFile.Close() + s.logger.Info().Msg("Successfully saved verifying key to vk.bin") + + return nil +} + +func (s *Groth16System) ProveCircuit(r1cs constraint.ConstraintSystem, pk groth16.ProvingKey) (groth16.Proof, witness.Witness, error) { + s.logger.Info().Msg("Loading verifier only circuit data and proof with public inputs in path " + s.circuitPath) + verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData( + gnark_verifier_types.ReadVerifierOnlyCircuitData(s.circuitPath + "/verifier_only_circuit_data.json"), + ) + proofWithPis := gnark_verifier_types.ReadProofWithPublicInputs(s.circuitPath + "/proof_with_public_inputs.json") + proofWithPisVariable := variables.DeserializeProofWithPublicInputs(proofWithPis) + + inputHash, outputHash := GetInputHashOutputHash(proofWithPis) + + // Circuit assignment + assignment := &VerifierCircuit{ + ProofWithPis: proofWithPisVariable, + VerifierData: verifierOnlyCircuitData, + VerifierDigest: verifierOnlyCircuitData.CircuitDigest, + InputHash: frontend.Variable(inputHash), + OutputHash: frontend.Variable(outputHash), + } + + s.logger.Info().Msg("Generating witness") + start := time.Now() + witness, err := frontend.NewWitness(assignment, ecc.BN254.ScalarField()) + if err != nil { + return nil, nil, errors.Wrap(err, "generate witness") + } + elapsed := time.Since(start) + s.logger.Info().Msg("Successfully generated witness in " + elapsed.String()) + + s.logger.Info().Msg("Creating proof") + start = time.Now() + proof, err := groth16.Prove(r1cs, pk, witness) + if err != nil { + return nil, nil, errors.Wrap(err, "create proof") + } + elapsed = time.Since(start) + s.logger.Info().Msg("Successfully created proof in " + elapsed.String()) + + _proof := proof.(*groth16_bn254.Proof) + s.logger.Info().Msg("Saving proof to proof.json") + jsonProof, err := json.Marshal(ProofResult{ + Output: []byte{}, + Proof: _proof.Ar.Marshal(), + }) + if err != nil { + return nil, nil, errors.Wrap(err, "marshal proof") + } + proofFile, err := os.Create("proof.json") + if err != nil { + return nil, nil, errors.Wrap(err, "create proof file") + } + defer proofFile.Close() + if _, err = proofFile.Write(jsonProof); err != nil { + return nil, nil, errors.Wrap(err, "write proof file") + } + s.logger.Info().Msg("Successfully saved proof") + + const fpSize = 4 * 8 + + proofBytes := _proof.Ar.Marshal() + + // Ensure proofBytes contains enough data for the expected operation + expectedLength := fpSize * 8 + if len(proofBytes) < expectedLength { + return nil, nil, fmt.Errorf("proofBytes length is %d, expected at least %d", len(proofBytes), expectedLength) + } + + proofs := make([]string, 8) + for i := 0; i < 8; i++ { + start := i * fpSize + end := (i + 1) * fpSize + // Additional check to prevent slice bounds out of range panic + if end > len(proofBytes) { + return nil, nil, fmt.Errorf("attempt to slice beyond proofBytes length at segment %d", i) + } + proofs[i] = "0x" + hex.EncodeToString(proofBytes[start:end]) + } + + publicWitness, _ := witness.Public() + publicWitnessBytes, _ := publicWitness.MarshalBinary() + publicWitnessBytes = publicWitnessBytes[12:] // We cut off the first 12 bytes because they encode length information + + inputs := make([]string, 3) + // Print out the public witness bytes + for i := 0; i < 3; i++ { + inputs[i] = "0x" + hex.EncodeToString(publicWitnessBytes[i*fpSize:(i+1)*fpSize]) + } + + // Write proof with all the public inputs and save to disk. + jsonProofWithWitness, err := json.Marshal(struct { + Inputs []string `json:"inputs"` + Proofs []string `json:"proofs"` + InputHash hexutil.Bytes `json:"input_hash"` + OutputHash hexutil.Bytes `json:"output_hash"` + VerifierDigest hexutil.Bytes `json:"verifier_digest"` + Proof hexutil.Bytes `json:"proof"` + }{ + Inputs: inputs, + Proofs: proofs, + InputHash: inputHash.Bytes(), + OutputHash: outputHash.Bytes(), + VerifierDigest: (verifierOnlyCircuitData.CircuitDigest).(*big.Int).Bytes(), + Proof: _proof.Ar.Marshal(), + }) + if err != nil { + return nil, nil, errors.Wrap(err, "marshal proof with witness") + } + proofFile, err = os.Create("proof_with_witness.json") + if err != nil { + return nil, nil, errors.Wrap(err, "create proof_with_witness file") + } + defer proofFile.Close() + if _, err = proofFile.Write(jsonProofWithWitness); err != nil { + return nil, nil, errors.Wrap(err, "write proof_with_witness file") + } + s.logger.Info().Msg("Successfully saved proof_with_witness to proof_with_witness.json") + + publicWitness, err = witness.Public() + if err != nil { + return nil, nil, errors.Wrap(err, "get public witness") + } + + s.logger.Info().Msg("Saving public witness to public_witness.bin") + witnessFile, err := os.Create("public_witness.bin") + if err != nil { + return nil, nil, errors.Wrap(err, "create public witness file") + } + defer witnessFile.Close() + if _, err = publicWitness.WriteTo(witnessFile); err != nil { + return nil, nil, errors.Wrap(err, "write public witness file") + } + s.logger.Info().Msg("Successfully saved public witness") + + return proof, publicWitness, nil +} + +func (s *Groth16System) ExportVerifierSolidity(vk groth16.VerifyingKey) error { + // Create a new buffer and export the VerifyingKey into it as a Solidity contract and + // convert the buffer content to a string for further manipulation. + buf := new(bytes.Buffer) + err := vk.ExportSolidity(buf) + if err != nil { + return errors.Wrap(err, "export verifying key to solidity") + } + content := buf.String() + + contractFile, err := os.Create(s.dataPath + "/Verifier.sol") + if err != nil { + return errors.Wrap(err, "create Verifier.sol file") + } + defer contractFile.Close() + + w := bufio.NewWriter(contractFile) + // write the new content to the writer + if _, err = w.Write([]byte(content)); err != nil { + return errors.Wrap(err, "write to Verifier.sol") + } + + if err = w.Flush(); err != nil { + return errors.Wrap(err, "flush writer for Verifier.sol") + } + + return nil +} + +func (s *Groth16System) ExportVerifierJSON(vk groth16.VerifyingKey) error { + vkFile, err := os.Create(s.dataPath + "/vk.json") + if err != nil { + return errors.Wrap(err, "create vk.json file") + } + defer vkFile.Close() + + return nil +} + +func (s *Groth16System) LoadCircuit() (constraint.ConstraintSystem, error) { + r1cs := groth16.NewCS(ecc.BN254) + f, err := os.Open(s.dataPath + "/r1cs.bin") + if err != nil { + return nil, errors.Wrap(err, "open r1cs file") + } + r1csReader := bufio.NewReader(f) + _, err = r1cs.ReadFrom(r1csReader) + if err != nil { + return nil, errors.Wrap(err, "read r1cs file") + } + f.Close() + + return r1cs, nil +} + +func (s *Groth16System) LoadProvingKey() (pk groth16.ProvingKey, err error) { + pk = groth16.NewProvingKey(ecc.BN254) + f, err := os.Open(s.dataPath + "/pk.bin") + if err != nil { + return pk, errors.Wrap(err, "open pk file") + } + _, err = pk.ReadFrom(f) + if err != nil { + return pk, errors.Wrap(err, "read pk file") + } + f.Close() + + return pk, nil +} + +func (s *Groth16System) LoadVerifierKey() (vk groth16.VerifyingKey, err error) { + vk = groth16.NewVerifyingKey(ecc.BN254) + f, err := os.Open(s.dataPath + "/vk.bin") + if err != nil { + return nil, errors.Wrap(err, "open vk file") + } + _, err = vk.ReadFrom(f) + if err != nil { + return nil, errors.Wrap(err, "read vk file") + } + f.Close() + + return vk, nil +} + +func (s *Groth16System) LoadProof() (proof groth16.Proof, err error) { + proof = groth16.NewProof(ecc.BN254) + f, err := os.Open(s.dataPath + "/proof.json") + if err != nil { + return proof, errors.Wrap(err, "open proof file") + } + jsonProof, err := io.ReadAll(f) + if err != nil { + return proof, errors.Wrap(err, "read proof file") + } + err = json.Unmarshal(jsonProof, proof) + if err != nil { + return proof, errors.Wrap(err, "read proof file") + } + f.Close() + + return proof, nil +} + +func (s *Groth16System) LoadPublicWitness() (witness.Witness, error) { + publicWitness, err := witness.New(ecc.BN254.ScalarField()) + if err != nil { + return publicWitness, errors.Wrap(err, "create public witness") + } + f, err := os.Open(s.dataPath + "/public_witness.bin") + if err != nil { + return publicWitness, errors.Wrap(err, "open public witness file") + } + _, err = publicWitness.ReadFrom(f) + if err != nil { + return publicWitness, errors.Wrap(err, "read public witness file") + } + f.Close() + + return publicWitness, nil +} diff --git a/plonky2x/verifier/system/groth16_test.go b/plonky2x/verifier/system/groth16_test.go new file mode 100644 index 000000000..1f01d2ac2 --- /dev/null +++ b/plonky2x/verifier/system/groth16_test.go @@ -0,0 +1,126 @@ +// Useful reference files in gnark: +// https://github.com/Consensys/gnark-solidity-checker/blob/main/cmd/templates.go +// https://github.com/Consensys/gnark/blob/cfe83dbce12428ad0b095bcc33de55c6a9121949/test/assert_solidity.go#L60-L77 +package system + +import ( + "bufio" + "bytes" + "encoding/hex" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "testing" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/consensys/gnark/std/rangecheck" + + "github.com/stretchr/testify/assert" +) + +type MyCircuit struct { + X frontend.Variable `gnark:",public"` + Y frontend.Variable `gnark:",public"` + Z frontend.Variable `gnark:",public"` + DoRangeCheck bool +} + +func (circuit *MyCircuit) Define(api frontend.API) error { + api.AssertIsEqual(circuit.Z, api.Add(circuit.X, circuit.Y)) + if true || circuit.DoRangeCheck { + rangeChecker := rangecheck.New(api) + rangeChecker.Check(circuit.X, 8) + } + return nil +} + +type Groth16ProofData struct { + Proof []string `json:"proof"` + Inputs []string `json:"inputs"` +} + +func TestGroth16(t *testing.T) { + + range_check := true + + circuit := MyCircuit{DoRangeCheck: range_check} + + r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit) + if err != nil { + panic(err) + } + pk, vk, err := groth16.Setup(r1cs) + buf := new(bytes.Buffer) + err = vk.ExportSolidity(buf) + if err != nil { + panic(err) + } + content := buf.String() + filename := "VerifierGroth16.sol" + contractFile, err := os.Create(filename) + if err != nil { + panic(err) + } + w := bufio.NewWriter(contractFile) + // write the new content to the writer + _, err = w.Write([]byte(content)) + if err != nil { + panic(err) + } + contractFile.Close() + + assignment := MyCircuit{ + X: 1, + Y: 2, + Z: 3, + } + + witness, err := frontend.NewWitness(&assignment, ecc.BN254.ScalarField()) + assert.Nil(t, err) + proof, err := groth16.Prove(r1cs, pk, witness) + assert.Nil(t, err) + + const fpSize = 4 * 8 + buf := new(bytes.Buffer) + proof.WriteRawTo(buf) + proofBytes := buf.Bytes() + + proofs := make([]string, 8) + // Print out the proof + for i := 0; i < 8; i++ { + proofs[i] = "0x" + hex.EncodeToString(proofBytes[i*fpSize:(i+1)*fpSize]) + } + + publicWitness, _ := witness.Public() + publicWitnessBytes, _ := publicWitness.MarshalBinary() + publicWitnessBytes = publicWitnessBytes[12:] // We cut off the first 12 bytes because they encode length information + + inputs := make([]string, 3) + // Print out the public witness bytes + for i := 0; i < 3; i++ { + inputs[i] = "0x" + hex.EncodeToString(publicWitnessBytes[i*fpSize:(i+1)*fpSize]) + } + + // Create the data struct and populate it + data := Groth16ProofData{ + Proof: proofs, + Inputs: inputs, + } + + // Marshal the data into JSON + jsonData, err := json.MarshalIndent(data, "", " ") + if err != nil { + fmt.Println("Error marshalling to JSON:", err) + return + } + + // Write the JSON to a file + err = ioutil.WriteFile("groth16_proof_data.json", jsonData, 0644) + if err != nil { + fmt.Println("Error writing to file:", err) + } +} diff --git a/plonky2x/verifier/system/interface.go b/plonky2x/verifier/system/interface.go new file mode 100644 index 000000000..13c1405d1 --- /dev/null +++ b/plonky2x/verifier/system/interface.go @@ -0,0 +1,8 @@ +package system + +type ProvingSystem interface { + Compile() error + Prove() error + Verify() error + Export() error +} diff --git a/plonky2x/verifier/system/plonk.go b/plonky2x/verifier/system/plonk.go new file mode 100644 index 000000000..13c97be77 --- /dev/null +++ b/plonky2x/verifier/system/plonk.go @@ -0,0 +1,433 @@ +package system + +import ( + "bufio" + "bytes" + "encoding/json" + "io" + "math/big" + "os" + "strings" + "time" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark-crypto/kzg" + "github.com/consensys/gnark/backend/plonk" + plonk_bn254 "github.com/consensys/gnark/backend/plonk/bn254" + "github.com/consensys/gnark/backend/witness" + "github.com/consensys/gnark/constraint" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/frontend/cs/scs" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pkg/errors" + "github.com/rs/zerolog" + + "github.com/succinctlabs/gnark-plonky2-verifier/trusted_setup" + gnark_verifier_types "github.com/succinctlabs/gnark-plonky2-verifier/types" + "github.com/succinctlabs/gnark-plonky2-verifier/variables" +) + +type PlonkSystem struct { + logger zerolog.Logger + circuitPath string + dataPath string +} + +func NewPlonkSystem(logger zerolog.Logger, circuitPath string, dataPath string) *PlonkSystem { + return &PlonkSystem{ + logger: logger, + circuitPath: circuitPath, + dataPath: dataPath, + } +} + +func (s *PlonkSystem) Compile() error { + s.logger.Info().Msg("starting compiling verifier circuit") + + r1cs, pk, vk, err := s.CompileVerifierCircuit() + if err != nil { + return errors.Wrap(err, "compile verifier circuit") + } + + err = s.SaveVerifierCircuit(r1cs, pk, vk) + if err != nil { + return errors.Wrap(err, "save verifier circuit") + } + + s.logger.Info().Msg("successfully compiled verifier circuit") + + return nil +} + +func (s *PlonkSystem) Prove() error { + s.logger.Info().Msg("starting prove -- loading verifier circuit and proving key") + + r1cs, err := s.LoadCircuit() + if err != nil { + return errors.Wrap(err, "load the verifier circuit") + } + pk, err := s.LoadProvingKey() + if err != nil { + return errors.Wrap(err, "load the proving key") + } + + // If the circuitPath is "" and not provided as part of the CLI flags, then we wait + // for user input. + if s.circuitPath == "" { + s.logger.Info().Msg("no circuitPath flag found, so user must input circuitPath via stdin") + reader := bufio.NewReader(os.Stdin) + str, err := reader.ReadString('\n') + if err != nil { + return errors.Wrap(err, "read circuitPath from stdin") + } + trimmed := strings.TrimSuffix(str, "\n") + s.circuitPath = trimmed + } + + s.logger.Info().Msgf("generating proof with circuit path %v", s.circuitPath) + _, _, err = s.ProveCircuit(r1cs, pk) + if err != nil { + return errors.Wrap(err, "create proof") + } + + s.logger.Info().Msg("successfully created proof") + + return nil +} + +func (s *PlonkSystem) Verify() error { + s.logger.Info().Msg("starting verify -- loading verifier key, public witness, and proof") + + vk, err := s.LoadVerifierKey() + if err != nil { + return errors.Wrap(err, "load verifier key") + } + + proof, err := s.LoadProof() + if err != nil { + return errors.Wrap(err, "load proof") + } + + publicWitness, err := s.LoadPublicWitness() + if err != nil { + return errors.Wrap(err, "load public witness") + } + + err = plonk.Verify(proof, vk, publicWitness) + if err != nil { + return errors.Wrap(err, "verify proof") + } + + s.logger.Info().Msg("successfully verified proof") + + return nil +} + +func (s *PlonkSystem) Export() error { + s.logger.Info().Msg("starting export -- loading verifier key and exporting Verifier solidity") + + vk, err := s.LoadVerifierKey() + if err != nil { + return errors.Wrap(err, "load verifier key") + } + + err = s.ExportVerifierSolidity(vk) + if err != nil { + return errors.Wrap(err, "export Verifier solidity") + } + + s.logger.Info().Msg("successfully exported Verifier solidity") + + return nil +} + +func (s *PlonkSystem) CompileVerifierCircuit() (constraint.ConstraintSystem, plonk.ProvingKey, plonk.VerifyingKey, error) { + verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData( + gnark_verifier_types.ReadVerifierOnlyCircuitData(s.circuitPath + "/verifier_only_circuit_data.json"), + ) + proofWithPis := variables.DeserializeProofWithPublicInputs( + gnark_verifier_types.ReadProofWithPublicInputs(s.circuitPath + "/proof_with_public_inputs.json"), + ) + commonCircuitData := gnark_verifier_types.ReadCommonCircuitData(s.circuitPath + "/common_circuit_data.json") + + circuit := VerifierCircuit{ + ProofWithPis: proofWithPis, + VerifierData: verifierOnlyCircuitData, + VerifierDigest: new(frontend.Variable), + InputHash: new(frontend.Variable), + OutputHash: new(frontend.Variable), + CommonCircuitData: commonCircuitData, + } + scs, err := frontend.Compile(ecc.BN254.ScalarField(), scs.NewBuilder, &circuit) + if err != nil { + return nil, nil, nil, errors.Wrap(err, "compile verifier circuit") + } + s.logger.Info().Msg("Successfully compiled verifier circuit") + + fileName := "srs_setup" + if _, err := os.Stat(fileName); os.IsNotExist(err) { + trusted_setup.DownloadAndSaveAztecIgnitionSrs(174, fileName) + } + fSRS, err := os.Open(fileName) + if err != nil { + return nil, nil, nil, errors.Wrap(err, "open srs file") + } + + var srs kzg.SRS = kzg.NewSRS(ecc.BN254) + _, err = srs.ReadFrom(fSRS) + fSRS.Close() + s.logger.Info().Msg("Successfully loaded SRS") + + start := time.Now() + pk, vk, err := plonk.Setup(scs, srs) + if err != nil { + return nil, nil, nil, err + } + elapsed := time.Since(start) + s.logger.Info().Msg("Successfully ran circuit setup in " + elapsed.String()) + + return scs, pk, vk, nil +} + +func (s *PlonkSystem) SaveVerifierCircuit(r1cs constraint.ConstraintSystem, pk plonk.ProvingKey, vk plonk.VerifyingKey) error { + os.MkdirAll(s.dataPath, 0755) + + r1csFile, err := os.Create(s.dataPath + "/r1cs.bin") + if err != nil { + return errors.Wrap(err, "create r1cs file") + } + r1cs.WriteTo(r1csFile) + r1csFile.Close() + s.logger.Info().Msg("Successfully saved circuit constraints to r1cs.bin") + + s.logger.Info().Msg("Saving proving key to pk.bin") + pkFile, err := os.Create(s.dataPath + "/pk.bin") + if err != nil { + return errors.Wrap(err, "create pk file") + } + pk.WriteRawTo(pkFile) + pkFile.Close() + s.logger.Info().Msg("Successfully saved proving key to pk.bin") + + vkFile, err := os.Create(s.dataPath + "/vk.bin") + if err != nil { + return errors.Wrap(err, "create vk file") + } + vk.WriteRawTo(vkFile) + vkFile.Close() + s.logger.Info().Msg("Successfully saved verifying key to vk.bin") + + return nil +} + +func (s *PlonkSystem) ProveCircuit(r1cs constraint.ConstraintSystem, pk plonk.ProvingKey) (plonk.Proof, witness.Witness, error) { + s.logger.Info().Msg("Loading verifier only circuit data and proof with public inputs in path " + s.circuitPath) + verifierOnlyCircuitData := variables.DeserializeVerifierOnlyCircuitData( + gnark_verifier_types.ReadVerifierOnlyCircuitData(s.circuitPath + "/verifier_only_circuit_data.json"), + ) + proofWithPis := gnark_verifier_types.ReadProofWithPublicInputs(s.circuitPath + "/proof_with_public_inputs.json") + proofWithPisVariable := variables.DeserializeProofWithPublicInputs(proofWithPis) + + inputHash, outputHash := GetInputHashOutputHash(proofWithPis) + + // Circuit assignment + assignment := &VerifierCircuit{ + ProofWithPis: proofWithPisVariable, + VerifierData: verifierOnlyCircuitData, + VerifierDigest: verifierOnlyCircuitData.CircuitDigest, + InputHash: frontend.Variable(inputHash), + OutputHash: frontend.Variable(outputHash), + } + + s.logger.Info().Msg("Generating witness") + start := time.Now() + witness, err := frontend.NewWitness(assignment, ecc.BN254.ScalarField()) + if err != nil { + return nil, nil, errors.Wrap(err, "generate witness") + } + elapsed := time.Since(start) + s.logger.Info().Msg("Successfully generated witness in " + elapsed.String()) + + s.logger.Info().Msg("Creating proof") + start = time.Now() + proof, err := plonk.Prove(r1cs, pk, witness) + if err != nil { + return nil, nil, errors.Wrap(err, "create proof") + } + elapsed = time.Since(start) + s.logger.Info().Msg("Successfully created proof in " + elapsed.String()) + + _proof := proof.(*plonk_bn254.Proof) + s.logger.Info().Msg("Saving proof to proof.json") + jsonProof, err := json.Marshal(ProofResult{ + Output: []byte{}, + Proof: _proof.MarshalSolidity(), + }) + if err != nil { + return nil, nil, errors.Wrap(err, "marshal proof") + } + proofFile, err := os.Create("proof.json") + if err != nil { + return nil, nil, errors.Wrap(err, "create proof file") + } + defer proofFile.Close() + if _, err = proofFile.Write(jsonProof); err != nil { + return nil, nil, errors.Wrap(err, "write proof file") + } + s.logger.Info().Msg("Successfully saved proof") + + // Write proof with all the public inputs and save to disk. + jsonProofWithWitness, err := json.Marshal(struct { + InputHash hexutil.Bytes `json:"input_hash"` + OutputHash hexutil.Bytes `json:"output_hash"` + VerifierDigest hexutil.Bytes `json:"verifier_digest"` + Proof hexutil.Bytes `json:"proof"` + }{ + InputHash: inputHash.Bytes(), + OutputHash: outputHash.Bytes(), + VerifierDigest: (verifierOnlyCircuitData.CircuitDigest).(*big.Int).Bytes(), + Proof: _proof.MarshalSolidity(), + }) + if err != nil { + return nil, nil, errors.Wrap(err, "marshal proof with witness") + } + proofFile, err = os.Create("proof_with_witness.json") + if err != nil { + return nil, nil, errors.Wrap(err, "create proof_with_witness file") + } + defer proofFile.Close() + if _, err = proofFile.Write(jsonProofWithWitness); err != nil { + return nil, nil, errors.Wrap(err, "write proof_with_witness file") + } + s.logger.Info().Msg("Successfully saved proof_with_witness to proof_with_witness.json") + + publicWitness, err := witness.Public() + if err != nil { + return nil, nil, errors.Wrap(err, "get public witness") + } + + s.logger.Info().Msg("Saving public witness to public_witness.bin") + witnessFile, err := os.Create("public_witness.bin") + if err != nil { + return nil, nil, errors.Wrap(err, "create public witness file") + } + defer witnessFile.Close() + if _, err = publicWitness.WriteTo(witnessFile); err != nil { + return nil, nil, errors.Wrap(err, "write public witness file") + } + s.logger.Info().Msg("Successfully saved public witness") + + return proof, publicWitness, nil +} + +func (s *PlonkSystem) ExportVerifierSolidity(vk plonk.VerifyingKey) error { + // Create a new buffer and export the VerifyingKey into it as a Solidity contract and + // convert the buffer content to a string for further manipulation. + buf := new(bytes.Buffer) + err := vk.ExportSolidity(buf) + if err != nil { + return errors.Wrap(err, "export verifying key to solidity") + } + content := buf.String() + + contractFile, err := os.Create(s.dataPath + "/Verifier.sol") + if err != nil { + return errors.Wrap(err, "create Verifier.sol file") + } + defer contractFile.Close() + + w := bufio.NewWriter(contractFile) + // write the new content to the writer + if _, err = w.Write([]byte(content)); err != nil { + return errors.Wrap(err, "write to Verifier.sol") + } + + if err = w.Flush(); err != nil { + return errors.Wrap(err, "flush writer for Verifier.sol") + } + + return nil +} + +func (s *PlonkSystem) LoadCircuit() (constraint.ConstraintSystem, error) { + r1cs := plonk.NewCS(ecc.BN254) + f, err := os.Open(s.dataPath + "/r1cs.bin") + if err != nil { + return nil, errors.Wrap(err, "open r1cs file") + } + r1csReader := bufio.NewReader(f) + _, err = r1cs.ReadFrom(r1csReader) + if err != nil { + return nil, errors.Wrap(err, "read r1cs file") + } + f.Close() + + return r1cs, nil +} + +func (s *PlonkSystem) LoadProvingKey() (pk plonk.ProvingKey, err error) { + pk = plonk.NewProvingKey(ecc.BN254) + f, err := os.Open(s.dataPath + "/pk.bin") + if err != nil { + return pk, errors.Wrap(err, "open pk file") + } + _, err = pk.ReadFrom(f) + if err != nil { + return pk, errors.Wrap(err, "read pk file") + } + f.Close() + + return pk, nil +} + +func (s *PlonkSystem) LoadVerifierKey() (vk plonk.VerifyingKey, err error) { + vk = plonk.NewVerifyingKey(ecc.BN254) + f, err := os.Open(s.dataPath + "/vk.bin") + if err != nil { + return nil, errors.Wrap(err, "open vk file") + } + _, err = vk.ReadFrom(f) + if err != nil { + return nil, errors.Wrap(err, "read vk file") + } + f.Close() + + return vk, nil +} + +func (s *PlonkSystem) LoadProof() (proof plonk.Proof, err error) { + proof = plonk.NewProof(ecc.BN254) + f, err := os.Open(s.dataPath + "/proof.json") + if err != nil { + return proof, errors.Wrap(err, "open proof file") + } + jsonProof, err := io.ReadAll(f) + if err != nil { + return proof, errors.Wrap(err, "read proof file") + } + err = json.Unmarshal(jsonProof, proof) + if err != nil { + return proof, errors.Wrap(err, "read proof file") + } + f.Close() + + return proof, nil +} + +func (s *PlonkSystem) LoadPublicWitness() (witness.Witness, error) { + publicWitness, err := witness.New(ecc.BN254.ScalarField()) + if err != nil { + return publicWitness, errors.Wrap(err, "create public witness") + } + f, err := os.Open(s.dataPath + "/public_witness.bin") + if err != nil { + return publicWitness, errors.Wrap(err, "open public witness file") + } + _, err = publicWitness.ReadFrom(f) + if err != nil { + return publicWitness, errors.Wrap(err, "read public witness file") + } + f.Close() + + return publicWitness, nil +} diff --git a/plonky2x/verifier/gnark_verifiers_test.go b/plonky2x/verifier/system/plonk_test.go similarity index 53% rename from plonky2x/verifier/gnark_verifiers_test.go rename to plonky2x/verifier/system/plonk_test.go index a53ef07f5..d3b3a2430 100644 --- a/plonky2x/verifier/gnark_verifiers_test.go +++ b/plonky2x/verifier/system/plonk_test.go @@ -1,7 +1,7 @@ // Useful reference files in gnark: // https://github.com/Consensys/gnark-solidity-checker/blob/main/cmd/templates.go // https://github.com/Consensys/gnark/blob/cfe83dbce12428ad0b095bcc33de55c6a9121949/test/assert_solidity.go#L60-L77 -package main +package system import ( "bufio" @@ -14,119 +14,13 @@ import ( "testing" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend/groth16" "github.com/consensys/gnark/backend/plonk" plonk_bn254 "github.com/consensys/gnark/backend/plonk/bn254" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/frontend/cs/r1cs" "github.com/consensys/gnark/frontend/cs/scs" - "github.com/consensys/gnark/std/rangecheck" "github.com/consensys/gnark/test" ) -type MyCircuit struct { - X frontend.Variable `gnark:",public"` - Y frontend.Variable `gnark:",public"` - Z frontend.Variable `gnark:",public"` - DoRangeCheck bool -} - -func (circuit *MyCircuit) Define(api frontend.API) error { - api.AssertIsEqual(circuit.Z, api.Add(circuit.X, circuit.Y)) - if true || circuit.DoRangeCheck { - rangeChecker := rangecheck.New(api) - rangeChecker.Check(circuit.X, 8) - } - return nil -} - -type Groth16ProofData struct { - Proof []string `json:"proof"` - Inputs []string `json:"inputs"` -} - -func TestGroth16(t *testing.T) { - - circuit := MyCircuit{DoRangeCheck: false} - - r1cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit) - if err != nil { - panic(err) - } - pk, vk, err := groth16.Setup(r1cs) - if err != nil { - panic(err) - } - - buf := new(bytes.Buffer) - err = vk.ExportSolidity(buf) - if err != nil { - panic(err) - } - content := buf.String() - - contractFile, err := os.Create("VerifierGroth16.sol") - if err != nil { - panic(err) - } - w := bufio.NewWriter(contractFile) - // write the new content to the writer - _, err = w.Write([]byte(content)) - if err != nil { - panic(err) - } - contractFile.Close() - - assignment := MyCircuit{ - X: 1, - Y: 2, - Z: 3, - } - - witness, _ := frontend.NewWitness(&assignment, ecc.BN254.ScalarField()) - proof, _ := groth16.Prove(r1cs, pk, witness) - - const fpSize = 4 * 8 - buf = new(bytes.Buffer) - proof.WriteRawTo(buf) - proofBytes := buf.Bytes() - - proofs := make([]string, 8) - // Print out the proof - for i := 0; i < 8; i++ { - proofs[i] = "0x" + hex.EncodeToString(proofBytes[i*fpSize:(i+1)*fpSize]) - } - - publicWitness, _ := witness.Public() - publicWitnessBytes, _ := publicWitness.MarshalBinary() - publicWitnessBytes = publicWitnessBytes[12:] // We cut off the first 12 bytes because they encode length information - - inputs := make([]string, 3) - // Print out the public witness bytes - for i := 0; i < 3; i++ { - inputs[i] = "0x" + hex.EncodeToString(publicWitnessBytes[i*fpSize:(i+1)*fpSize]) - } - - // Create the data struct and populate it - data := Groth16ProofData{ - Proof: proofs, - Inputs: inputs, - } - - // Marshal the data into JSON - jsonData, err := json.MarshalIndent(data, "", " ") - if err != nil { - fmt.Println("Error marshalling to JSON:", err) - return - } - - // Write the JSON to a file - err = ioutil.WriteFile("groth16_proof_data.json", jsonData, 0644) - if err != nil { - fmt.Println("Error writing to file:", err) - } -} - type PlonkProofData struct { Proof string `json:"proof"` Inputs []string `json:"inputs"` diff --git a/plonky2x/verifier/system/proof.go b/plonky2x/verifier/system/proof.go new file mode 100644 index 000000000..a2d05f268 --- /dev/null +++ b/plonky2x/verifier/system/proof.go @@ -0,0 +1,70 @@ +package system + +import ( + "encoding/json" + "fmt" + "math/big" + "os" + + "github.com/ethereum/go-ethereum/common/hexutil" + gnark_verifier_types "github.com/succinctlabs/gnark-plonky2-verifier/types" +) + +type ProofResult struct { + Proof hexutil.Bytes `json:"proof"` + Output hexutil.Bytes `json:"output"` +} + +type Groth16Proof struct { + A [2]*big.Int `json:"a"` + B [2][2]*big.Int `json:"b"` + C [2]*big.Int `json:"c"` + Input hexutil.Bytes `json:"input,omitempty"` + Output hexutil.Bytes `json:"output,omitempty"` +} + +// Export saves the proof to a file. +func (g *Groth16Proof) Export(file string) error { + // Write the proof to a JSON-compatible format. + + // Create the proof file. + proofFile, err := os.Create(file) + if err != nil { + panic(fmt.Errorf("failed to create file: %w", err)) + } + defer proofFile.Close() + + // Marshal the proof to JSON. + jsonString, err := json.Marshal(g) + if err != nil { + panic(fmt.Errorf("failed to marshal output: %w", err)) + } + + // Write the proof to the file. + _, err = proofFile.Write(jsonString) + if err != nil { + panic(fmt.Errorf("failed to write data: %w", err)) + } + + return nil +} + +func GetInputHashOutputHash(proofWithPis gnark_verifier_types.ProofWithPublicInputsRaw) (*big.Int, *big.Int) { + publicInputs := proofWithPis.PublicInputs + if len(publicInputs) != 64 { + panic("publicInputs must be 64 bytes") + } + publicInputsBytes := make([]byte, 64) + for i, v := range publicInputs { + publicInputsBytes[i] = byte(v & 0xFF) + } + inputHash := new(big.Int).SetBytes(publicInputsBytes[0:32]) + outputHash := new(big.Int).SetBytes(publicInputsBytes[32:64]) + if inputHash.BitLen() > 253 { + panic("inputHash must be at most 253 bits") + } + if outputHash.BitLen() > 253 { + panic("outputHash must be at most 253 bits") + } + return inputHash, outputHash +} diff --git a/plonky2x/verifier/verifier.go b/plonky2x/verifier/verifier.go deleted file mode 100644 index c3cc693b5..000000000 --- a/plonky2x/verifier/verifier.go +++ /dev/null @@ -1,100 +0,0 @@ -package main - -import ( - "bufio" - "bytes" - "encoding/json" - "fmt" - "io" - "os" - "time" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/backend/plonk" - "github.com/consensys/gnark/backend/witness" - "github.com/consensys/gnark/logger" -) - -func LoadVerifierKey(path string) (plonk.VerifyingKey, error) { - log := logger.Logger() - vkFile, err := os.Open(path + "/vk.bin") - if err != nil { - return nil, fmt.Errorf("failed to open vk file: %w", err) - } - vk := plonk.NewVerifyingKey(ecc.BN254) - start := time.Now() - _, err = vk.ReadFrom(vkFile) - if err != nil { - return nil, fmt.Errorf("failed to read vk file: %w", err) - } - vkFile.Close() - elapsed := time.Since(start) - log.Debug().Msg("Successfully loaded verifying key, time: " + elapsed.String()) - - return vk, nil -} - -func LoadPublicWitness(circuitPath string) (witness.Witness, error) { - log := logger.Logger() - witnessFile, err := os.Open(circuitPath + "/public_witness.bin") - if err != nil { - return nil, fmt.Errorf("failed to open public witness file: %w", err) - } - publicWitness, err := witness.New(ecc.BN254.ScalarField()) - if err != nil { - return nil, fmt.Errorf("failed to create public witness: %w", err) - } - publicWitness.ReadFrom(witnessFile) - witnessFile.Close() - log.Debug().Msg("Successfully loaded public witness") - - return publicWitness, nil -} - -func LoadProof() (plonk.Proof, error) { - log := logger.Logger() - proofFile, err := os.Open("/proof.json") - if err != nil { - return nil, fmt.Errorf("failed to open proof file: %w", err) - } - proof := plonk.NewProof(ecc.BN254) - jsonProof, err := io.ReadAll(proofFile) - if err != nil { - return nil, fmt.Errorf("failed to read proof file: %w", err) - } - err = json.Unmarshal(jsonProof, proof) - if err != nil { - return nil, fmt.Errorf("failed to read proof file: %w", err) - } - proofFile.Close() - log.Debug().Msg("Successfully loaded proof") - - return proof, nil -} - -func ExportIFunctionVerifierSolidity(path string, vk plonk.VerifyingKey) error { - log := logger.Logger() - // Create a new buffer and export the VerifyingKey into it as a Solidity contract and - // convert the buffer content to a string for further manipulation. - buf := new(bytes.Buffer) - err := vk.ExportSolidity(buf) - if err != nil { - log.Err(err).Msg("failed to export verifying key to solidity") - return err - } - content := buf.String() - - contractFile, err := os.Create(path + "/Verifier.sol") - if err != nil { - return err - } - w := bufio.NewWriter(contractFile) - // write the new content to the writer - _, err = w.Write([]byte(content)) - if err != nil { - return err - } - - contractFile.Close() - return err -}