Skip to content

Latest commit

 

History

History
176 lines (135 loc) · 5.56 KB

README.md

File metadata and controls

176 lines (135 loc) · 5.56 KB

Circom Gnark parser

Circom2Gnark is a Go module that allows you to parse and verify Circom proofs using the Gnark library. You can:

  • Verify Circom proofs using the Gnark verifier.
  • Recursively verify Circom proofs within a Gnark circuit.

This enables interoperability between Circom-generated proofs and Go applications using Gnark for zkSNARK proofs, using Groth16 and the bn254 curve.

This work is based on the vocdoni/go-snark Circom compatible implementation created by @arnaucube

Installation

To use Circom2Gnark in your Go project, you need to import it and ensure it's included in your go.mod file:

go get github.com/vocdoni/circom2gnark

In your Go code, import the parser package:

import "github.com/vocdoni/circom2gnark/parser"

Features

  • Parsing Circom Data: Parse proofs, verification keys, and public signals generated by Circom/SnarkJS.
  • Verification with Gnark: Verify Circom proofs using Gnark's verifier outside of a circuit.
  • Recursive Verification: Verify Circom proofs recursively within a Gnark circuit, enabling proof composition and aggregation.

Usage

Parsing Circom Proofs and Verification Keys

Circom2Gnark provides functions to unmarshal Circom proofs, verification keys, and public signals from JSON files generated by Circom/SnarkJS.

// Read the proof, verification key, and public signals from files
proofData, err := os.ReadFile("proof.json")
if err != nil {
    log.Fatalf("failed to read proof: %v", err)
}

vkData, err := os.ReadFile("vkey.json")
if err != nil {
    log.Fatalf("failed to read verification key: %v", err)
}

publicSignalsData, err := os.ReadFile("public_signals.json")
if err != nil {
    log.Fatalf("failed to read public signals: %v", err)
}

// Unmarshal the JSON data
snarkProof, err := parser.UnmarshalCircomProofJSON(proofData)
if err != nil {
    log.Fatalf("failed to unmarshal proof: %v", err)
}

snarkVk, err := parser.UnmarshalCircomVerificationKeyJSON(vkData)
if err != nil {
    log.Fatalf("failed to unmarshal verification key: %v", err)
}

publicSignals, err := parser.UnmarshalCircomPublicSignalsJSON(publicSignalsData)
if err != nil {
    log.Fatalf("failed to unmarshal public signals: %v", err)
}

Verifying Circom Proofs with Gnark Verifier

After parsing, you can convert the Circom proof and verification key into Gnark-compatible formats and verify the proof using Gnark's verifier.

// Convert Circom proof to Gnark proof
gnarkProof, err := parser.ConvertCircomToGnark(snarkProof, snarkVk, publicSignals)
if err != nil {
    log.Fatalf("failed to convert Circom proof to Gnark proof: %v", err)
}

// Verify the proof using Gnark verifier
verified, err := parser.VerifyProof(gnarkProof)
if err != nil {
    log.Fatalf("failed to verify proof: %v", err)
}
if !verified {
    log.Fatalf("proof verification failed")
}
fmt.Println("Proof verification succeeded!")

Recursive Verification within a Gnark Circuit

Circom2Gnark allows you to verify Circom proofs recursively within a Gnark circuit. This is useful for building higher-level circuits that include verification of other proofs.

// Convert Circom proof to Gnark recursion data
recursionData, recursionPlaceholders, err := parser.ConvertCircomToGnarkRecursion(snarkProof, snarkVk, publicSignals)
if err != nil {
    log.Fatalf("failed to convert Circom proof to Gnark recursion proof: %v", err)
}

// Create a placeholder circuit
placeholderCircuit := &VerifyCircomProofCircuit{
    Proof:        recursionPlaceholders.Proof,
    VerifyingKey: recursionPlaceholders.Vk,
    PublicInputs: recursionPlaceholders.Witness,
}

// Compile the circuit
ccs, pk, vk, err := CompileCircuit(placeholderCircuit)
if err != nil {
    log.Fatalf("failed to compile circuit: %v", err)
}

// Create the circuit assignment with actual values
circuitAssignment := &VerifyCircomProofCircuit{
    Proof:        recursionData.Proof,
    VerifyingKey: recursionData.Vk,
    PublicInputs: recursionData.PublicInputs,
}

// Create the witness
witnessFull, err := frontend.NewWitness(circuitAssignment, ecc.BN254.ScalarField())
if err != nil {
    log.Fatalf("failed to create witness: %v", err)
}

// Generate the proof
proof, err := groth16.Prove(ccs, pk, witnessFull)
if err != nil {
    log.Fatalf("proving failed: %v", err)
}

// Verify the recursive proof
err = groth16.Verify(proof, vk, witnessFull.Public())
if err != nil {
    log.Fatalf("verification failed: %v", err)
}
fmt.Println("Recursive proof verification succeeded!")

Example

There is a complete example at the example directory, demosttrating how to use circom2gnark to verify a Circom proof and recursively verify it within a Gnark circuit. The Circom JSON files can be found at example/circom_data.

$ cd example
$ go run .

Verifying proof with Gnark verifier...

13:06:06 DBG verifier done backend=groth16 curve=bn254 took=0.876262
Proof verification succeeded!

Now let's build a new circuit to verify the Circom proof recursively

Found existing pk.bin, vk.bin, and circuit.r1cs. Loading them...
Loading circuit...
Loading proving key...
Loading verifying key...
Loading artifacts total time: 1m39.934126612s
Creating witness...
Witness creation time: 783.317µs
Proving...
13:07:49 DBG constraint system solver done nbConstraints=4772531 took=2695.463075
13:08:01 DBG prover done acceleration=none backend=groth16 curve=bn254 nbConstraints=4772531 took=12045.051975
Proving Recursion time: 14.740656608s
Verifying...
13:08:01 DBG verifier done backend=groth16 curve=bn254 took=1.492666

Recursive proof verification succeeded! took 1.528964ms