Skip to content

Commit

Permalink
fixup! fixup! add Ciphertexts type
Browse files Browse the repository at this point in the history
  • Loading branch information
altergui committed Dec 20, 2024
1 parent e8c110d commit bb9afbb
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 112 deletions.
13 changes: 7 additions & 6 deletions circuits/statetransition/circuit.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/vocdoni/gnark-crypto-primitives/elgamal"
"github.com/vocdoni/gnark-crypto-primitives/utils"
"github.com/vocdoni/vocdoni-z-sandbox/state"
"github.com/vocdoni/vocdoni-z-sandbox/util"
)

var HashFn = utils.MiMCHasher
Expand Down Expand Up @@ -96,7 +97,7 @@ func (circuit Circuit) VerifyMerkleProofs(api frontend.API, hFn utils.Hasher) {

func (circuit Circuit) VerifyMerkleTransitions(api frontend.API, hFn utils.Hasher) {
// verify chain of tree transitions, order here is fundamental.
api.Println("tree transition starts with RootHashBefore:", prettyHex(circuit.RootHashBefore))
api.Println("tree transition starts with RootHashBefore:", util.PrettyHex(circuit.RootHashBefore))
root := circuit.RootHashBefore
for i := range circuit.Ballot {
root = circuit.Ballot[i].Verify(api, hFn, root)
Expand All @@ -106,7 +107,7 @@ func (circuit Circuit) VerifyMerkleTransitions(api frontend.API, hFn utils.Hashe
}
root = circuit.ResultsAdd.Verify(api, hFn, root)
root = circuit.ResultsSub.Verify(api, hFn, root)
api.Println("and final root is", prettyHex(root), "should be equal to RootHashAfter", prettyHex(circuit.RootHashAfter))
api.Println("and final root is", util.PrettyHex(root), "should be equal to RootHashAfter", util.PrettyHex(circuit.RootHashAfter))
api.AssertIsEqual(root, circuit.RootHashAfter)
}

Expand All @@ -119,19 +120,19 @@ func (circuit Circuit) VerifyBallots(api frontend.API) {
// TODO: check that Hash(NewCiphertext) matches b.NewValue
// and Hash(OldCiphertext) matches b.OldValue
ballotSum.Add(api, ballotSum,
elgamal.NewCiphertexts().Select(api, b.IsInsertOrUpdate(api), b.NewCiphertexts, zero))
elgamal.NewCiphertexts().Select(api, b.IsInsertOrUpdate(api), &b.NewCiphertexts, zero))

overwrittenSum.Add(api, overwrittenSum,
elgamal.NewCiphertexts().Select(api, b.IsUpdate(api), b.OldCiphertexts, zero))
elgamal.NewCiphertexts().Select(api, b.IsUpdate(api), &b.OldCiphertexts, zero))

ballotCount = api.Add(ballotCount, api.Select(b.IsInsertOrUpdate(api), 1, 0))
overwrittenCount = api.Add(overwrittenCount, api.Select(b.IsUpdate(api), 1, 0))
}

circuit.ResultsAdd.NewCiphertexts.AssertIsEqual(api,
circuit.ResultsAdd.OldCiphertexts.Add(api, circuit.ResultsAdd.OldCiphertexts, ballotSum))
circuit.ResultsAdd.OldCiphertexts.Add(api, &circuit.ResultsAdd.OldCiphertexts, ballotSum))
circuit.ResultsSub.NewCiphertexts.AssertIsEqual(api,
circuit.ResultsSub.OldCiphertexts.Add(api, circuit.ResultsSub.OldCiphertexts, overwrittenSum))
circuit.ResultsSub.OldCiphertexts.Add(api, &circuit.ResultsSub.OldCiphertexts, overwrittenSum))
api.AssertIsEqual(circuit.NumNewVotes, ballotCount)
api.AssertIsEqual(circuit.NumOverwrites, overwrittenCount)
}
50 changes: 24 additions & 26 deletions circuits/statetransition/circuit_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package statetransition_test

import (
"encoding/hex"
"fmt"
"math/big"
"os"
"reflect"
"testing"

"github.com/consensys/gnark-crypto/ecc"
Expand All @@ -18,6 +16,7 @@ import (
"github.com/vocdoni/vocdoni-z-sandbox/circuits/statetransition"
"github.com/vocdoni/vocdoni-z-sandbox/crypto/elgamal"
"github.com/vocdoni/vocdoni-z-sandbox/state"
"github.com/vocdoni/vocdoni-z-sandbox/util"

"github.com/vocdoni/arbo"
"go.vocdoni.io/dvote/db/metadb"
Expand Down Expand Up @@ -99,15 +98,32 @@ func TestCircuitProve(t *testing.T) {
func debugLog(t *testing.T, witness *statetransition.Circuit) {
// js, _ := json.MarshalIndent(witness, "", " ")
// fmt.Printf("\n\n%s\n\n", js)
t.Log("public: RootHashBefore", prettyHex(witness.RootHashBefore))
t.Log("public: RootHashAfter", prettyHex(witness.RootHashAfter))
t.Log("public: NumVotes", prettyHex(witness.NumNewVotes))
t.Log("public: NumOverwrites", prettyHex(witness.NumOverwrites))
t.Log("public: RootHashBefore", util.PrettyHex(witness.RootHashBefore))
t.Log("public: RootHashAfter", util.PrettyHex(witness.RootHashAfter))
t.Log("public: NumVotes", util.PrettyHex(witness.NumNewVotes))
t.Log("public: NumOverwrites", util.PrettyHex(witness.NumOverwrites))
for name, mts := range map[string][statetransition.VoteBatchSize]state.MerkleTransition{
"Ballot": witness.Ballot,
"Commitment": witness.Commitment,
} {
for _, mt := range mts {
t.Log(name, "transitioned", "(root", util.PrettyHex(mt.OldRoot), "->", util.PrettyHex(mt.NewRoot), ")",
"value", mt.OldValue, "->", mt.NewValue,
)
for i := range mt.OldCiphertexts {
t.Log(name, i, "elgamal.C1.X", mt.OldCiphertexts[i].C1.X, "->", mt.NewCiphertexts[i].C1.X)
t.Log(name, i, "elgamal.C1.Y", mt.OldCiphertexts[i].C1.Y, "->", mt.NewCiphertexts[i].C1.Y)
t.Log(name, i, "elgamal.C2.X", mt.OldCiphertexts[i].C2.X, "->", mt.NewCiphertexts[i].C2.X)
t.Log(name, i, "elgamal.C2.Y", mt.OldCiphertexts[i].C2.Y, "->", mt.NewCiphertexts[i].C2.Y)
}
}
}

for name, mt := range map[string]state.MerkleTransition{
"ResultsAdd": witness.ResultsAdd,
"ResultsSub": witness.ResultsSub,
} {
t.Log(name, "transitioned", "(root", prettyHex(mt.OldRoot), "->", prettyHex(mt.NewRoot), ")",
t.Log(name, "transitioned", "(root", util.PrettyHex(mt.OldRoot), "->", util.PrettyHex(mt.NewRoot), ")",
"value", mt.OldValue, "->", mt.NewValue,
)
for i := range mt.OldCiphertexts {
Expand All @@ -119,24 +135,6 @@ func debugLog(t *testing.T, witness *statetransition.Circuit) {
}
}

func prettyHex(v frontend.Variable) string {
type hasher interface {
HashCode() [16]byte
}
switch v := v.(type) {
case (*big.Int):
return hex.EncodeToString(arbo.BigIntToBytes(32, v)[:4])
case int:
return fmt.Sprintf("%d", v)
case []byte:
return fmt.Sprintf("%x", v[:4])
case hasher:
return fmt.Sprintf("%x", v.HashCode())
default:
return fmt.Sprintf("(%v)=%+v", reflect.TypeOf(v), v)
}
}

type CircuitBallots struct {
statetransition.Circuit
}
Expand Down Expand Up @@ -271,7 +269,7 @@ func newMockVote(index, amount int64) *state.Vote {
ballot, err := elgamal.NewCiphertexts(publicKey).Encrypt(
[elgamal.NumCiphertexts]*big.Int{
big.NewInt(int64(amount)),
big.NewInt(int64(amount)),
big.NewInt(int64(amount + 1)),
},
publicKey, nil)
if err != nil {
Expand Down
29 changes: 0 additions & 29 deletions circuits/statetransition/util.go

This file was deleted.

8 changes: 2 additions & 6 deletions circuits/statetransition/witness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,15 @@ func GenerateWitnesses(o *state.State) (*statetransition.Circuit, error) {
}

// update ResultsAdd
witness.ResultsAdd.OldCiphertexts = o.ResultsAdd.ToGnark()
witness.ResultsAdd.NewCiphertexts = o.ResultsAdd.Add(o.ResultsAdd, o.BallotSum).ToGnark()
witness.ResultsAdd, err = o.MerkleTransitionFromAddOrUpdate(
state.KeyResultsAdd, o.ResultsAdd.Serialize())
state.KeyResultsAdd, o.ResultsAdd.Add(o.ResultsAdd, o.BallotSum).Serialize())
if err != nil {
return nil, fmt.Errorf("ResultsAdd: %w", err)
}

// update ResultsSub
witness.ResultsSub.OldCiphertexts = o.ResultsSub.ToGnark()
witness.ResultsSub.NewCiphertexts = o.ResultsSub.Add(o.ResultsSub, o.OverwriteSum).ToGnark()
witness.ResultsSub, err = o.MerkleTransitionFromAddOrUpdate(
state.KeyResultsSub, o.ResultsSub.Serialize())
state.KeyResultsSub, o.ResultsSub.Add(o.ResultsSub, o.OverwriteSum).Serialize())
if err != nil {
return nil, fmt.Errorf("ResultsSub: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion crypto/elgamal/ciphertext.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (cs *Ciphertexts) Deserialize(data []byte) error {
func (cs *Ciphertexts) ToGnark() *gelgamal.Ciphertexts {

Check failure on line 106 in crypto/elgamal/ciphertext.go

View workflow job for this annotation

GitHub Actions / job_go_test

undefined: gelgamal.Ciphertexts

Check failure on line 106 in crypto/elgamal/ciphertext.go

View workflow job for this annotation

GitHub Actions / job_go_checks

undefined: gelgamal.Ciphertexts
gcs := &gelgamal.Ciphertexts{}

Check failure on line 107 in crypto/elgamal/ciphertext.go

View workflow job for this annotation

GitHub Actions / job_go_test

undefined: gelgamal.Ciphertexts

Check failure on line 107 in crypto/elgamal/ciphertext.go

View workflow job for this annotation

GitHub Actions / job_go_checks

undefined: gelgamal.Ciphertexts
for i := range cs {
gcs[i] = cs[i].ToGnark()
gcs[i] = *cs[i].ToGnark()
}
return gcs
}
Expand Down
74 changes: 30 additions & 44 deletions state/merkleproof.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@ package state

import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"math/big"
"reflect"

"github.com/consensys/gnark/frontend"
"github.com/vocdoni/arbo"
gelgamal "github.com/vocdoni/gnark-crypto-primitives/elgamal"
"github.com/vocdoni/gnark-crypto-primitives/utils"

garbo "github.com/vocdoni/gnark-crypto-primitives/tree/arbo"
"github.com/vocdoni/gnark-crypto-primitives/tree/smt"
"github.com/vocdoni/gnark-crypto-primitives/utils"
"github.com/vocdoni/vocdoni-z-sandbox/crypto/elgamal"
"github.com/vocdoni/vocdoni-z-sandbox/util"
)

// ArboProof stores the proof in arbo native types
Expand Down Expand Up @@ -95,6 +94,18 @@ func (o *State) GenMerkleProof(k []byte) (MerkleProof, error) {

// MerkleProofFromArboProof converts an ArboProof into a MerkleProof
func MerkleProofFromArboProof(p *ArboProof) MerkleProof {
padSiblings := func(unpackedSiblings [][]byte) [MaxLevels]frontend.Variable {
paddedSiblings := [MaxLevels]frontend.Variable{}
for i := range MaxLevels {
if i < len(unpackedSiblings) {
paddedSiblings[i] = arbo.BytesToBigInt(unpackedSiblings[i])
} else {
paddedSiblings[i] = big.NewInt(0)
}
}
return paddedSiblings
}

fnc := 0 // inclusion
if !p.Existence {
fnc = 1 // non-inclusion
Expand All @@ -108,29 +119,23 @@ func MerkleProofFromArboProof(p *ArboProof) MerkleProof {
}
}

func padSiblings(unpackedSiblings [][]byte) [MaxLevels]frontend.Variable {
paddedSiblings := [MaxLevels]frontend.Variable{}
for i := range MaxLevels {
if i < len(unpackedSiblings) {
paddedSiblings[i] = arbo.BytesToBigInt(unpackedSiblings[i])
} else {
paddedSiblings[i] = big.NewInt(0)
}
}
return paddedSiblings
}

// Verify uses garbo.CheckInclusionProof to verify that:
// - mp.Root matches passed root
// - Key + Value belong to Root
func (mp *MerkleProof) VerifyProof(api frontend.API, hFn utils.Hasher, root frontend.Variable) {
api.Println("verify proof", mp.String()) // TODO: remove this debug log

api.AssertIsEqual(root, mp.Root)

if err := garbo.CheckInclusionProof(api, hFn, mp.Key, mp.Value, mp.Root, mp.Siblings[:]); err != nil {
panic(err)
}
}

func (mp *MerkleProof) String() string {
return fmt.Sprint(mp.Key, "=", mp.Value, " -> ", util.PrettyHex(mp.Root))
}

// MerkleTransition stores a pair of leaves and root hashes, and a single path common to both proofs
type MerkleTransition struct {
// NewKey + NewValue hashed through Siblings path, should produce NewRoot hash
Expand All @@ -150,8 +155,8 @@ type MerkleTransition struct {
// TODO: replace Is*ElGamal by a check on len(Ciphertext) or something?
IsOldElGamal frontend.Variable
IsNewElGamal frontend.Variable
OldCiphertexts *gelgamal.Ciphertexts
NewCiphertexts *gelgamal.Ciphertexts
OldCiphertexts gelgamal.Ciphertexts
NewCiphertexts gelgamal.Ciphertexts
}

// MerkleTransitionFromArboProofPair generates a MerkleTransition based on the pair of proofs passed
Expand Down Expand Up @@ -194,8 +199,8 @@ func MerkleTransitionFromArboProofPair(before, after *ArboProof) MerkleTransitio
Fnc1: fnc1,
IsOldElGamal: 0,
IsNewElGamal: 0,
OldCiphertexts: gelgamal.NewCiphertexts(),
NewCiphertexts: gelgamal.NewCiphertexts(),
OldCiphertexts: *gelgamal.NewCiphertexts(),
NewCiphertexts: *gelgamal.NewCiphertexts(),
}
}

Expand All @@ -222,8 +227,8 @@ func (o *State) MerkleTransitionFromAddOrUpdate(k []byte, v []byte) (MerkleTrans
mp.IsNewElGamal = 1
}

mp.OldCiphertexts = oldCiphertexts.ToGnark()
mp.NewCiphertexts = newCiphertexts.ToGnark()
mp.OldCiphertexts = *oldCiphertexts.ToGnark()
mp.NewCiphertexts = *newCiphertexts.ToGnark()

return mp, nil
}
Expand Down Expand Up @@ -252,7 +257,7 @@ func (o *State) MerkleTransitionFromNoop() (MerkleTransition, error) {
//
// and returns mp.NewRoot
func (mp *MerkleTransition) Verify(api frontend.API, hFn utils.Hasher, oldRoot frontend.Variable) frontend.Variable {
mp.printDebugLog(api)
api.Println("verify transition", mp.String()) // TODO: remove this debug log

api.AssertIsEqual(oldRoot, mp.OldRoot)

Expand Down Expand Up @@ -281,28 +286,9 @@ func (mp *MerkleTransition) Verify(api frontend.API, hFn utils.Hasher, oldRoot f
return mp.NewRoot
}

// TODO: remove this debug log
func (mp *MerkleTransition) printDebugLog(api frontend.API) {
prettyHex := func(v frontend.Variable) string {
type hasher interface {
HashCode() [16]byte
}
switch v := v.(type) {
case (*big.Int):
return hex.EncodeToString(arbo.BigIntToBytes(32, v)[:4])
case int:
return fmt.Sprintf("%d", v)
case []byte:
return fmt.Sprintf("%x", v[:4])
case hasher:
return fmt.Sprintf("%x", v.HashCode())
default:
return fmt.Sprintf("(%v)=%+v", reflect.TypeOf(v), v)
}
}

api.Println("verify transition", prettyHex(mp.OldRoot), "->", prettyHex(mp.NewRoot), "|",
mp.OldKey, "=", mp.OldValue, "->", mp.NewKey, "=", mp.NewValue)
func (mp *MerkleTransition) String() string {
return fmt.Sprint(util.PrettyHex(mp.OldRoot), " -> ", util.PrettyHex(mp.NewRoot), " | ",
mp.OldKey, "=", mp.OldValue, " -> ", mp.NewKey, "=", mp.NewValue)
}

// IsUpdate returns true when mp.Fnc0 == 0 && mp.Fnc1 == 1
Expand Down
23 changes: 23 additions & 0 deletions util/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ package util

import (
"crypto/rand"
"encoding/hex"
"fmt"
"math/big"
"reflect"

"github.com/consensys/gnark/frontend"
"github.com/vocdoni/arbo"
)

// RandomBytes generates a random byte slice of length n.
Expand Down Expand Up @@ -62,3 +67,21 @@ func BigToFF(iv *big.Int) *big.Int {
}
return z.Mod(iv, bn254BaseField)
}

func PrettyHex(v frontend.Variable) string {
type hasher interface {
HashCode() [16]byte
}
switch v := v.(type) {
case (*big.Int):
return hex.EncodeToString(arbo.BigIntToBytes(32, v)[:4])
case int:
return fmt.Sprintf("%d", v)
case []byte:
return fmt.Sprintf("%x", v[:4])
case hasher:
return fmt.Sprintf("%x", v.HashCode())
default:
return fmt.Sprintf("(%v)=%+v", reflect.TypeOf(v), v)
}
}

0 comments on commit bb9afbb

Please sign in to comment.