~edwargix/tallyard

70372a4c3834ca252ce46f5ce72b11ba9839a0d5 — David Florness 5 years ago 7463078
Create preferences matrix for ballot from voting UI
3 files changed, 76 insertions(+), 11 deletions(-)

M main.go
M merkle.go
M ui.go
M main.go => main.go +2 -2
@@ 25,7 25,7 @@ var (
	logger     = log.Logger("tallyard")
	protocolID = protocol.ID("/tallyard/0.0.0")

	candidates    []ElectionOption
	candidates    []Candidate
	optionsMerkle *merkletree.MerkleTree

	rendezvousNonce Nonce


@@ 155,7 155,7 @@ func bootstrap() {
			}
			str = stripNewline(str)
			if str != "" {
				candidates = append(candidates, ElectionOption(str))
				candidates = append(candidates, Candidate(str))
				fmt.Println(str)
			}
			if err == io.EOF {

M merkle.go => merkle.go +4 -4
@@ 11,9 11,9 @@ import (
	"github.com/mr-tron/base58/base58"
)

type ElectionOption string
type Candidate string

func (eo ElectionOption) CalculateHash() ([]byte, error) {
func (eo Candidate) CalculateHash() ([]byte, error) {
	h := sha256.New()
	if _, err := h.Write([]byte(eo)); err != nil {
		return nil, err


@@ 21,8 21,8 @@ func (eo ElectionOption) CalculateHash() ([]byte, error) {
	return h.Sum(nil), nil
}

func (eo ElectionOption) Equals(other merkletree.Content) (bool, error) {
	return eo == other.(ElectionOption), nil
func (eo Candidate) Equals(other merkletree.Content) (bool, error) {
	return eo == other.(Candidate), nil
}

type Nonce string

M ui.go => ui.go +70 -5
@@ 2,6 2,7 @@ package main

import (
	"fmt"
	"sort"
	"strconv"
	"strings"
	"unicode"


@@ 37,7 38,7 @@ func createElection() {
			rendezvousNonce = NewNonce()
			content := []merkletree.Content{rendezvousNonce}
			for i := 0; i < n-1; i++ {
				eo := ElectionOption(form.GetFormItem(i).(*tview.InputField).GetText())
				eo := Candidate(form.GetFormItem(i).(*tview.InputField).GetText())
				candidates = append(candidates, eo)
				content = append(content, eo)
			}


@@ 80,10 81,12 @@ func joinElection() {
	}
}

func vote(candidates []ElectionOption) []int {
	result := make([]int, len(candidates))
// displays a voting UI to the user and returns the encoded ballot
func vote(candidates []Candidate) []byte {
	ranks := make([]int, len(candidates))
	app := tview.NewApplication()
	form := tview.NewForm()

	for _, eo := range candidates {
		// TODO: support more than 99 candidates
		form.AddInputField(string(eo), "", 2,


@@ 91,6 94,7 @@ func vote(candidates []ElectionOption) []int {
				return len(textToCheck) < 3 && unicode.IsDigit(lastChar)
			}, nil)
	}

	form.AddButton("Submit", func() {
		app.Stop()
		for i := 0; i < len(candidates); i++ {


@@ 99,11 103,72 @@ func vote(candidates []ElectionOption) []int {
			if err != nil {
				panic(err)
			}
			result[i] = rank
			ranks[i] = rank
		}
	})

	if err := app.SetRoot(form, true).EnableMouse(true).Run(); err != nil {
		panic(err)
	}
	return result

	return GetBallotFromRankings(ranks)
}

func GetBallotFromRankings(ranks []int) []byte {
	n := len(ranks)
	candidates := make([]int, n)

	for i := 0; i < n; i++ {
		candidates[i] = i
	}

	// sort candidates by ranking
	cr := CandidateRanking{candidates, ranks}
	sort.Sort(&cr)

	// TODO: support more than 255 voters (limit from usage of byte)
	prefMatrix := make([][]byte, n)

	for len(candidates) > 0 {
		r := ranks[candidates[0]]
		i := 1
		for ; i < len(candidates) && ranks[candidates[i]] == r; i++ {}
		// i is now index of the first candidate with worse (i.e. higher
		// in value) rank
		row := make([]byte, n)
		for j := i; j < len(candidates); j++ {
			row[candidates[j]] = 1
		}
		for j := 0; j < i; j++ {
			prefMatrix[candidates[j]] = row
		}
		candidates = candidates[i:]
	}

	// convert 2D array into 1D array
	barray := make([]byte, 0, n*n)
	for _, row := range prefMatrix {
		barray = append(barray, row...)
	}

	return barray
}

type CandidateRanking struct {
	candidates []int // becomes list of candidate IDs sorted by rank
	ranks      []int // maps candidate ID to rank
}

func (cr *CandidateRanking) Len() int {
	return len(cr.ranks)
}

func (cr *CandidateRanking) Less(i, j int) bool {
	return cr.ranks[cr.candidates[i]] < cr.ranks[cr.candidates[j]]
}

func (cr *CandidateRanking) Swap(i, j int) {
	tmp := cr.candidates[i]
	cr.candidates[i] = cr.candidates[j]
	cr.candidates[j] = tmp
}