Skip to content

Commit

Permalink
statetransition: integration test stub
Browse files Browse the repository at this point in the history
  • Loading branch information
altergui committed Jan 12, 2025
1 parent 6576edb commit d113dda
Show file tree
Hide file tree
Showing 2 changed files with 211 additions and 0 deletions.
177 changes: 177 additions & 0 deletions circuits/test/statetransition/statetransition_inputs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package statetransitiontest

import (
"fmt"
"math"
"math/big"

"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/groth16"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/frontend/cs/r1cs"
"github.com/consensys/gnark/std/algebra/native/sw_bls12377"
"github.com/consensys/gnark/std/math/emulated/emparams"
stdgroth16 "github.com/consensys/gnark/std/recursion/groth16"
"github.com/vocdoni/vocdoni-z-sandbox/circuits"
"github.com/vocdoni/vocdoni-z-sandbox/circuits/aggregator"
"github.com/vocdoni/vocdoni-z-sandbox/circuits/statetransition"
aggregatortest "github.com/vocdoni/vocdoni-z-sandbox/circuits/test/aggregator"
ballottest "github.com/vocdoni/vocdoni-z-sandbox/circuits/test/ballotproof"
)

// StateTransitionTestResults struct includes relevant data after StateTransitionCircuit
// inputs generation, including the encrypted ballots in both formats: matrix
// and plain (for hashing)
type StateTransitionTestResults struct {
ProcessId []byte
CensusRoot *big.Int
EncryptionPubKey [2]*big.Int
Nullifiers []*big.Int
Commitments []*big.Int
Addresses []*big.Int
EncryptedBallots [][ballottest.NFields][2][2]*big.Int
PlainEncryptedBallots []*big.Int
}

// StateTransitionInputsForTest returns the StateTransitionTestResults, the placeholder
// and the assigments of a StateTransitionCircuit for the processId provided
// generating nValidVoters. If something fails it returns an error.
func StateTransitionInputsForTest(processId []byte, nValidVoters int) (
*StateTransitionTestResults, *statetransition.Circuit, *statetransition.Circuit, error,
) {
// generate aggregator circuit and inputs
agInputs, agPlaceholder, agWitness, err := aggregatortest.AggregarorInputsForTest(processId, nValidVoters)
if err != nil {
return nil, nil, nil, err
}
// compile aggregoar circuit
agCCS, err := frontend.Compile(ecc.BLS12_377.ScalarField(), r1cs.NewBuilder, agPlaceholder)
if err != nil {
return nil, nil, nil, err
}
agPk, agVk, err := groth16.Setup(agCCS)
if err != nil {
return nil, nil, nil, err
}
// generate voters proofs
proofInBLS12377 := stdgroth16.Proof[sw_bls12377.G1Affine, sw_bls12377.G2Affine]{}
pubInputs := stdgroth16.Witness[emparams.BLS12377Fr]{}
// parse the witness to the circuit
fullWitness, err := frontend.NewWitness(agWitness, ecc.BLS12_377.ScalarField())
if err != nil {
return nil, nil, nil, err
}
// generate the proof
proof, err := groth16.Prove(agCCS, agPk, fullWitness, stdgroth16.GetNativeProverOptions(ecc.BW6_761.ScalarField(), ecc.BLS12_377.ScalarField()))
if err != nil {
return nil, nil, nil, fmt.Errorf("err proving proof: %w", err)
}
// convert the proof to the circuit proof type
proofInBLS12377, err = stdgroth16.ValueOfProof[sw_bls12377.G1Affine, sw_bls12377.G2Affine](proof)
if err != nil {
return nil, nil, nil, err
}
// convert the public inputs to the circuit public inputs type
publicWitness, err := fullWitness.Public()
if err != nil {
return nil, nil, nil, err
}
err = groth16.Verify(proof, agVk, publicWitness, stdgroth16.GetNativeVerifierOptions(ecc.BW6_761.ScalarField(), ecc.BLS12_377.ScalarField()))
if err != nil {
return nil, nil, nil, err
}
pubInputs, err = stdgroth16.ValueOfWitness[sw_bls12377.ScalarField](publicWitness)
if err != nil {
return nil, nil, nil, err
}
// compute public inputs hash
inputs := []*big.Int{
new(big.Int).SetBytes(agInputs.ProcessId),
agInputs.CensusRoot,
// ballotMode(), // TODO: serialize ballotmode into a bigint? or flatten it?
agInputs.EncryptionPubKey[0],
agInputs.EncryptionPubKey[1],
}
// pad voters inputs (nullifiers, commitments, addresses, plain EncryptedBallots)
nullifiers := circuits.BigIntArrayToN(agInputs.Nullifiers, aggregator.MaxVotes)
commitments := circuits.BigIntArrayToN(agInputs.Commitments, aggregator.MaxVotes)
addresses := circuits.BigIntArrayToN(agInputs.Addresses, aggregator.MaxVotes)
plainEncryptedBallots := circuits.BigIntArrayToN(agInputs.PlainEncryptedBallots, aggregator.MaxVotes*ballottest.NFields*4)
// append voters inputs (nullifiers, commitments, addresses, plain EncryptedBallots)
inputs = append(inputs, nullifiers...)
inputs = append(inputs, commitments...)
inputs = append(inputs, addresses...)
inputs = append(inputs, plainEncryptedBallots...)
// hash the inputs to generate the inputs hash
var buf [fr_bw6761.Bytes]byte
aggregatorHashFn := bw6761mimc.NewMiMC()
for _, input := range inputs {
input.FillBytes(buf[:])
_, err := aggregatorHashFn.Write(buf[:])
if err != nil {
return nil, nil, nil, err
}
}
publicHash := new(big.Int).SetBytes(aggregatorHashFn.Sum(nil))
// init final assigments stuff
finalAssigments := &statetransition.Circuit{
InputsHash: publicHash,

Check failure on line 120 in circuits/test/statetransition/statetransition_inputs.go

View workflow job for this annotation

GitHub Actions / job_go_test

unknown field InputsHash in struct literal of type statetransition.Circuit
ValidVotes: aggregator.EncodeProofsSelector(nValidVoters),

Check failure on line 121 in circuits/test/statetransition/statetransition_inputs.go

View workflow job for this annotation

GitHub Actions / job_go_test

unknown field ValidVotes in struct literal of type statetransition.Circuit
BallotMode: circuits.BallotMode[frontend.Variable]{

Check failure on line 122 in circuits/test/statetransition/statetransition_inputs.go

View workflow job for this annotation

GitHub Actions / job_go_test

cannot use circuits.BallotMode[frontend.Variable]{…} (value of type circuits.BallotMode[frontend.Variable]) as state.MerkleProof value in struct literal
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{agInputs.EncryptionPubKey[0], agInputs.EncryptionPubKey[1]},
},
ProcessId: new(big.Int).SetBytes(agInputs.ProcessId),

Check failure on line 133 in circuits/test/statetransition/statetransition_inputs.go

View workflow job for this annotation

GitHub Actions / job_go_test

unknown field ProcessId in struct literal of type statetransition.Circuit, but does have ProcessID
CensusRoot: agInputs.CensusRoot,

Check failure on line 134 in circuits/test/statetransition/statetransition_inputs.go

View workflow job for this annotation

GitHub Actions / job_go_test

cannot use agInputs.CensusRoot (variable of type *big.Int) as state.MerkleProof value in struct literal
VerifyProofs: proofInBLS12377,

Check failure on line 135 in circuits/test/statetransition/statetransition_inputs.go

View workflow job for this annotation

GitHub Actions / job_go_test

unknown field VerifyProofs in struct literal of type statetransition.Circuit
VerifyPublicInputs: pubInputs,

Check failure on line 136 in circuits/test/statetransition/statetransition_inputs.go

View workflow job for this annotation

GitHub Actions / job_go_test

unknown field VerifyPublicInputs in struct literal of type statetransition.Circuit
}
// set voters final witness stuff
for i := 0; i < nValidVoters; i++ {
finalAssigments.Nullifiers[i] = agInputs.Nullifiers[i]

Check failure on line 140 in circuits/test/statetransition/statetransition_inputs.go

View workflow job for this annotation

GitHub Actions / job_go_test

finalAssigments.Nullifiers undefined (type *statetransition.Circuit has no field or method Nullifiers)
finalAssigments.Commitments[i] = agInputs.Commitments[i]

Check failure on line 141 in circuits/test/statetransition/statetransition_inputs.go

View workflow job for this annotation

GitHub Actions / job_go_test

finalAssigments.Commitments undefined (type *statetransition.Circuit has no field or method Commitments)
finalAssigments.Addresses[i] = new(big.Int).SetBytes(vvData[i].Address.Bytes())
for j := 0; j < ballottest.NFields; j++ {
for n := 0; n < 2; n++ {
for m := 0; m < 2; m++ {
finalAssigments.EncryptedBallots[i][j][n][m] = agInputs.EncryptedBallots[i][j][n][m]
}
}
}
}
// create final placeholder
finalPlaceholder := &statetransition.Circuit{
AggregatedProofWitness: stdgroth16.Witness[sw_bls12377.ScalarField]{},
AggregatedProof: stdgroth16.Proof[sw_bls12377.G1Affine, sw_bls12377.G2Affine]{},
AggregatedProofVK: stdgroth16.VerifyingKey[sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{},
}
// fix the vote verifier verification key
fixedVk, err := stdgroth16.ValueOfVerifyingKeyFixed[sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT](agVk)
if err != nil {
return nil, nil, nil, err
}
finalPlaceholder.AggregatedProofVK = fixedVk
// // fill placeholder and witness with dummy circuits
// if err := aggregator.FillWithDummyFixed(finalPlaceholder, finalAssigments, agCCS, nValidVoters); err != nil {
// return nil, nil, nil, err
// }
return &StateTransitionTestResults{
ProcessId: agInputs.ProcessId,
CensusRoot: agInputs.CensusRoot,
EncryptionPubKey: agInputs.EncryptionPubKey,
Nullifiers: nullifiers,
Commitments: commitments,
Addresses: addresses,
EncryptedBallots: agInputs.EncryptedBallots,
PlainEncryptedBallots: plainEncryptedBallots,
}, finalPlaceholder, finalAssigments, nil
}
34 changes: 34 additions & 0 deletions circuits/test/statetransition/statetransition_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package statetransitiontest

import (
"os"
"testing"
"time"

"github.com/consensys/gnark-crypto/ecc"
"github.com/consensys/gnark/backend"
stdgroth16 "github.com/consensys/gnark/std/recursion/groth16"
"github.com/consensys/gnark/test"
qt "github.com/frankban/quicktest"
"github.com/vocdoni/vocdoni-z-sandbox/util"
)

func TestStateTransitionCircuit(t *testing.T) {
if os.Getenv("RUN_CIRCUIT_TESTS") == "" || os.Getenv("RUN_CIRCUIT_TESTS") == "false" {
t.Skip("skipping circuit tests...")
}
c := qt.New(t)
// inputs generation
now := time.Now()
processId := util.RandomBytes(20)
_, placeholder, witness, err := StateTransitionInputsForTest(processId, 3)
c.Assert(err, qt.IsNil)
c.Logf("inputs generation took %s", time.Since(now).String())
// proving
now = time.Now()
assert := test.NewAssert(t)
assert.SolvingSucceeded(placeholder, witness,
test.WithCurves(ecc.BW6_761), test.WithBackends(backend.GROTH16),
test.WithProverOpts(stdgroth16.GetNativeProverOptions(ecc.BN254.ScalarField(), ecc.BW6_761.ScalarField())))
c.Logf("proving took %s", time.Since(now).String())
}

0 comments on commit d113dda

Please sign in to comment.