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

Election: Servers cannot tally multiple times

parent 6999665d
Pipeline #21384 passed with stages
in 1 minute
......@@ -27,6 +27,7 @@ const (
ReasonDeadline = "deadline reached"
ReasonVotes = "all votes received"
ReasonAgreement = "majority decided to end election"
ReasonTally = "received enough tallies"
)
// Phase of an election
......@@ -67,9 +68,10 @@ type Participants struct {
// Status contains the current status of the election.
type Status struct {
Phase Phase
ClosedServers map[UniqueID]bool // Map of closed servers
Turnout map[UniqueID]bool // Map of voters who voted
Phase Phase
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
}
// Election is an abstraction of an election.
......@@ -138,9 +140,10 @@ func NewElection(config *Config) *Election {
Result: nil,
Deadline: config.Deadline,
Status: Status{
Phase: PhaseNotStarted,
ClosedServers: make(map[UniqueID]bool),
Turnout: make(map[UniqueID]bool),
Phase: PhaseNotStarted,
ClosedServers: make(map[UniqueID]bool),
TalliedServers: make(map[UniqueID]bool),
Turnout: make(map[UniqueID]bool),
},
closeCallback: config.CloseCallback,
tallyCallback: config.TallyCallback,
......@@ -262,24 +265,34 @@ func (election *Election) HandleClosing(id UniqueID) {
}
}
func (election *Election) shouldTally() bool {
return len(election.Status.ClosedServers) >= election.Participants.RequiredServers
}
// Tally ballots
func (election *Election) Tally() *Tally {
// HandleTally handles the tallies received from servers
func (election *Election) HandleTally(serverID UniqueID, tally *Tally) {
election.Lock()
defer election.Unlock()
return election.ballotBox.Tally()
}
if election.Status.Phase == PhaseNotStarted {
log.Printf("ERROR: Server %s tallied before election started\n", serverID)
return
}
// HandleTally handles the tallies received from servers
func (election *Election) HandleTally(serverID UniqueID, tally *Tally) {
// TODO verify serverID have not tallied!!
if _, ok := election.Status.ClosedServers[serverID]; !ok {
log.Printf("ERROR: Received tally from server %s, who have not closed\n", serverID)
return
}
if _, ok := election.Status.TalliedServers[serverID]; ok {
log.Printf("ERROR: Server %s have already sent a tally\n", serverID)
return
}
err := election.tallyBox.Put(tally)
if err != nil {
log.Println("ERROR: " + err.Error())
return
}
election.Status.TalliedServers[serverID] = true
if len(election.Status.TalliedServers) >= election.Participants.RequiredServers &&
election.Status.Phase < PhaseResult {
election.nextPhase(ReasonTally)
}
}
......
......@@ -3,12 +3,15 @@ package election
import (
"bsc-shamir/crypto/common"
"crypto/rand"
"fmt"
"io/ioutil"
"log"
"math/big"
"strconv"
"testing"
"time"
"github.com/google/uuid"
)
/////////////////////
......@@ -247,3 +250,39 @@ func TestOnlyVoteOnce(t *testing.T) {
t.Errorf("Expected ballot box size 1, got %d", e.ballotBox.Size())
}
}
func TestHandleTally(t *testing.T) {
ballots := CreateBallots(3, createXS(5), big.NewInt(1))
tally1 := &Tally{
ID: uuid.New(),
Share: ballots["3"].Share,
Commits: ballots["3"].Commits,
}
tally2 := &Tally{
ID: uuid.New(),
Share: ballots["4"].Share,
Commits: ballots["4"].Commits,
}
_ = testCloseCondition(t, ReasonAgreement, PhaseResult, func(e *Election) {
e.HandleBallot("4", ballots["1"])
e.HandleTally("2", tally1)
if e.tallyBox.Size() != 0 {
t.Errorf("Expected 0 tally, got %d", e.tallyBox.Size())
}
e.HandleClosing("2")
e.HandleTally("2", tally1)
e.HandleTally("2", tally1)
if e.tallyBox.Size() != 1 {
t.Errorf("Expected 1 tally, got %d", e.tallyBox.Size())
}
fmt.Println("Phase:", e.Status.Phase)
e.HandleClosing("3")
fmt.Println("Phase:", e.Status.Phase)
e.HandleTally("3", tally2)
fmt.Println("Phase:", e.Status.Phase)
})
}
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