Skip to content

Commit

Permalink
Putting ballots in separate memory
Browse files Browse the repository at this point in the history
This PR separates the ballots from the forms.
This allows to run adding a new ballot much faster.
When there are more than 100 ballots, this gets very important:
adding a 1000th ballot can take 1s, a 10'000th ballot 10s.

Using this PR, up to the 10'000th ballot it only takes 100ms.
  • Loading branch information
ineiti committed Feb 28, 2024
1 parent c819e31 commit 7804ac7
Show file tree
Hide file tree
Showing 36 changed files with 698 additions and 718 deletions.
12 changes: 10 additions & 2 deletions contracts/evoting/controller/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,11 @@ func (a *scenarioTestAction) Execute(ctx node.Context) error {
return xerrors.Errorf(getFormErr, err)
}

encryptedBallots := form.Suffragia.Ciphervotes
suff, err := form.Suffragia(serdecontext, service.GetStore())
if err != nil {
return xerrors.Errorf(getFormErr, err)
}
encryptedBallots := suff.Ciphervotes
dela.Logger.Info().Msg("Length encrypted ballots: " + strconv.Itoa(len(encryptedBallots)))
dela.Logger.Info().Msgf("Ballot of user1: %s", encryptedBallots[0])
dela.Logger.Info().Msgf("Ballot of user2: %s", encryptedBallots[1])
Expand Down Expand Up @@ -485,7 +489,11 @@ func (a *scenarioTestAction) Execute(ctx node.Context) error {

logFormStatus(form)
dela.Logger.Info().Msg("Number of shuffled ballots : " + strconv.Itoa(len(form.ShuffleInstances)))
dela.Logger.Info().Msg("Number of encrypted ballots : " + strconv.Itoa(len(form.Suffragia.Ciphervotes)))
suff, err = form.Suffragia(serdecontext, service.GetStore())
if err != nil {
return xerrors.Errorf(getFormErr, err)
}
dela.Logger.Info().Msg("Number of encrypted ballots : " + strconv.Itoa(len(suff.Ciphervotes)))

// ###################################### REQUEST PUBLIC SHARES ############

Expand Down
16 changes: 11 additions & 5 deletions contracts/evoting/evoting.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

103 changes: 38 additions & 65 deletions contracts/evoting/json/forms.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package json

import (
"encoding/hex"
"encoding/json"

"github.com/c4dt/d-voting/contracts/evoting/types"
Expand Down Expand Up @@ -35,9 +36,14 @@ func (formFormat) Encode(ctx serde.Context, message serde.Message) ([]byte, erro
}
}

suffragia, err := encodeSuffragia(ctx, m.Suffragia)
if err != nil {
return nil, xerrors.Errorf("failed to encode suffragia: %v", err)
suffragias := make([]string, len(m.SuffragiaIDs))
for i, suf := range m.SuffragiaIDs {
suffragias[i] = hex.EncodeToString(suf)
}

suffragiaHashes := make([]string, len(m.SuffragiaHashes))
for i, sufH := range m.SuffragiaHashes {
suffragiaHashes[i] = hex.EncodeToString(sufH)
}

shuffleInstances, err := encodeShuffleInstances(ctx, m.ShuffleInstances)
Expand All @@ -62,7 +68,9 @@ func (formFormat) Encode(ctx serde.Context, message serde.Message) ([]byte, erro
Status: uint16(m.Status),
Pubkey: pubkey,
BallotSize: m.BallotSize,
Suffragia: suffragia,
Suffragias: suffragias,
SuffragiaHashes: suffragiaHashes,
BallotCount: m.BallotCount,
ShuffleInstances: shuffleInstances,
ShuffleThreshold: m.ShuffleThreshold,
PubsharesUnits: pubsharesUnits,
Expand Down Expand Up @@ -100,9 +108,20 @@ func (formFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error)
}
}

suffragia, err := decodeSuffragia(ctx, formJSON.Suffragia)
if err != nil {
return nil, xerrors.Errorf("failed to decode suffragia: %v", err)
suffragias := make([][]byte, len(formJSON.Suffragias))
for i, suff := range formJSON.Suffragias {
suffragias[i], err = hex.DecodeString(suff)
if err != nil {
return nil, xerrors.Errorf("failed to decode suffragia-address: %v", err)
}
}

suffragiaHashes := make([][]byte, len(formJSON.SuffragiaHashes))
for i, suffH := range formJSON.SuffragiaHashes {
suffragiaHashes[i], err = hex.DecodeString(suffH)
if err != nil {
return nil, xerrors.Errorf("failed to decode suffragia-hash: %v", err)
}
}

shuffleInstances, err := decodeShuffleInstances(ctx, formJSON.ShuffleInstances)
Expand Down Expand Up @@ -132,7 +151,9 @@ func (formFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error)
Status: types.Status(formJSON.Status),
Pubkey: pubKey,
BallotSize: formJSON.BallotSize,
Suffragia: suffragia,
SuffragiaIDs: suffragias,
SuffragiaHashes: suffragiaHashes,
BallotCount: formJSON.BallotCount,
ShuffleInstances: shuffleInstances,
ShuffleThreshold: formJSON.ShuffleThreshold,
PubsharesUnits: pubSharesSubmissions,
Expand All @@ -157,7 +178,15 @@ type FormJSON struct {
// to pad smaller ballots such that all ballots cast have the same size
BallotSize int

Suffragia SuffragiaJSON
// Suffragias are the hex-encoded addresses of the Suffragia storages.
Suffragias []string

// BallotCount represents the total number of ballots cast.
BallotCount uint32

// SuffragiaHashes are the hex-encoded sha256-hashes of the ballots
// in every Suffragia.
SuffragiaHashes []string

// ShuffleInstances is all the shuffles, along with their proof and identity
// of shuffler.
Expand All @@ -179,62 +208,6 @@ type FormJSON struct {
RosterBuf []byte
}

// SuffragiaJSON defines the JSON representation of a suffragia.
type SuffragiaJSON struct {
UserIDs []string
Ciphervotes []json.RawMessage
}

func encodeSuffragia(ctx serde.Context, suffragia types.Suffragia) (SuffragiaJSON, error) {
ciphervotes := make([]json.RawMessage, len(suffragia.Ciphervotes))

for i, ciphervote := range suffragia.Ciphervotes {
buff, err := ciphervote.Serialize(ctx)
if err != nil {
return SuffragiaJSON{}, xerrors.Errorf("failed to serialize ciphervote: %v", err)
}

ciphervotes[i] = buff
}
return SuffragiaJSON{
UserIDs: suffragia.UserIDs,
Ciphervotes: ciphervotes,
}, nil
}

func decodeSuffragia(ctx serde.Context, suffragiaJSON SuffragiaJSON) (types.Suffragia, error) {
var res types.Suffragia
fac := ctx.GetFactory(types.CiphervoteKey{})

factory, ok := fac.(types.CiphervoteFactory)
if !ok {
return res, xerrors.Errorf("invalid ciphervote factory: '%T'", fac)
}

ciphervotes := make([]types.Ciphervote, len(suffragiaJSON.Ciphervotes))

for i, ciphervoteJSON := range suffragiaJSON.Ciphervotes {
msg, err := factory.Deserialize(ctx, ciphervoteJSON)
if err != nil {
return res, xerrors.Errorf("failed to deserialize ciphervote json: %v", err)
}

ciphervote, ok := msg.(types.Ciphervote)
if !ok {
return res, xerrors.Errorf("wrong type: '%T'", msg)
}

ciphervotes[i] = ciphervote
}

res = types.Suffragia{
UserIDs: suffragiaJSON.UserIDs,
Ciphervotes: ciphervotes,
}

return res, nil
}

// ShuffleInstanceJSON defines the JSON representation of a shuffle instance
type ShuffleInstanceJSON struct {
// ShuffledBallots contains the list of shuffled ciphertext for this round
Expand Down
1 change: 1 addition & 0 deletions contracts/evoting/json/mod.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

func init() {
types.RegisterFormFormat(serde.FormatJSON, formFormat{})
types.RegisterSuffragiaFormat(serde.FormatJSON, suffragiaFormat{})
types.RegisterCiphervoteFormat(serde.FormatJSON, ciphervoteFormat{})
types.RegisterTransactionFormat(serde.FormatJSON, transactionFormat{})
}
97 changes: 97 additions & 0 deletions contracts/evoting/json/suffragia.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package json

import (
"encoding/json"

"github.com/c4dt/d-voting/contracts/evoting/types"
"go.dedis.ch/dela/serde"
"golang.org/x/xerrors"
)

type suffragiaFormat struct{}

func (suffragiaFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, error) {
switch m := msg.(type) {
case types.Suffragia:
sJson, err := encodeSuffragia(ctx, m)
if err != nil {
return nil, xerrors.Errorf("couldn't encode suffragia: %v", err)
}

buff, err := ctx.Marshal(&sJson)
if err != nil {
return nil, xerrors.Errorf("failed to marshal form: %v", err)
}

return buff, nil
default:
return nil, xerrors.Errorf("Unknown format: %T", msg)
}
}

func (suffragiaFormat) Decode(ctx serde.Context, data []byte) (serde.Message, error) {
var sJson SuffragiaJSON

err := ctx.Unmarshal(data, &sJson)
if err != nil {
return nil, xerrors.Errorf("failed to unmarshal form: %v", err)
}

return decodeSuffragia(ctx, sJson)
}

// SuffragiaJSON defines the JSON representation of a suffragia.
type SuffragiaJSON struct {
UserIDs []string
Ciphervotes []json.RawMessage
}

func encodeSuffragia(ctx serde.Context, suffragia types.Suffragia) (SuffragiaJSON, error) {
ciphervotes := make([]json.RawMessage, len(suffragia.Ciphervotes))

for i, ciphervote := range suffragia.Ciphervotes {
buff, err := ciphervote.Serialize(ctx)
if err != nil {
return SuffragiaJSON{}, xerrors.Errorf("failed to serialize ciphervote: %v", err)
}

ciphervotes[i] = buff
}
return SuffragiaJSON{
UserIDs: suffragia.UserIDs,
Ciphervotes: ciphervotes,
}, nil
}

func decodeSuffragia(ctx serde.Context, suffragiaJSON SuffragiaJSON) (types.Suffragia, error) {
var res types.Suffragia
fac := ctx.GetFactory(types.CiphervoteKey{})

factory, ok := fac.(types.CiphervoteFactory)
if !ok {
return res, xerrors.Errorf("invalid ciphervote factory: '%T'", fac)
}

ciphervotes := make([]types.Ciphervote, len(suffragiaJSON.Ciphervotes))

for i, ciphervoteJSON := range suffragiaJSON.Ciphervotes {
msg, err := factory.Deserialize(ctx, ciphervoteJSON)
if err != nil {
return res, xerrors.Errorf("failed to deserialize ciphervote json: %v", err)
}

ciphervote, ok := msg.(types.Ciphervote)
if !ok {
return res, xerrors.Errorf("wrong type: '%T'", msg)
}

ciphervotes[i] = ciphervote
}

res = types.Suffragia{
UserIDs: suffragiaJSON.UserIDs,
Ciphervotes: ciphervotes,
}

return res, nil
}
Loading

0 comments on commit 7804ac7

Please sign in to comment.