From 8e5ec26e337beb83bba7dbe96d3803bd9a78c875 Mon Sep 17 00:00:00 2001 From: Gui Iribarren Date: Wed, 18 Dec 2024 13:01:57 +0100 Subject: [PATCH] fixup! pointers, errors --- circuits/statetransition/circuit_test.go | 28 +++++++++++++++-------- circuits/statetransition/witness_test.go | 2 +- crypto/elgamal/ciphertext.go | 29 ++++++++++++------------ crypto/elgamal/ciphertext_test.go | 11 ++++----- state/merkleproof.go | 8 +++++-- state/state.go | 17 +++++++------- state/vote.go | 10 ++++---- 7 files changed, 58 insertions(+), 47 deletions(-) diff --git a/circuits/statetransition/circuit_test.go b/circuits/statetransition/circuit_test.go index c89c726..880b8ca 100644 --- a/circuits/statetransition/circuit_test.go +++ b/circuits/statetransition/circuit_test.go @@ -246,11 +246,15 @@ func newMockState(t *testing.T) *state.State { return s } +const ( + mockNullifiersOffset = 100 // mock, should really be a prefix, not an offset + mockAddressesOffset = 200 // mock, should really be a prefix, not an offset +) + // newMockVote creates a new vote -func newMockVote(nullifier, amount uint64) state.Vote { - var v state.Vote - v.Nullifier = arbo.BigIntToBytes(state.MaxKeyLen, - big.NewInt(int64(nullifier)+int64(state.KeyNullifiersOffset))) // mock +func newMockVote(index, amount int64) *state.Vote { + nullifier := arbo.BigIntToBytes(state.MaxKeyLen, + big.NewInt(int64(index)+int64(mockNullifiersOffset))) // mock // generate a public mocked key publicKey, _, err := elgamal.GenerateKey(state.Curve) @@ -258,15 +262,19 @@ func newMockVote(nullifier, amount uint64) state.Vote { panic(fmt.Errorf("error generating public key: %v", err)) } - c, err := elgamal.NewCiphertext(publicKey).Encrypt(big.NewInt(int64(amount)), publicKey, nil) + ballot, err := elgamal.NewCiphertext(publicKey).Encrypt(big.NewInt(int64(amount)), publicKey, nil) if err != nil { panic(fmt.Errorf("error encrypting: %v", err)) } - v.Ballot = c + address := arbo.BigIntToBytes(state.MaxKeyLen, + big.NewInt(int64(index)+int64(mockAddressesOffset))) // mock + commitment := big.NewInt(amount + 256) - v.Address = arbo.BigIntToBytes(state.MaxKeyLen, - big.NewInt(int64(nullifier)+int64(state.KeyAddressesOffset))) // mock - v.Commitment.SetUint64(amount + 256) // mock - return v + return &state.Vote{ + Nullifier: nullifier, + Ballot: ballot, + Address: address, + Commitment: commitment, + } } diff --git a/circuits/statetransition/witness_test.go b/circuits/statetransition/witness_test.go index ea4002f..63d33e9 100644 --- a/circuits/statetransition/witness_test.go +++ b/circuits/statetransition/witness_test.go @@ -54,7 +54,7 @@ func GenerateWitnesses(o *state.State) (*statetransition.Circuit, error) { for i := range witness.Commitment { if i < len(o.Votes()) { witness.Commitment[i], err = o.MerkleTransitionFromAddOrUpdate( - o.Votes()[i].Address, arbo.BigIntToBytes(32, &o.Votes()[i].Commitment)) + o.Votes()[i].Address, arbo.BigIntToBytes(32, o.Votes()[i].Commitment)) } else { witness.Commitment[i], err = o.MerkleTransitionFromNoop() } diff --git a/crypto/elgamal/ciphertext.go b/crypto/elgamal/ciphertext.go index b1e4a86..bcc9266 100644 --- a/crypto/elgamal/ciphertext.go +++ b/crypto/elgamal/ciphertext.go @@ -13,6 +13,9 @@ import ( "github.com/vocdoni/vocdoni-z-sandbox/crypto/ecc/format" ) +// size in bytes needed to serialize an ecc.Point coord +const sizePointCoord = 32 + // Ciphertext represents an ElGamal encrypted message with homomorphic properties. // It is a wrapper for convenience of the elGamal ciphersystem that encapsulates the two points of a ciphertext. type Ciphertext struct { @@ -61,39 +64,35 @@ func (z *Ciphertext) Serialize() []byte { c1x, c1y := format.FromTEtoRTE(z.C1.Point()) c2x, c2y := format.FromTEtoRTE(z.C2.Point()) for _, bi := range []*big.Int{c1x, c1y, c2x, c2y} { - if _, err := buf.Write(arbo.BigIntToBytes(32, bi)); err != nil { - panic(err) - } + buf.Write(arbo.BigIntToBytes(sizePointCoord, bi)) } return buf.Bytes() } // Deserialize reconstructs an Ciphertext from a slice of bytes. -// The input must be of len 4*32 bytes (otherwise it panics), +// The input must be of len 4*32 bytes (otherwise it returns an error), // representing the C1.X, C1.Y, C2.X, C2.Y as little-endian, // in reduced twisted edwards form. -func (z *Ciphertext) Deserialize(data []byte) { - const fieldSize = 32 // Each field element is 32 bytes - expectedLen := 4 * fieldSize - +func (z *Ciphertext) Deserialize(data []byte) error { // Validate the input length - if len(data) != expectedLen { - panic(fmt.Errorf("invalid input length: got %d bytes, expected %d bytes", len(data), expectedLen)) + if len(data) != 4*sizePointCoord { + return fmt.Errorf("invalid input length: got %d bytes, expected %d bytes", len(data), 4*sizePointCoord) } // Helper function to extract *big.Int from a 32-byte slice readBigInt := func(offset int) *big.Int { - return arbo.BytesToBigInt(data[offset : offset+fieldSize]) + return arbo.BytesToBigInt(data[offset : offset+sizePointCoord]) } // Deserialize each field z.C1 = z.C1.SetPoint(format.FromRTEtoTE( - readBigInt(0*fieldSize), - readBigInt(1*fieldSize), + readBigInt(0*sizePointCoord), + readBigInt(1*sizePointCoord), )) z.C2 = z.C2.SetPoint(format.FromRTEtoTE( - readBigInt(2*fieldSize), - readBigInt(3*fieldSize), + readBigInt(2*sizePointCoord), + readBigInt(3*sizePointCoord), )) + return nil } // Marshal converts Ciphertext to a byte slice. diff --git a/crypto/elgamal/ciphertext_test.go b/crypto/elgamal/ciphertext_test.go index cbffdd6..fe26867 100644 --- a/crypto/elgamal/ciphertext_test.go +++ b/crypto/elgamal/ciphertext_test.go @@ -99,7 +99,8 @@ func TestCiphertext_SerializeDeserialize(t *testing.T) { // Test deserialization deserialized := NewCiphertext(publicKey) - deserialized.Deserialize(serialized) + err = deserialized.Deserialize(serialized) + c.Assert(err, qt.IsNil) // Compare points x1, y1 := encrypted.C1.Point() @@ -171,14 +172,12 @@ func TestCiphertext_String(t *testing.T) { c.Assert(str, qt.Matches, `\{C1: .+, C2: .+\}`) } -func TestCiphertext_DeserializePanic(t *testing.T) { +func TestCiphertext_DeserializeError(t *testing.T) { c := qt.New(t) cipher := NewCiphertext(curves.New(curves.CurveTypeBN254)) // Test with invalid length, should panic - c.Assert(func() { - cipher.Deserialize(make([]byte, 127)) // Should be 128 - }, - qt.PanicMatches, "invalid input length.*") + c.Assert(cipher.Deserialize(make([]byte, 127)), // Should be 128 + qt.ErrorMatches, "invalid input length.*") } diff --git a/state/merkleproof.go b/state/merkleproof.go index 481c71e..3280a66 100644 --- a/state/merkleproof.go +++ b/state/merkleproof.go @@ -205,11 +205,15 @@ func (o *State) MerkleTransitionFromAddOrUpdate(k []byte, v []byte) (MerkleTrans oldCiphertext, newCiphertext := elgamal.NewCiphertext(Curve), elgamal.NewCiphertext(Curve) if len(mpBefore.Value) > 32 { - oldCiphertext.Deserialize(mpBefore.Value) + if err := oldCiphertext.Deserialize(mpBefore.Value); err != nil { + return MerkleTransition{}, err + } mp.IsOldElGamal = 1 } if len(mpAfter.Value) > 32 { - newCiphertext.Deserialize(mpAfter.Value) + if err := newCiphertext.Deserialize(mpAfter.Value); err != nil { + return MerkleTransition{}, err + } mp.IsNewElGamal = 1 } diff --git a/state/state.go b/state/state.go index 06dd66d..bfa2fc2 100644 --- a/state/state.go +++ b/state/state.go @@ -34,9 +34,6 @@ var ( KeyEncryptionKey = []byte{0x03} KeyResultsAdd = []byte{0x04} KeyResultsSub = []byte{0x05} - - KeyNullifiersOffset = 100 // mock, should really be a prefix, not an offset - KeyAddressesOffset = 200 // mock, should really be a prefix, not an offsest ) // State represents a state tree @@ -53,7 +50,7 @@ type State struct { OverwriteSum *encrypt.Ciphertext ballotCount int overwriteCount int - votes []Vote + votes []*Vote } // New creates or opens a State stored in the passed database. @@ -121,21 +118,25 @@ func (o *State) StartBatch() error { if err != nil { return err } - o.ResultsAdd.Deserialize(v) + if err := o.ResultsAdd.Deserialize(v); err != nil { + return err + } } { _, v, err := o.tree.Get(KeyResultsSub) if err != nil { return err } - o.ResultsSub.Deserialize(v) + if err := o.ResultsSub.Deserialize(v); err != nil { + return err + } } o.BallotSum = elgamal.NewCiphertext(Curve) o.OverwriteSum = elgamal.NewCiphertext(Curve) o.ballotCount = 0 o.overwriteCount = 0 - o.votes = []Vote{} + o.votes = []*Vote{} return nil } @@ -159,6 +160,6 @@ func (o *State) OverwriteCount() int { return o.overwriteCount } -func (o *State) Votes() []Vote { +func (o *State) Votes() []*Vote { return o.votes } diff --git a/state/vote.go b/state/vote.go index 74bc130..55b5d28 100644 --- a/state/vote.go +++ b/state/vote.go @@ -12,14 +12,12 @@ type Vote struct { Nullifier []byte Ballot *elgamal.Ciphertext Address []byte - Commitment big.Int + Commitment *big.Int } // AddVote adds a vote to the state // - if nullifier exists, it counts as vote overwrite -// -// TODO: use Tx to rollback in case of failure -func (o *State) AddVote(v Vote) error { +func (o *State) AddVote(v *Vote) error { if len(o.votes) >= VoteBatchSize { return fmt.Errorf("too many votes for this batch") } @@ -28,7 +26,9 @@ func (o *State) AddVote(v Vote) error { // so it's later added to circuit.ResultsSub if _, value, err := o.tree.Get(v.Nullifier); err == nil { oldVote := elgamal.NewCiphertext(Curve) - oldVote.Deserialize(value) + if err := oldVote.Deserialize(value); err != nil { + return err + } o.OverwriteSum.Add(o.OverwriteSum, oldVote) o.overwriteCount++ }