Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

statetransition: implement VerifyAggregatedZKProof #14

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
44 changes: 0 additions & 44 deletions circuits/aggregator/dummy.go

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ import (
"math/big"

"github.com/consensys/gnark-crypto/ecc"
"github.com/consensys/gnark/backend/groth16"
"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/consensys/gnark/std/algebra/native/sw_bls12377"
stdgroth16 "github.com/consensys/gnark/std/recursion/groth16"
"github.com/vocdoni/vocdoni-z-sandbox/circuits/dummy"
)

// EncodeProofsSelector function returns a number that its base2 representation
Expand Down Expand Up @@ -39,8 +37,8 @@ func EncodeProofsSelector(nValidProofs int) *big.Int {
// something fails.
func FillWithDummyFixed(placeholder, assigments *AggregatorCircuit, main constraint.ConstraintSystem, fromIdx int) error {
// compile the dummy circuit for the main
dummyCCS, pubWitness, proof, vk, err := compileAndVerifyCircuit(
DummyPlaceholder(main), DummyAssigment(),
dummyCCS, pubWitness, proof, vk, err := dummy.Prove(
dummy.Placeholder(main), dummy.Assignment(1),
ecc.BW6_761.ScalarField(), ecc.BLS12_377.ScalarField())
if err != nil {
return err
Expand Down Expand Up @@ -82,30 +80,3 @@ func FillWithDummyFixed(placeholder, assigments *AggregatorCircuit, main constra
}
return nil
}

func compileAndVerifyCircuit(placeholder, assigment frontend.Circuit, outer *big.Int, field *big.Int) (constraint.ConstraintSystem, witness.Witness, groth16.Proof, groth16.VerifyingKey, error) {
ccs, err := frontend.Compile(field, r1cs.NewBuilder, placeholder)
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("compile error: %w", err)
}
pk, vk, err := groth16.Setup(ccs)
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("setup error: %w", err)
}
fullWitness, err := frontend.NewWitness(assigment, field)
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("full witness error: %w", err)
}
proof, err := groth16.Prove(ccs, pk, fullWitness, stdgroth16.GetNativeProverOptions(outer, field))
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("proof error: %w", err)
}
publicWitness, err := fullWitness.Public()
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("pub witness error: %w", err)
}
if err = groth16.Verify(proof, vk, publicWitness, stdgroth16.GetNativeVerifierOptions(outer, field)); err != nil {
return nil, nil, nil, nil, fmt.Errorf("verify error: %w", err)
}
return ccs, publicWitness, proof, vk, nil
}
50 changes: 50 additions & 0 deletions circuits/dummy/dummy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package dummy

import (
"errors"

"github.com/consensys/gnark/constraint"
"github.com/consensys/gnark/frontend"
)

type Circuit struct {
nbConstraints int
SecretInput frontend.Variable `gnark:",secret"`
PublicInputs frontend.Variable `gnark:",public"`
}

func (c *Circuit) Define(api frontend.API) error {
cmtr, ok := api.(frontend.Committer)
if !ok {
return errors.New("api is not a commiter")
}
secret, err := cmtr.Commit(c.SecretInput)
if err != nil {
return err
}
api.AssertIsDifferent(secret, 0)

res := api.Mul(c.SecretInput, c.SecretInput)
for i := 2; i < c.nbConstraints; i++ {
res = api.Mul(res, c.SecretInput)
}
api.AssertIsEqual(c.PublicInputs, c.PublicInputs)
return nil
}

// Placeholder function returns the placeholder of a dummy circuit for
// the constraint.ConstraintSystem provided.
func Placeholder(mainCircuit constraint.ConstraintSystem) *Circuit {
return &Circuit{nbConstraints: mainCircuit.GetNbConstraints()}
}

// PlaceholderWithConstraints returns the placeholder of a dummy circuit
// with the desired number of constraints.
func PlaceholderWithConstraints(nbConstraints int) *Circuit {
return &Circuit{nbConstraints: nbConstraints}
}

// Assignment returns the assignment of a dummy circuit.
func Assignment(publicInput frontend.Variable) *Circuit {
return &Circuit{PublicInputs: publicInput, SecretInput: 1}
}
40 changes: 40 additions & 0 deletions circuits/dummy/dummy_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package dummy

import (
"fmt"
"math/big"

"github.com/consensys/gnark/backend/groth16"
"github.com/consensys/gnark/backend/witness"
"github.com/consensys/gnark/constraint"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/frontend/cs/r1cs"
stdgroth16 "github.com/consensys/gnark/std/recursion/groth16"
)

