Commit 8e474316 authored by Mikkel Wienberg Madsen's avatar Mikkel Wienberg Madsen 👀
Browse files

Judge Duty

parent 84f69800
......@@ -143,3 +143,15 @@ func (params *Params) MulProofs(proofs []Proof) Proof {
return result
}
func (proof Proof) Equals(other Proof) bool {
if len(other) != len(proof) {
return false
}
for i, e := range proof {
if other[i].Cmp(e) != 0 {
return false
}
}
return true
}
package election
import (
"bsc-shamir/crypto/pedersen"
"sync"
"github.com/google/uuid"
)
type Whitelist map[uuid.UUID]pedersen.Proof
type Judge struct {
mutex sync.Mutex
Threadshold int
Points map[uuid.UUID]int
Answers map[uuid.UUID]pedersen.Proof
BallotIDs []uuid.UUID
}
func NewJudge(threadshold int, box *BallotBox) *Judge {
m := len(box.ballots)
j := &Judge{
mutex: sync.Mutex{},
Threadshold: threadshold,
Points: make(map[uuid.UUID]int, m),
Answers: make(map[uuid.UUID]pedersen.Proof, m),
BallotIDs: []uuid.UUID{},
}
for _, id := range box.Filter() {
ballot := box.ballots[id]
j.Answers[id] = ballot.Commits
j.Points[id] = 0
j.BallotIDs = append(j.BallotIDs, id)
}
return j
}
func (j *Judge) HandleWhitelist(wl Whitelist) {
j.mutex.Lock()
defer j.mutex.Unlock()
for _, id := range j.BallotIDs {
proof := wl[id]
answer := j.Answers[id]
if proof.Equals(answer) {
j.Points[id]++
}
}
}
func (j *Judge) GoodIds() []uuid.UUID {
j.mutex.Lock()
defer j.mutex.Unlock()
good := []uuid.UUID{}
for _, id := range j.BallotIDs {
if j.Points[id] >= j.Threadshold {
good = append(good, id)
}
}
return good
}
......@@ -96,8 +96,22 @@ func (box *BallotBox) Put(ballot *Ballot) error {
return nil
}
// Filter ballots by returning a list of valid ballots
func (box *BallotBox) Filter() []uuid.UUID {
good := []uuid.UUID{}
for id, ballot := range box.ballots {
if box.X.Cmp(ballot.Share.X) != 0 {
continue
}
if ballot.Verify(box.params) {
good = append(good, id)
}
}
return good
}
// Tally ballots
func (box *BallotBox) Tally() *Tally {
func (box *BallotBox) Tally(filter []uuid.UUID) *Tally {
box.Lock()
defer box.Unlock()
......@@ -107,16 +121,11 @@ func (box *BallotBox) Tally() *Tally {
commits := make([]pedersen.Proof, 0, size)
valids := 0
for _, ballot := range box.ballots {
if box.X.Cmp(ballot.Share.X) == 0 {
if ballot.Verify(box.params) {
shares = append(shares, ballot.Share)
commits = append(commits, ballot.Commits)
valids++
}
} else {
log.Printf("Ballot box (%d) contains share with x=%d\n", box.X, ballot.Share.X)
}
for _, id := range filter {
ballot := box.ballots[id]
shares = append(shares, ballot.Share)
commits = append(commits, ballot.Commits)
valids++
}
log.Printf("processed %d ballots, %d/%d valid", valids, valids, size)
......@@ -125,6 +134,8 @@ func (box *BallotBox) Tally() *Tally {
return newTally(params.AddShares(shares), params.MulProofs(commits))
}
// Count of ballots in the box
func (box *BallotBox) Count() int {
return len(box.ballots)
......
......@@ -84,7 +84,7 @@ type Election struct {
Participants Participants
// The ballot box, which handles ballots received from voters
ballotBox *BallotBox
// The tally bix, which hanges tallies received from servers
// The tally box, which hanges tallies received from servers
tallyBox *TallyBox
// Result of the election
Result *Result
......@@ -93,6 +93,8 @@ type Election struct {
Deadline time.Time
// Struct containing server status about the election.
Status Status
// Judge for deciding which ballots are good
Judge *Judge
// Called when the election is closed locally
closeCallback func(Reason)
// Called when a enough servers (Participants.RequiredServers) have closed
......@@ -149,7 +151,7 @@ func NewElection(config *Config) *Election {
}
}
// Start the election, by opening for incomming ballots.
// Start the election, by opening for incoming ballots.
// Returns error if the deadline is not a future time.
func (election *Election) Start() error {
election.Lock()
......@@ -205,30 +207,6 @@ func (election *Election) nextPhase(reason Reason) {
election.nextPhaseTally()
}
// switch election.Status.Phase {
// case PhaseCollecting:
// election.closeElection(reason)
// election.Status.Phase = PhaseClosed
// if !election.shouldTally() {
// log.Println("Waiting for enough servers to close election")
// } else {
// election.nextPhase(reason) // Recursion :D
// }
// case PhaseClosed:
// if election.ballotBox.Count() == 0 {
// // No votes
// election.Status.Phase = PhaseResult
// go election.resultCallback(nil)
// } else {
// election.Status.Phase = PhaseTallying
// go election.tallyCallback()
// }
// case PhaseTallying:
// election.Result = election.createResults()
// election.Status.Phase = PhaseResult
// go election.resultCallback(election.Result)
// }
}
func (election *Election) nextPhaseTally() {
......@@ -280,6 +258,8 @@ func (election *Election) HandleClosing(id UniqueID) {
}
}
func (election *Election) shouldTally() bool {
return len(election.Status.ClosedServers) >= election.Participants.RequiredServers
}
......@@ -288,6 +268,10 @@ func (election *Election) shouldTally() bool {
func (election *Election) Tally() *Tally {
election.Lock()
defer election.Unlock()
election.Judge = NewJudge(election.Participants.RequiredServers, election.ballotBox)
// TODO: Send white list
// broadcast(election.Judge.Answers)
return election.ballotBox.Tally()
}
......
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