@@ -67,7 +67,7 @@ type Results struct {
6767}
6868
6969// ProcessProofs struct contains the Merkle proofs for the process for the ID
70- // CensusRoot , BallotMode and EncryptionKey.
70+ // CensusOrigin , BallotMode and EncryptionKey.
7171type ProcessProofs struct {
7272 ID merkleproof.MerkleProof
7373 CensusOrigin merkleproof.MerkleProof
@@ -84,9 +84,8 @@ type CensusProofs struct {
8484}
8585
8686// VotesProofs struct contains the Merkle transition proofs for the ballots and
87- // commitments .
87+ // voteIDs .
8888type VotesProofs struct {
89- // Key is Address, LeafHash is smt.Hash1(encoded(Ballot.Serialize()))
9089 Ballot [params .VotesPerBatch ]merkleproof.MerkleTransition
9190 VoteIDs [params .VotesPerBatch ]merkleproof.MerkleTransition
9291}
@@ -114,13 +113,16 @@ func (circuit StateTransitionCircuit) Define(api frontend.API) error {
114113 // recursive proof
115114 circuit .VerifyAggregatorProof (api , isRealVote )
116115 // current state
117- circuit .VerifyMerkleProofs (api , HashFn )
116+ circuit .VerifyProcessProofKeys (api )
117+ circuit .VerifyProcessProofs (api , HashFn )
118118 // state transition
119+ circuit .VerifyMerkleTransitionKeys (api )
119120 circuit .VerifyMerkleTransitions (api , isRealVote )
120121 circuit .VerifyRootTransition (api , HashFn )
121122 // leaf hashes
122123 circuit .VerifyLeafHashes (api , HashFn )
123124 // censuses
125+ circuit .VerifyIsValidCensusOrigin (api )
124126 circuit .VerifyMerkleCensusProofs (api , isRealVote )
125127 circuit .VerifyCSPCensusProofs (api , isRealVote )
126128 // votes reencryption and ballots
@@ -249,33 +251,49 @@ func (circuit StateTransitionCircuit) VerifyAggregatorProof(api frontend.API, is
249251// To reencrypt the votes, it adds the encrypted zero ballot to the original
250252// ballot. The encrypted zero uses the reencryptionK as the randomness.
251253func (circuit StateTransitionCircuit ) VerifyReencryptedVotes (api frontend.API , isRealVote []frontend.Variable ) {
252- lastK := frontend .Variable (circuit .ReencryptionK )
253- for i , v := range circuit .Votes {
254- var err error
255- var reencryptedBallot * circuits.Ballot
256- reencryptedBallot , lastK , err = v .Ballot .Reencrypt (api , circuit .Process .EncryptionKey , lastK )
254+ lastK := circuit .ReencryptionK
255+ for i := range params .VotesPerBatch {
256+ v := circuit .Votes [i ]
257+ reencryptedBallot , k , err := v .Ballot .Reencrypt (api , circuit .Process .EncryptionKey , lastK )
257258 if err != nil {
258259 circuits .FrontendError (api , "failed to reencrypt ballot: " , err )
259260 return
260261 }
261262 circuits .AssertTrueIf (api , isRealVote [i ], v .ReencryptedBallot .IsEqual (api , reencryptedBallot ))
263+ lastK = k
262264 }
263265}
264266
265- // VerifyMerkleProofs verifies that the ProcessID, CensusRoot, BallotMode
267+ // VerifyProcessProofKeys asserts that the process proofs are bound to the
268+ // canonical state keys for each process parameter.
269+ func (circuit StateTransitionCircuit ) VerifyProcessProofKeys (api frontend.API ) {
270+ api .AssertIsEqual (circuit .ProcessProofs .ID .Key , params .StateKeyProcessID )
271+ api .AssertIsEqual (circuit .ProcessProofs .CensusOrigin .Key , params .StateKeyCensusOrigin )
272+ api .AssertIsEqual (circuit .ProcessProofs .BallotMode .Key , params .StateKeyBallotMode )
273+ api .AssertIsEqual (circuit .ProcessProofs .EncryptionKey .Key , params .StateKeyEncryptionKey )
274+ }
275+
276+ // VerifyProcessProofs verifies that the ProcessID, CensusOrigin, BallotMode
266277// and EncryptionKey belong to the RootHashBefore. It uses the MerkleProof
267278// structure to verify the proofs. The proofs are verified using the Verify
268279// function of the MerkleProof structure.
269- func (circuit StateTransitionCircuit ) VerifyMerkleProofs (api frontend.API , hFn utils.Hasher ) {
280+ func (circuit StateTransitionCircuit ) VerifyProcessProofs (api frontend.API , hFn utils.Hasher ) {
270281 circuit .ProcessProofs .ID .Verify (api , hFn , circuit .RootHashBefore )
271282 circuit .ProcessProofs .CensusOrigin .Verify (api , hFn , circuit .RootHashBefore )
272283 circuit .ProcessProofs .BallotMode .Verify (api , hFn , circuit .RootHashBefore )
273284 circuit .ProcessProofs .EncryptionKey .Verify (api , hFn , circuit .RootHashBefore )
274285}
275286
287+ // VerifyIsValidCensusOrigin asserts that the census origin is one of the
288+ // variants currently supported by the circuit.
289+ func (circuit StateTransitionCircuit ) VerifyIsValidCensusOrigin (api frontend.API ) {
290+ api .AssertIsEqual (census .IsValidCensusOrigin (api , circuit .Process .CensusOrigin ), 1 )
291+ }
292+
276293// VerifyMerkleTransitions enforces that each MerkleTransition is of the expected type:
277294// - Ballot transitions must be INSERT or UPDATE
278295// - VoteID transitions must be INSERT
296+ // - ResultsAdd and ResultsSub transitions must be UPDATE
279297// - all dummy slots must be NOOP
280298func (circuit StateTransitionCircuit ) VerifyMerkleTransitions (api frontend.API , isRealVote []frontend.Variable ) {
281299 for i := range params .VotesPerBatch {
@@ -287,6 +305,20 @@ func (circuit StateTransitionCircuit) VerifyMerkleTransitions(api frontend.API,
287305 circuits .AssertTrueIf (api , isDummy , circuit .VotesProofs .Ballot [i ].IsNoop (api ))
288306 circuits .AssertTrueIf (api , isDummy , circuit .VotesProofs .VoteIDs [i ].IsNoop (api ))
289307 }
308+ api .AssertIsEqual (circuit .ResultsProofs .ResultsAdd .IsUpdate (api ), 1 )
309+ api .AssertIsEqual (circuit .ResultsProofs .ResultsSub .IsUpdate (api ), 1 )
310+ }
311+
312+ // VerifyMerkleTransitionKeys asserts that the merkle transition keys are bound to the
313+ // canonical state keys or namespaces.
314+ func (circuit StateTransitionCircuit ) VerifyMerkleTransitionKeys (api frontend.API ) {
315+ circuit .ResultsProofs .ResultsAdd .VerifyNewKey (api , params .StateKeyResultsAdd )
316+ circuit .ResultsProofs .ResultsSub .VerifyNewKey (api , params .StateKeyResultsSub )
317+ // Votes
318+ for i := range params .VotesPerBatch {
319+ circuit .VotesProofs .VoteIDs [i ].VerifyNewKey (api , circuit .Votes [i ].VoteID )
320+ circuit .VotesProofs .Ballot [i ].VerifyNewKey (api , circuit .Votes [i ].BallotIndex )
321+ }
290322}
291323
292324// VerifyRootTransition verifies that the chain of tree transitions is valid.
@@ -326,33 +358,36 @@ func (circuit StateTransitionCircuit) VerifyLeafHashes(api frontend.API, hFn uti
326358 return
327359 }
328360 // Votes
329- for i , v := range circuit .Votes {
330- // Address
331- circuit .VotesProofs .Ballot [i ].VerifyNewKey (api , v .BallotIndex )
361+ for i := range params .VotesPerBatch {
332362 // Ballot
333- if err := circuit .VotesProofs .Ballot [i ].VerifyNewLeafHash (api , hFn , v .ReencryptedBallot .SerializeVars ()... ); err != nil {
363+ if err := circuit .VotesProofs .Ballot [i ].VerifyNewLeafHash (api , hFn , circuit . Votes [ i ] .ReencryptedBallot .SerializeVars ()... ); err != nil {
334364 circuits .FrontendError (api , "failed to verify ballot vote proof leaf hash: " , err )
335365 return
336366 }
337367 // OverwrittenBallot
338- if err := circuit .VotesProofs .Ballot [i ].VerifyOverwrittenBallot (api , hFn , v .OverwrittenBallot .SerializeVars ()... ); err != nil {
368+ if err := circuit .VotesProofs .Ballot [i ].VerifyOverwrittenBallot (api , hFn , circuit . Votes [ i ] .OverwrittenBallot .SerializeVars ()... ); err != nil {
339369 circuits .FrontendError (api , "failed to verify ballot vote proof leaf hash: " , err )
340370 return
341371 }
372+ // VoteID
373+ if err := circuit .VotesProofs .VoteIDs [i ].VerifyNewLeafHash (api , hFn , params .VoteIDLeafValue ); err != nil {
374+ circuits .FrontendError (api , "failed to verify voteID vote proof leaf hash: " , err )
375+ return
376+ }
342377 }
343378 // Results
344379 if err := circuit .ResultsProofs .ResultsAdd .VerifyOldLeafHash (api , hFn , circuit .Results .OldResultsAdd .SerializeVars ()... ); err != nil {
345380 circuits .FrontendError (api , "failed to verify add results proof old leaf hash: " , err )
346381 return
347382 }
348- if err := circuit .ResultsProofs .ResultsSub .VerifyOldLeafHash (api , hFn , circuit .Results .OldResultsSub .SerializeVars ()... ); err != nil {
349- circuits .FrontendError (api , "failed to verify sub results proof old leaf hash: " , err )
350- return
351- }
352383 if err := circuit .ResultsProofs .ResultsAdd .VerifyNewLeafHash (api , hFn , circuit .Results .NewResultsAdd .SerializeVars ()... ); err != nil {
353384 circuits .FrontendError (api , "failed to verify add results proof new leaf hash: " , err )
354385 return
355386 }
387+ if err := circuit .ResultsProofs .ResultsSub .VerifyOldLeafHash (api , hFn , circuit .Results .OldResultsSub .SerializeVars ()... ); err != nil {
388+ circuits .FrontendError (api , "failed to verify sub results proof old leaf hash: " , err )
389+ return
390+ }
356391 if err := circuit .ResultsProofs .ResultsSub .VerifyNewLeafHash (api , hFn , circuit .Results .NewResultsSub .SerializeVars ()... ); err != nil {
357392 circuits .FrontendError (api , "failed to verify sub results proof new leaf hash: " , err )
358393 return
@@ -365,9 +400,8 @@ func (circuit StateTransitionCircuit) VerifyBlobs(api frontend.API) {
365400 // Build blob and verify evaluation
366401 //
367402 // The blob is built as follows:
368- // - First, we add the new results (addition and subtraction) - always present
369- // - Then, we add the votes sequentially (no padding)
370- // - Finally, we add a sentinel (voteID = 0x0) to mark end of votes
403+ // - First, we add the new results (addition and subtraction) and VotersCount
404+ // - Finally, we add exactly VotersCount votes sequentially
371405 // Each ballot coordinate is represented as a field element (32 bytes).
372406 // Each field element is represented as a big-endian byte array.
373407 // The blob is a fixed-size array (FieldElementsPerBlob * BytesPerFieldElement).
@@ -380,30 +414,23 @@ func (circuit StateTransitionCircuit) VerifyBlobs(api frontend.API) {
380414 blobIndex ++
381415 }
382416 }
383- // Always include results (no sentinel applies to them)
417+ // Always include results.
384418 appendBallotMasked (circuit .Results .NewResultsAdd , 1 )
385419 appendBallotMasked (circuit .Results .NewResultsSub , 1 )
386- // Votes section with sentinel handling.
387- // keep==1 means "we haven't seen sentinel yet". Once we see voteID==0,
388- // keep becomes 0 and stays 0, zeroing out everything afterwards.
389- keep := frontend .Variable (1 )
420+ blob [blobIndex ] = circuit .VotersCount
421+ blobIndex ++
422+ isRealVote := circuit .VoteMask (api )
390423 for i := range params .VotesPerBatch {
391- voteID := circuit .Votes [i ].VoteID
392- isZero := api .IsZero (voteID ) // 1 if voteID==0 else 0
393- notZero := api .Sub (1 , isZero ) // 1 if voteID!=0 else 0
394- // Only write this vote if keep==1 AND voteID!=0
395- writeMask := api .Mul (keep , notZero )
424+ writeMask := isRealVote [i ]
396425 // VoteID, Address and BallotIndex
397- blob [blobIndex ] = api .Mul (writeMask , voteID )
426+ blob [blobIndex ] = api .Mul (writeMask , circuit . Votes [ i ]. VoteID )
398427 blobIndex ++
399428 blob [blobIndex ] = api .Mul (writeMask , circuit .Votes [i ].Address )
400429 blobIndex ++
401430 blob [blobIndex ] = api .Mul (writeMask , circuit .Votes [i ].BallotIndex )
402431 blobIndex ++
403432 // Reencrypted ballot (masked)
404433 appendBallotMasked (circuit .Votes [i ].ReencryptedBallot , writeMask )
405- // Update keep for next iterations: once we saw 0, keep→0 forever
406- keep = api .Mul (keep , notZero )
407434 }
408435 // Fill the rest of the blob with zeros
409436 for i := blobIndex ; i < len (blob ); i ++ {
@@ -431,9 +458,9 @@ func (circuit StateTransitionCircuit) VerifyBallots(api frontend.API) {
431458 sumOfAllBallots , sumOfOverwrittenBallots , zero := circuits .NewBallot (), circuits .NewBallot (), circuits .NewBallot ()
432459 var votersCount , overwrittenVotesCount frontend.Variable = 0 , 0
433460
434- for i , b := range circuit . VotesProofs . Ballot {
435- isInsertOrUpdate := b .IsInsertOrUpdate (api )
436- isUpdate := b .IsUpdate (api )
461+ for i := range params . VotesPerBatch {
462+ isInsertOrUpdate := circuit . VotesProofs . Ballot [ i ] .IsInsertOrUpdate (api )
463+ isUpdate := circuit . VotesProofs . Ballot [ i ] .IsUpdate (api )
437464
438465 ballot := circuits .NewBallot ().Select (api , isInsertOrUpdate , & circuit .Votes [i ].ReencryptedBallot , zero )
439466 sumOfAllBallots .Add (api , sumOfAllBallots , ballot )
0 commit comments