func Prove(placeholder, assigment frontend.Circuit, outer *big.Int, field *big.Int) (constraint.ConstraintSystem, witness.Witness, groth16.Proof, groth16.VerifyingKey, error) {
ccs, err := frontend.Compile(field, r1cs.NewBuilder, placeholder)
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("compile error: %w", err)
}
pk, vk, err := groth16.Setup(ccs)
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("setup error: %w", err)
}
fullWitness, err := frontend.NewWitness(assigment, field)
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("full witness error: %w", err)
}
proof, err := groth16.Prove(ccs, pk, fullWitness, stdgroth16.GetNativeProverOptions(outer, field))
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("proof error: %w", err)
}
publicWitness, err := fullWitness.Public()
if err != nil {
return nil, nil, nil, nil, fmt.Errorf("pub witness error: %w", err)
}
if err = groth16.Verify(proof, vk, publicWitness, stdgroth16.GetNativeVerifierOptions(outer, field)); err != nil {
return nil, nil, nil, nil, fmt.Errorf("verify error: %w", err)
}
return ccs, publicWitness, proof, vk, nil
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package aggregator
package dummy

import (
"testing"
Expand Down Expand Up @@ -28,7 +28,7 @@ func TestSameCircuitsInfo(t *testing.T) {
c.Assert(err, qt.IsNil)
mainVk := stdgroth16.PlaceholderVerifyingKey[sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT](mainCCS)

dummyCCS, err := frontend.Compile(ecc.BLS12_377.ScalarField(), r1cs.NewBuilder, DummyPlaceholder(mainCCS))
dummyCCS, err := frontend.Compile(ecc.BLS12_377.ScalarField(), r1cs.NewBuilder, Placeholder(mainCCS))
c.Assert(err, qt.IsNil)
dummyVk := stdgroth16.PlaceholderVerifyingKey[sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT](dummyCCS)

Expand Down
126 changes: 101 additions & 25 deletions circuits/statetransition/circuit.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package statetransition

import (
"fmt"

"github.com/consensys/gnark-crypto/ecc"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/std/algebra/emulated/sw_bw6761"
"github.com/consensys/gnark/std/recursion/groth16"
"github.com/vocdoni/gnark-crypto-primitives/elgamal"
"github.com/vocdoni/gnark-crypto-primitives/utils"
"github.com/vocdoni/vocdoni-z-sandbox/circuits/dummy"
"github.com/vocdoni/vocdoni-z-sandbox/state"
"github.com/vocdoni/vocdoni-z-sandbox/util"
)
Expand All @@ -19,7 +25,6 @@ type Circuit struct {
// ---------------------------------------------------------------------------------------------
// PUBLIC INPUTS

// list of root hashes
RootHashBefore frontend.Variable `gnark:",public"`
RootHashAfter frontend.Variable `gnark:",public"`
NumNewVotes frontend.Variable `gnark:",public"`
Expand All @@ -28,7 +33,9 @@ type Circuit struct {
// ---------------------------------------------------------------------------------------------
// SECRET INPUTS

AggregatedProof frontend.Variable // mock, this should be a zkProof
AggregatedProof groth16.Proof[sw_bw6761.G1Affine, sw_bw6761.G2Affine]
AggregatedProofWitness groth16.Witness[sw_bw6761.ScalarField]
AggregatedProofVK groth16.VerifyingKey[sw_bw6761.G1Affine, sw_bw6761.G2Affine, sw_bw6761.GTEl] `gnark:"-"`

ProcessID state.MerkleProof
CensusRoot state.MerkleProof
Expand All @@ -42,14 +49,19 @@ type Circuit struct {

// Define declares the circuit's constraints
func (circuit Circuit) Define(api frontend.API) error {
circuit.VerifyAggregatedZKProof(api)
if err := circuit.VerifyAggregatedWitness(api, HashFn); err != nil {
return err
}
if err := circuit.VerifyAggregatedZKProof(api); err != nil {
return err
}
circuit.VerifyMerkleProofs(api, HashFn)
circuit.VerifyMerkleTransitions(api, HashFn)
circuit.VerifyBallots(api)
return nil
}

func (circuit Circuit) VerifyAggregatedZKProof(api frontend.API) {
func (circuit Circuit) VerifyAggregatedWitness(api frontend.API, hFn utils.Hasher) error {
// all of the following values compose the preimage that is hashed
// to produce the public input needed to verify AggregatedProof.
// they are extracted from the MerkleProofs:
Expand All @@ -62,29 +74,52 @@ func (circuit Circuit) VerifyAggregatedZKProof(api frontend.API) {
// Addressess := circuit.Commitment[i].NewKey
// Commitments := circuit.Commitment[i].NewValue

api.Println("verify AggregatedZKProof mock:", circuit.AggregatedProof) // mock

packedInputs := func() frontend.Variable {
for i, p := range []state.MerkleProof{
circuit.ProcessID,
circuit.CensusRoot,
circuit.BallotMode,
circuit.EncryptionKey,
} {
api.Println("packInputs mock", i, p.Value) // mock
}
for i := range circuit.Ballot {
api.Println("packInputs mock nullifier", i, circuit.Ballot[i].NewKey) // mock
api.Println("packInputs mock ballot", i, circuit.Ballot[i].NewValue) // mock
}
for i := range circuit.Commitment {
api.Println("packInputs mock address", i, circuit.Commitment[i].NewKey) // mock
api.Println("packInputs mock commitment", i, circuit.Commitment[i].NewValue) // mock
}
return 1 // mock, should return hash of packed inputs
inputs := []frontend.Variable{
circuit.ProcessID.Value,
circuit.CensusRoot.Value,
circuit.BallotMode.Value,
circuit.EncryptionKey.Value,
}
// for _, mt := range circuit.Ballot {
// inputs = append(inputs, mt.NewKey) // Nullifier
// }
// for _, mt := range circuit.Ballot {
// inputs = append(inputs, mt.NewValue) // Ballot
// }
// for _, mt := range circuit.Commitment {
// inputs = append(inputs, mt.NewKey) // Address
// }
// for _, mt := range circuit.Commitment {
// inputs = append(inputs, mt.NewValue) // Commitment
// }
// hash the inputs
hash, err := hFn(api, inputs...)
if err != nil {
return fmt.Errorf("failed to hash: %w", err)
}
api.Println("hash:", inputs)
api.Println("hashed", len(inputs), "inputs, hash =", util.PrettyHex(hash))

api.AssertIsEqual(packedInputs(), 1) // TODO: mock, should actually verify AggregatedZKProof
api.AssertIsEqual(len(circuit.AggregatedProofWitness.Public), 1)
publicInput, err := utils.PackScalarToVar(api, &circuit.AggregatedProofWitness.Public[0])
if err != nil {
return fmt.Errorf("failed to pack scalar to var: %w", err)
}
api.AssertIsEqual(hash, publicInput)
return nil
}

func (circuit Circuit) VerifyAggregatedZKProof(api frontend.API) error {
// initialize the verifier
verifier, err := groth16.NewVerifier[sw_bw6761.ScalarField, sw_bw6761.G1Affine, sw_bw6761.G2Affine, sw_bw6761.GTEl](api)
if err != nil {
return fmt.Errorf("failed to create bw6761 verifier: %w", err)
}
// verify the proof with the hash as input and the fixed verification key
if err := verifier.AssertProof(circuit.AggregatedProofVK, circuit.AggregatedProof, circuit.AggregatedProofWitness); err != nil {
return fmt.Errorf("failed to verify aggregated proof: %w", err)
}
return nil
}

func (circuit Circuit) VerifyMerkleProofs(api frontend.API, hFn utils.Hasher) {
Expand Down Expand Up @@ -136,3 +171,44 @@ func (circuit Circuit) VerifyBallots(api frontend.API) {
api.AssertIsEqual(circuit.NumNewVotes, ballotCount)
api.AssertIsEqual(circuit.NumOverwrites, overwrittenCount)
}

func CircuitPlaceholder() *Circuit {
_, ph, err := WitnessAndCircuitPlaceholder(0)
if err != nil {
panic(err)
}
return ph
}

func WitnessAndCircuitPlaceholder(inputsHash frontend.Variable) (*Circuit, *Circuit, error) {
_, witness, proof, vk, err := dummy.Prove(
dummy.PlaceholderWithConstraints(10), dummy.Assignment(inputsHash),
ecc.BN254.ScalarField(), ecc.BW6_761.ScalarField())
if err != nil {
return nil, nil, err
}
// parse dummy proof and witness
dummyProof, err := groth16.ValueOfProof[sw_bw6761.G1Affine, sw_bw6761.G2Affine](proof)
if err != nil {
return nil, nil, fmt.Errorf("dummy proof value error: %w", err)
}
dummyWitness, err := groth16.ValueOfWitness[sw_bw6761.ScalarField](witness)
if err != nil {
return nil, nil, fmt.Errorf("dummy witness value error: %w", err)
}
// set fixed dummy vk in the placeholders
dummyVK, err := groth16.ValueOfVerifyingKeyFixed[sw_bw6761.G1Affine, sw_bw6761.G2Affine, sw_bw6761.GTEl](vk)
if err != nil {
return nil, nil, fmt.Errorf("fix dummy vk error: %w", err)
}

return &Circuit{
AggregatedProof: dummyProof,
AggregatedProofWitness: dummyWitness,
AggregatedProofVK: dummyVK,
}, &Circuit{
AggregatedProof: dummyProof,
AggregatedProofWitness: dummyWitness,
AggregatedProofVK: dummyVK,
}, nil
}
Loading
Loading