Commit 786d4d35 authored by Anders Jensen Løvig's avatar Anders Jensen Løvig
Browse files

Merge branch 'election-fix'

parents 039e64d1 389848b9
......@@ -14,6 +14,7 @@ import (
"log"
"math/big"
"sync"
"time"
"crypto"
......@@ -24,10 +25,11 @@ import (
// Ballot represents a share to be submitted to a vote counter.
type Ballot struct {
ID uuid.UUID `json:"id"`
Share *pedersen.Share `json:"share"`
Commits pedersen.Proof `json:"commits"`
Proofs [2]*sigma.Proof `json:"proofs"`
ID uuid.UUID `json:"id"`
Timestamp time.Time `json:"timestamp`
Share *pedersen.Share `json:"share"`
Commits pedersen.Proof `json:"commits"`
Proofs [2]*sigma.Proof `json:"proofs"`
}
// BallotBox collects ballots from voters
......@@ -58,13 +60,16 @@ func CreateBallots(t int, xs []*big.Int, vote *big.Int) map[UniqueID]*Ballot {
shares, commits := pedersen.NewParams(params).Create(t, xs, vote, binder)
proof := sigma.NewParams(params).Prove(vote, binder, commits[0])
timestamp := time.Now()
ballots := make(map[UniqueID]*Ballot)
for _, share := range shares {
ballots[UniqueID(share.X.String())] = &Ballot{
ID: uuid.New(),
Share: share,
Commits: commits,
Proofs: proof,
ID: uuid.New(),
Timestamp: timestamp,
Share: share,
Commits: commits,
Proofs: proof,
}
}
......
......@@ -17,11 +17,12 @@ type UniqueID string
type Reason string
const (
ReasonStarting = "starting election"
ReasonDeadline = "deadline reached"
ReasonVotes = "all votes received"
ReasonAgreement = "majority agrees"
ReasonTally = "received enough tallies"
ReasonStarting = "starting election"
ReasonDeadline = "deadline reached"
ReasonCloseTimeout = "timeout while waiting for late ballots"
ReasonVotes = "all votes received"
ReasonAgreement = "majority agrees"
ReasonTally = "received enough tallies"
)
// Phase of an election
......@@ -31,6 +32,7 @@ type Phase int
const (
PhaseNotStarted Phase = iota
PhaseCollecting // When voters can send ballots
PhaseCloseWait // When server waits after close
PhaseClosed // When waiting for other servers to close
PhaseTallying // When tallying
PhaseResult // When a result is available
......@@ -63,6 +65,8 @@ type Participants struct {
// Status contains the current status of the election.
type Status struct {
Phase Phase
PhaseChannel chan bool
CloseTime time.Time
ClosedServers map[UniqueID]bool // Map of closed servers
TalliedServers map[UniqueID]bool // Map of servers who tallied
Turnout map[UniqueID]bool // Map of voters who voted
......@@ -71,6 +75,7 @@ type Status struct {
// Election is an abstraction of an election.
type Election struct {
sync.Mutex
Config *Config
// The network Server on which this election is held.
server *network.Server
......@@ -103,6 +108,9 @@ type Config struct {
RequiredServers int `json:"required"`
Deadline time.Time `json:"deadline"`
ResultCallback func(*Result) `json:"-"`
// Delay between closing and tallying
CloseSleep time.Duration
}
// Result represents the result of an election
......@@ -120,6 +128,7 @@ func NewElection(config *Config) *Election {
log.Fatal(success)
}
e := &Election{
Config: config,
server: config.Server,
ServerID: config.ServerID,
Participants: Participants{
......@@ -133,6 +142,7 @@ func NewElection(config *Config) *Election {
Deadline: config.Deadline,
Status: Status{
Phase: PhaseNotStarted,
PhaseChannel: make(chan bool, 1),
ClosedServers: make(map[UniqueID]bool),
TalliedServers: make(map[UniqueID]bool),
Turnout: make(map[UniqueID]bool),
......@@ -184,15 +194,46 @@ func (election *Election) nextPhase(reason Reason) {
log.Println("==================== Collecting ====================")
election.Status.Phase = PhaseCollecting
case PhaseCollecting:
log.Println("====================== Closed ======================")
// The closed phase is twofold. If the reason for closing is another
// than all votes received (ReasonVotes), then we must wait for late
// ballots.
// Next we wait for more than required servers to close.
// Close election and tell other servers
election.Status.CloseTime = time.Now()
election.Status.ClosedServers[election.ServerID] = true
election.server.Broadcast(network.NewMessage(MsgClosing))
// Wait for late ballots if not all votes received.
if reason != ReasonVotes {
log.Println("Election: waiting 5 seconds for late ballots")
election.Status.Phase = PhaseCloseWait
go func(e *Election) {
var reason Reason
// Wait for either 5 seconds or PhaseChannel is set.
select {
case <-time.After(5 * time.Second):
reason = ReasonCloseTimeout
case <-e.Status.PhaseChannel:
reason = ReasonVotes
}
// Then next phase
e.Lock()
defer e.Unlock()
// Actually close the election
e.Status.Phase = PhaseClosed
e.nextPhase(reason)
}(election)
break
}
// If more than required servers have closed, then we can skip the next phase
// Else we have to wait for more servers to close
if len(election.Status.ClosedServers) < election.Participants.RequiredServers {
// Not enough servers to tally.
log.Println("====================== Closed ======================")
election.Status.Phase = PhaseClosed
n := election.Participants.RequiredServers - len(election.Status.ClosedServers)
log.Printf("Election: waiting for %d more servers to close\n", n)
break
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment