Skip to content

Commit

Permalink
new struct to group some inner circuits inputs, BigToFF moved to ecc …
Browse files Browse the repository at this point in the history
…package, error wrapping
  • Loading branch information
lucasmenendez committed Jan 2, 2025
1 parent eb0e5ce commit 117a671
Show file tree
Hide file tree
Showing 11 changed files with 151 additions and 125 deletions.
50 changes: 25 additions & 25 deletions circuits/aggregator/aggregator.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@
package aggregator

import (
"fmt"

"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/std/algebra/native/sw_bls12377"
"github.com/consensys/gnark/std/hash/mimc"
"github.com/consensys/gnark/std/math/bits"
"github.com/consensys/gnark/std/recursion/groth16"
"github.com/vocdoni/vocdoni-z-sandbox/circuits"
)

const (
Expand All @@ -44,17 +47,15 @@ type AggregatorCircuit struct {
// The following variables are priv-public inputs, so should be hashed and
// compared with the InputsHash. All the variables should be hashed in the
// same order as they are defined here.
MaxCount frontend.Variable // Part of InputsHash
ForceUniqueness frontend.Variable // Part of InputsHash
MaxValue frontend.Variable // Part of InputsHash
MinValue frontend.Variable // Part of InputsHash
MaxTotalCost frontend.Variable // Part of InputsHash
MinTotalCost frontend.Variable // Part of InputsHash
CostExp frontend.Variable // Part of InputsHash
CostFromWeight frontend.Variable // Part of InputsHash
EncryptionPubKey [2]frontend.Variable // Part of InputsHash
ProcessId frontend.Variable // Part of InputsHash
CensusRoot frontend.Variable // Part of InputsHash

// BallotMode is a struct that contains the common inputs for all the
// voters. The values of this struct should be the same for all the voters
// in the same process.
circuits.BallotMode[frontend.Variable]
// Other common inputs
ProcessId frontend.Variable // Part of InputsHash
CensusRoot frontend.Variable // Part of InputsHash
// Voter inputs
Nullifiers [MaxVotes]frontend.Variable // Part of InputsHash
Commitments [MaxVotes]frontend.Variable // Part of InputsHash
Addresses [MaxVotes]frontend.Variable // Part of InputsHash
Expand All @@ -67,16 +68,16 @@ type AggregatorCircuit struct {
VerificationKeys [2]groth16.VerifyingKey[sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT] `gnark:"-"`
}

func checkInputs(api frontend.API, inputsHash, maxCount, forceUniqueness, maxValue, minValue,
maxTotalCost, minTotalCost, costExp, costFromWeight, processId, censusRoot frontend.Variable,
encryptionKeys [2]frontend.Variable, nullifiers, commitments, addresses []frontend.Variable,
func checkInputs(api frontend.API, mode circuits.BallotMode[frontend.Variable],
inputsHash, processId, censusRoot frontend.Variable,
nullifiers, commitments, addresses []frontend.Variable,
encryptedBallots [][MaxFields][2][2]frontend.Variable,
) error {
// group all the inputs to hash them
inputs := []frontend.Variable{
maxCount, forceUniqueness, maxValue, minValue, maxTotalCost,
minTotalCost, costExp, costFromWeight, encryptionKeys[0],
encryptionKeys[1], processId, censusRoot,
mode.MaxCount, mode.ForceUniqueness, mode.MaxValue, mode.MinValue, mode.MaxTotalCost,
mode.MinTotalCost, mode.CostExp, mode.CostFromWeight, mode.EncryptionPubKey[0],
mode.EncryptionPubKey[1], processId, censusRoot,
}
inputs = append(inputs, nullifiers...)
inputs = append(inputs, commitments...)
Expand All @@ -90,7 +91,7 @@ func checkInputs(api frontend.API, inputsHash, maxCount, forceUniqueness, maxVal
// hash the inputs
hFn, err := mimc.NewMiMC(api)
if err != nil {
return err
return fmt.Errorf("error creating hash function: %w", err)
}
hFn.Write(inputs...)
// compare the hash with the provided InputsHash
Expand All @@ -100,17 +101,16 @@ func checkInputs(api frontend.API, inputsHash, maxCount, forceUniqueness, maxVal

func (c *AggregatorCircuit) Define(api frontend.API) error {
// check the inputs of the circuit
if err := checkInputs(api, c.InputsHash, c.MaxCount, c.ForceUniqueness,
c.MaxValue, c.MinValue, c.MaxTotalCost, c.MinTotalCost, c.CostExp,
c.CostFromWeight, c.ProcessId, c.CensusRoot, c.EncryptionPubKey,
if err := checkInputs(api,
c.BallotMode, c.InputsHash, c.ProcessId, c.CensusRoot,
c.Nullifiers[:], c.Commitments[:], c.Addresses[:], c.EncryptedBallots[:],
); err != nil {
return err
return fmt.Errorf("inputs check error: %w", err)
}
// initialize the verifier
verifier, err := groth16.NewVerifier[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT](api)
if err != nil {
return err
return fmt.Errorf("failed to create BLS12-377 verifier: %w", err)
}
// verify each proof with the provided public inputs and the fixed
// verification key
Expand All @@ -119,10 +119,10 @@ func (c *AggregatorCircuit) Define(api frontend.API) error {
for i := 0; i < len(c.VerifyProofs); i++ {
vk, err := verifier.SwitchVerificationKey(validProofs[i], c.VerificationKeys[:])
if err != nil {
return err
return fmt.Errorf("failed to switch verification key: %w", err)
}
if err := verifier.AssertProof(vk, c.VerifyProofs[i], c.VerifyPublicInputs[i]); err != nil {
return err
return fmt.Errorf("failed to verify proof %d: %w", i, err)
}
expectedValidVotes = api.Add(expectedValidVotes, validProofs[i])
totalValidVotes = api.Add(totalValidVotes, validProofs[i])
Expand Down
54 changes: 23 additions & 31 deletions circuits/aggregator/aggregator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,23 @@ import (
"math/big"
"testing"

"github.com/consensys/gnark-crypto/ecc"
gecc "github.com/consensys/gnark-crypto/ecc"
fr_bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761/fr"
bw6761mimc "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/mimc"
"github.com/consensys/gnark/backend"
"github.com/consensys/gnark/frontend"
stdgroth16 "github.com/consensys/gnark/std/recursion/groth16"
gtest "github.com/consensys/gnark/test"
qt "github.com/frankban/quicktest"
"github.com/vocdoni/arbo"
"github.com/vocdoni/vocdoni-z-sandbox/circuits"
ballottest "github.com/vocdoni/vocdoni-z-sandbox/circuits/test/ballotproof"
"github.com/vocdoni/vocdoni-z-sandbox/crypto/ecc"
"github.com/vocdoni/vocdoni-z-sandbox/util"
)

type testCheckInputsCircuit struct {
InputsHash frontend.Variable `gnark:",public"`
MaxCount frontend.Variable
ForceUniqueness frontend.Variable
MaxValue frontend.Variable
MinValue frontend.Variable
MaxTotalCost frontend.Variable
MinTotalCost frontend.Variable
CostExp frontend.Variable
CostFromWeight frontend.Variable
EncryptionPubKey [2]frontend.Variable
InputsHash frontend.Variable `gnark:",public"`
circuits.BallotMode[frontend.Variable]
ProcessId frontend.Variable
CensusRoot frontend.Variable
Nullifiers [MaxVotes]frontend.Variable
Expand All @@ -39,17 +31,15 @@ type testCheckInputsCircuit struct {
}

func (c *testCheckInputsCircuit) Define(api frontend.API) error {
return checkInputs(api, c.InputsHash, c.MaxCount, c.ForceUniqueness,
c.MaxValue, c.MinValue, c.MaxTotalCost, c.MinTotalCost, c.CostExp,
c.CostFromWeight, c.ProcessId, c.CensusRoot, c.EncryptionPubKey,
return checkInputs(api, c.BallotMode, c.InputsHash, c.ProcessId, c.CensusRoot,
c.Nullifiers[:], c.Commitments[:], c.Addresses[:], c.EncryptedBallots[:])
}

func TestCheckInputs(t *testing.T) {
c := qt.New(t)

processId := arbo.BigToFF(ecc.BW6_761.ScalarField(), new(big.Int).SetBytes(util.RandomBytes(20)))
censusRoot := arbo.BigToFF(ecc.BW6_761.ScalarField(), new(big.Int).SetBytes(util.RandomBytes(20)))
processId := ecc.BigToFF(gecc.BW6_761.ScalarField(), new(big.Int).SetBytes(util.RandomBytes(20)))
censusRoot := ecc.BigToFF(gecc.BW6_761.ScalarField(), new(big.Int).SetBytes(util.RandomBytes(20)))
encryptionKey := ballottest.GenEncryptionKeyForTest()
encryptionKeyX, encryptionKeyY := encryptionKey.Point()
// voter data
Expand Down Expand Up @@ -98,18 +88,20 @@ func TestCheckInputs(t *testing.T) {
}
publicHash := new(big.Int).SetBytes(aggregatorHashFn.Sum(nil))
assigment := testCheckInputsCircuit{
InputsHash: publicHash,
MaxCount: ballottest.MaxCount,
ForceUniqueness: ballottest.ForceUniqueness,
MaxValue: ballottest.MaxValue,
MinValue: ballottest.MinValue,
MaxTotalCost: int(math.Pow(float64(ballottest.MaxValue), float64(ballottest.CostExp))) * ballottest.MaxCount,
MinTotalCost: ballottest.MaxCount,
CostExp: ballottest.CostExp,
CostFromWeight: ballottest.CostFromWeight,
ProcessId: processId,
EncryptionPubKey: [2]frontend.Variable{encryptionKeyX, encryptionKeyY},
CensusRoot: censusRoot,
InputsHash: publicHash,
BallotMode: circuits.BallotMode[frontend.Variable]{
MaxCount: ballottest.MaxCount,
ForceUniqueness: ballottest.ForceUniqueness,
MaxValue: ballottest.MaxValue,
MinValue: ballottest.MinValue,
MaxTotalCost: int(math.Pow(float64(ballottest.MaxValue), float64(ballottest.CostExp))) * ballottest.MaxCount,
MinTotalCost: ballottest.MaxCount,
CostExp: ballottest.CostExp,
CostFromWeight: ballottest.CostFromWeight,
EncryptionPubKey: [2]frontend.Variable{encryptionKeyX, encryptionKeyY},
},
ProcessId: processId,
CensusRoot: censusRoot,
}
assigment.EncryptedBallots = [MaxVotes][MaxFields][2][2]frontend.Variable{}
assigment.Nullifiers = [MaxVotes]frontend.Variable{}
Expand All @@ -136,6 +128,6 @@ func TestCheckInputs(t *testing.T) {

assert := gtest.NewAssert(t)
assert.SolvingSucceeded(&testCheckInputsCircuit{}, &assigment,
gtest.WithCurves(ecc.BW6_761), gtest.WithBackends(backend.GROTH16),
gtest.WithProverOpts(stdgroth16.GetNativeProverOptions(ecc.BN254.ScalarField(), ecc.BW6_761.ScalarField())))
gtest.WithCurves(gecc.BW6_761), gtest.WithBackends(backend.GROTH16),
gtest.WithProverOpts(stdgroth16.GetNativeProverOptions(gecc.BN254.ScalarField(), gecc.BW6_761.ScalarField())))
}
4 changes: 2 additions & 2 deletions circuits/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package circuits
import (
"math/big"

"github.com/vocdoni/arbo"
"github.com/vocdoni/vocdoni-z-sandbox/crypto/ecc"
)

// BigIntArrayToN pads the big.Int array to n elements, if needed,
Expand Down Expand Up @@ -35,7 +35,7 @@ func BigIntArrayToStringArray(arr []*big.Int, n int) []string {
// 32 bytes so if it is not, fill with zeros at the beginning of the bytes
// representation.
func BigIntToMIMCHash(input, base *big.Int) []byte {
hash := arbo.BigToFF(base, input).Bytes()
hash := ecc.BigToFF(base, input).Bytes()
for len(hash) < 32 {
hash = append([]byte{0}, hash...)
}
Expand Down
24 changes: 13 additions & 11 deletions circuits/test/aggregator/aggregator_inputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,17 +147,19 @@ func AggregarorInputsForTest(processId []byte, nValidVoters int) (
publicHash := new(big.Int).SetBytes(aggregatorHashFn.Sum(nil))
// init final assigments stuff
finalAssigments := &aggregator.AggregatorCircuit{
InputsHash: publicHash,
ValidVotes: aggregator.EncodeProofsSelector(nValidVoters),
MaxCount: ballottest.MaxCount,
ForceUniqueness: ballottest.ForceUniqueness,
MaxValue: ballottest.MaxValue,
MinValue: ballottest.MinValue,
MaxTotalCost: int(math.Pow(float64(ballottest.MaxValue), float64(ballottest.CostExp))) * ballottest.MaxCount,
MinTotalCost: ballottest.MaxCount,
CostExp: ballottest.CostExp,
CostFromWeight: ballottest.CostFromWeight,
EncryptionPubKey: [2]frontend.Variable{vvInputs.EncryptionPubKey[0], vvInputs.EncryptionPubKey[1]},
InputsHash: publicHash,
ValidVotes: aggregator.EncodeProofsSelector(nValidVoters),
BallotMode: circuits.BallotMode[frontend.Variable]{
MaxCount: ballottest.MaxCount,
ForceUniqueness: ballottest.ForceUniqueness,
MaxValue: ballottest.MaxValue,
MinValue: ballottest.MinValue,
MaxTotalCost: int(math.Pow(float64(ballottest.MaxValue), float64(ballottest.CostExp))) * ballottest.MaxCount,
MinTotalCost: ballottest.MaxCount,
CostExp: ballottest.CostExp,
CostFromWeight: ballottest.CostFromWeight,
EncryptionPubKey: [2]frontend.Variable{vvInputs.EncryptionPubKey[0], vvInputs.EncryptionPubKey[1]},
},
ProcessId: new(big.Int).SetBytes(vvInputs.ProcessID),
CensusRoot: vvInputs.CensusRoot,
VerifyProofs: proofs,
Expand Down
7 changes: 4 additions & 3 deletions circuits/test/ballotproof/ballot_proof_inputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"math"
"math/big"

gecc "github.com/consensys/gnark-crypto/ecc"
gecdsa "github.com/consensys/gnark-crypto/ecc/secp256k1/ecdsa"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
Expand Down Expand Up @@ -253,8 +254,8 @@ func BallotProofForTest(address, processId []byte, encryptionKey ecc.Point) (*Vo
if err != nil {
return nil, err
}
ffAddress := arbo.BigToFF(arbo.BN254BaseField, new(big.Int).SetBytes(address))
ffProcessID := arbo.BigToFF(arbo.BN254BaseField, new(big.Int).SetBytes(processId))
ffAddress := ecc.BigToFF(gecc.BN254.BaseField(), new(big.Int).SetBytes(address))
ffProcessID := ecc.BigToFF(arbo.BN254BaseField, new(big.Int).SetBytes(processId))
// group the circom inputs to hash
bigCircomInputs := []*big.Int{
big.NewInt(int64(MaxCount)),
Expand Down Expand Up @@ -297,7 +298,7 @@ func BallotProofForTest(address, processId []byte, encryptionKey ecc.Point) (*Vo
"cipherfields": strCipherfields,
"nullifier": nullifier.String(),
"commitment": commitment.String(),
"secret": arbo.BigToFF(arbo.BN254BaseField, new(big.Int).SetBytes(secret)).String(),
"secret": ecc.BigToFF(gecc.BN254.BaseField(), new(big.Int).SetBytes(secret)).String(),
"inputs_hash": circomInputsHash.String(),
}
bCircomInputs, err := json.Marshal(circomInputs)
Expand Down
26 changes: 14 additions & 12 deletions circuits/test/voteverifier/vote_verifier_inputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,20 +123,22 @@ func VoteVerifierInputsForTest(votersData []VoterTestData, processId []byte) (
assigments = append(assigments, voteverifier.VerifyVoteCircuit{
InputsHash: inputsHash,
// circom inputs
MaxCount: emulated.ValueOf[sw_bn254.ScalarField](ballottest.MaxCount),
ForceUniqueness: emulated.ValueOf[sw_bn254.ScalarField](ballottest.ForceUniqueness),
MaxValue: emulated.ValueOf[sw_bn254.ScalarField](ballottest.MaxValue),
MinValue: emulated.ValueOf[sw_bn254.ScalarField](ballottest.MinValue),
MaxTotalCost: emulated.ValueOf[sw_bn254.ScalarField](int(math.Pow(float64(ballottest.MaxValue), float64(ballottest.CostExp))) * ballottest.MaxCount),
MinTotalCost: emulated.ValueOf[sw_bn254.ScalarField](ballottest.MaxCount),
CostExp: emulated.ValueOf[sw_bn254.ScalarField](ballottest.CostExp),
CostFromWeight: emulated.ValueOf[sw_bn254.ScalarField](ballottest.CostFromWeight),
BallotMode: circuits.BallotMode[emulated.Element[sw_bn254.ScalarField]]{
MaxCount: emulated.ValueOf[sw_bn254.ScalarField](ballottest.MaxCount),
ForceUniqueness: emulated.ValueOf[sw_bn254.ScalarField](ballottest.ForceUniqueness),
MaxValue: emulated.ValueOf[sw_bn254.ScalarField](ballottest.MaxValue),
MinValue: emulated.ValueOf[sw_bn254.ScalarField](ballottest.MinValue),
MaxTotalCost: emulated.ValueOf[sw_bn254.ScalarField](int(math.Pow(float64(ballottest.MaxValue), float64(ballottest.CostExp))) * ballottest.MaxCount),
MinTotalCost: emulated.ValueOf[sw_bn254.ScalarField](ballottest.MaxCount),
CostExp: emulated.ValueOf[sw_bn254.ScalarField](ballottest.CostExp),
CostFromWeight: emulated.ValueOf[sw_bn254.ScalarField](ballottest.CostFromWeight),
EncryptionPubKey: [2]emulated.Element[sw_bn254.ScalarField]{
emulated.ValueOf[sw_bn254.ScalarField](encryptionKeyX),
emulated.ValueOf[sw_bn254.ScalarField](encryptionKeyY),
},
},
Address: emulated.ValueOf[sw_bn254.ScalarField](voterProof.Address),
UserWeight: emulated.ValueOf[sw_bn254.ScalarField](ballottest.Weight),
EncryptionPubKey: [2]emulated.Element[sw_bn254.ScalarField]{
emulated.ValueOf[sw_bn254.ScalarField](encryptionKeyX),
emulated.ValueOf[sw_bn254.ScalarField](encryptionKeyY),
},
Nullifier: emulated.ValueOf[sw_bn254.ScalarField](voterProof.Nullifier),
Commitment: emulated.ValueOf[sw_bn254.ScalarField](voterProof.Commitment),
ProcessId: emulated.ValueOf[sw_bn254.ScalarField](voterProof.ProcessID),
Expand Down
16 changes: 16 additions & 0 deletions circuits/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package circuits

// BallotMode is a struct that contains the common inputs for all the voters.
// The values of this struct should be the same for all the voters in the same
// process. Is a generic struct that can be used with any type of circuit input.
type BallotMode[T any] struct {
MaxCount T
ForceUniqueness T
MaxValue T
MinValue T
MaxTotalCost T
MinTotalCost T
CostExp T
CostFromWeight T
EncryptionPubKey [2]T
}
Loading

0 comments on commit 117a671

Please sign in to comment.