M cmd/tallyard/main.go => cmd/tallyard/main.go +6 -7
@@ 111,12 111,12 @@ func main() {
if err != nil {
panic(err)
}
- if el == nil || el.LocalVoter == nil {
+ if el == nil {
// no election selected; user likely hit C-c
return
}
- // wait for election to start
+ // wait for election to start if needed
err = ui.ElectionWaitTUI(client, el, elections.EventStore)
if err != nil {
panic(err)
@@ 128,15 128,14 @@ func main() {
// vote if we need to (user may have voted in previous tallyard
// invocation)
- if el.LocalVoter.Poly == nil {
- candidates := el.Candidates
- ballot := ui.VoteTUI(candidates)
+ if el.LocalVoter != nil && el.LocalVoter.Poly == nil {
+ ballot := ui.VoteTUI(el.Candidates)
el.LocalVoter.Poly = math.NewRandomPoly(uint(len(*el.FinalJoinIDs)-1), 1024, ballot)
el.Save()
}
// send evals if needed
- if el.LocalVoter.EvalsID == nil {
+ if el.LocalVoter != nil && el.LocalVoter.EvalsID == nil {
err = el.SendEvals(client, elections.EventStore)
if err != nil {
panic(err)
@@ 144,7 143,7 @@ func main() {
}
// send sum if needed
- if el.LocalVoter.SumID == nil {
+ if el.LocalVoter != nil && el.LocalVoter.SumID == nil {
fmt.Println("waiting for evals...")
err = el.SendSum(client, elections.EventStore)
if err != nil {
M election/election.go => election/election.go +1 -0
@@ 17,6 17,7 @@ type Election struct {
FinalJoinIDs *[]id.EventID `json:"final_voters,omitempty"`
Joins map[id.EventID]*Voter `json:"joins"`
LocalVoter *LocalVoter `json:"local_voter,omitempty"`
+ Result *[]byte `json:"result,omitempty"`
RoomID id.RoomID `json:"room_id"`
StartID *id.EventID `json:"start_id,omitempty"`
Title string `json:"title"`
M election/msg.go => election/msg.go +12 -0
@@ 503,5 503,17 @@ func (elections *ElectionsMap) onSumMessage(evt *event.Event) (success bool) {
voter.SumID = &evt.ID
voter.Sum = new(big.Int).SetBytes(bytes)
+ // once we've recieved all sums, calculate the election result!
+ receivedAllSums := true
+ for _, voterID := range *el.FinalJoinIDs {
+ if el.Joins[voterID].SumID == nil {
+ receivedAllSums = false
+ break
+ }
+ }
+ if receivedAllSums {
+ el.CalculateResult()
+ }
+
return true
}
M election/result.go => election/result.go +10 -17
@@ 4,29 4,15 @@ import (
"fmt"
"math/big"
"sort"
- "sync"
"time"
log "github.com/sirupsen/logrus"
- "maunium.net/go/mautrix/id"
"tallyard.xyz/math"
)
-func (el *Election) PrintResults() {
- var sumIDs []id.EventID
- var wg sync.WaitGroup
- for _, voterJoinId := range *el.FinalJoinIDs {
- wg.Add(1)
- go func(voter *Voter) {
- for voter.SumID == nil {
- time.Sleep(time.Millisecond * 100)
- }
- sumIDs = append(sumIDs, *voter.SumID)
- wg.Done()
- }(el.Joins[voterJoinId])
- }
- wg.Wait()
-
+func (el *Election) CalculateResult() {
+ // this assumes all sums have been received and successfully validated
+ // and processed
M := constructPolyMatrix(el)
M.RREF()
constant := M[0][len(M[0])-1]
@@ 37,7 23,14 @@ func (el *Election) PrintResults() {
// number of bytes we need to insert at the front since they're zero
diff := (len(el.Candidates) * len(el.Candidates)) - len(result)
result = append(make([]byte, diff), result...)
+ el.Result = &result
+}
+func (el *Election) PrintResults() {
+ for el.Result == nil {
+ time.Sleep(time.Millisecond * 200)
+ }
+ result := *el.Result
candidates := el.Candidates
log.Debugf("result: %v", result)
M ui/tui.go => ui/tui.go +13 -9
@@ 130,12 130,16 @@ func RoomTUI(client *mautrix.Client, roomID id.RoomID, elections *election.Elect
el = L[i - 1]
// don't need to lock because this goroutine controls LocalVoter
if el.LocalVoter == nil {
- // ask user if s/he wants to join election
- if joinElectionConfirmation(el) {
- err = el.JoinElection(client, elections.EventStore)
- } else {
- // user needs to select a different election
+ // ask user if s/he wants to join election (or
+ // display results if it's already started)
+ if !electionConfirmation(el) {
+ // user needs to select a different
+ // election
el, err = RoomTUI(client, roomID, elections)
+ } else if el.StartID == nil {
+ // user selected yes on joining the
+ // election
+ err = el.JoinElection(client, elections.EventStore)
}
}
return
@@ 162,7 166,7 @@ func RoomTUI(client *mautrix.Client, roomID id.RoomID, elections *election.Elect
return
}
-func joinElectionConfirmation(el *election.Election) (shouldJoin bool) {
+func electionConfirmation(el *election.Election) (shouldJoin bool) {
app := newTallyardApplication()
var buttons []string
@@ 171,8 175,8 @@ func joinElectionConfirmation(el *election.Election) (shouldJoin bool) {
el.RLock()
// TODO: handle when election starts while in modal
if el.StartID != nil {
- buttons = []string{"Ok"}
- text = "Election has already started, sorry"
+ buttons = []string{"Yes", "No"}
+ text = "Election has already started; display results?"
} else {
buttons = []string{"Yes", "No"}
text = "Join election?"
@@ 250,7 254,7 @@ func ElectionWaitTUI(client *mautrix.Client, el *election.Election, eventStore *
frame.SetTitle(el.Title).SetBorder(true)
app := newTallyardApplication()
el.RLock()
- if el.CreateEvt.Sender == el.LocalVoter.JoinEvt.Sender {
+ if el.LocalVoter != nil && el.CreateEvt.Sender == el.LocalVoter.JoinEvt.Sender {
frame.AddText("Press enter to start the election", false, tview.AlignCenter, tcell.ColorWhite)
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyEnter {