~edwargix/tallyard

34cd04934a8f9e1d3f77ed737b25f646af6c296b — David Florness 5 years ago 1d7eeab
Don't generate an identity if we've joined this election before
4 files changed, 69 insertions(+), 46 deletions(-)

M config.go
M main.go
M merkle.go
M ui.go
M config.go => config.go +2 -2
@@ 28,8 28,8 @@ type ElectionConfig struct{

type ElectionConfigs map[string]*ElectionConfig

// sees if we've joined this election before and attempt to retrieve the data we
// stored for it
// sees if we've joined this election before and attempts to retrieve the data
// we stored for it
func getElectionConfigMaybe(electionKey string) *ElectionConfig {
	root := path.Join(xdg.ConfigHome(), "tallyard")
	file, err := os.OpenFile(path.Join(root, "elections.json"), os.O_RDONLY|os.O_CREATE, 0600)

M main.go => main.go +40 -23
@@ 21,27 21,28 @@ import (
)

const protocolID = protocol.ID("/tallyard/0.0.0")

var logger = log.Logger("tallyard")

func bootstrap(election *Election, me *Me, merkleRoot []byte) {
func bootstrap(election *Election, me *Me, merkleRoot []byte, hostOpts ...libp2p.Option) {
	me.ctx = context.Background()
	election.remoteVoters = make(map[peer.ID]*Voter)

	var err error
	me.Host, err = libp2p.New(me.ctx,
		libp2p.Routing(func(h host.Host) (routing.PeerRouting, error) {
			var err error
			me.kdht, err = dht.New(me.ctx, h)
			if err != nil {
				return me.kdht, err
			}
			logger.Info("boostrapping the DHT")
			if err = me.kdht.Bootstrap(me.ctx); err != nil {
				panic(err)
			}
	routing := libp2p.Routing(func(h host.Host) (routing.PeerRouting, error) {
		var err error
		me.kdht, err = dht.New(me.ctx, h)
		if err != nil {
			return me.kdht, err
		}),
	)
		}
		logger.Info("boostrapping the DHT")
		if err = me.kdht.Bootstrap(me.ctx); err != nil {
			panic(err)
		}
		return me.kdht, err
	})

	var err error
	me.Host, err = libp2p.New(me.ctx, append(hostOpts, routing)...)
	if err != nil {
		panic(err)
	}


@@ 173,24 174,40 @@ func main() {
	}

	var (
		election   *Election
		me         = new(Me)
		merkleRoot []byte
		election    *Election
		electionKey string
		identityOpt libp2p.Option = libp2p.RandomIdentity
		me          *Me           = new(Me)
		merkleRoot  []byte
	)

	// check if election key was given via cli
	if electionKey := flag.Arg(0); electionKey != "" {
	// check if election key was given via CLI args
	if electionKey = flag.Arg(0); electionKey != "" {
		election = new(Election)
		merkleRoot, election.masterID = splitElectionKey(electionKey)
	} else {
		election, merkleRoot = tui()
		// tui button was never pressed
		// create/join election via TUI
		election, merkleRoot, electionKey = tui()
		if election == nil {
			// tui form wasn't submitted (maybe the user hit ^C)
			os.Exit(0)
		}
	}

	bootstrap(election, me, merkleRoot)
	if electionKey != "" { // we are a slave
		// try to recover election info if we've joined before
		if electionConfig := getElectionConfigMaybe(electionKey); electionConfig != nil {
			// ensure public key from config is same as one derived
			// from private key
			if !electionConfig.PrivKey.GetPublic().Equals(electionConfig.PubKey) {
				// TODO: handle properly
				panic("public keys do not match")
			}
			identityOpt = libp2p.Identity(electionConfig.PrivKey)
		}
	}

	bootstrap(election, me, merkleRoot, identityOpt)

	startVoting(election, me)
}

M merkle.go => merkle.go +14 -0
@@ 90,3 90,17 @@ func splitElectionKey(electionKey string) (merkleRoot []byte, masterID peer.ID) 
	logger.Info("master ID:", masterID)
	return merkleRoot, masterID
}

func (e *Election) createMerkle(candidates []Candidate) (merkleRoot []byte) {
	e.rendezvousNonce = NewNonce()
	content := []merkletree.Content{e.rendezvousNonce}
	for _, cand := range candidates {
		content = append(content, cand)
	}
	optionsMerkle, err := merkletree.NewTree(content)
	if err != nil {
		panic(err)
	}
	merkleRoot = optionsMerkle.MerkleRoot()
	return merkleRoot
}

M ui.go => ui.go +13 -21
@@ 6,21 6,21 @@ import (
	"strconv"
	"unicode"

	"github.com/cbergoon/merkletree"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/rivo/tview"
)

func tui() (election *Election, merkleRoot []byte) {
func tui() (election *Election, merkleRoot []byte, electionKey string) {
	app := tview.NewApplication()
	done := func(buttonIndex int, buttonLabel string) {
		app.Stop()
		election = new(Election)
		switch buttonLabel {
		case "Create Election":
			merkleRoot, election.rendezvousNonce, election.candidates = createElection()
			election.candidates = createElection()
			merkleRoot = election.createMerkle(election.candidates)
		case "Join Election":
			merkleRoot, election.masterID = joinElection()
			electionKey := joinElection()
			merkleRoot, election.masterID = splitElectionKey(electionKey)
		}
	}
	modal := tview.NewModal().


@@ 30,10 30,10 @@ func tui() (election *Election, merkleRoot []byte) {
	if err := app.SetRoot(modal, false).EnableMouse(true).Run(); err != nil {
		panic(err)
	}
	return election, merkleRoot
	return election, merkleRoot, electionKey
}

func createElection() (merkleRoot []byte, rendezvousNonce Nonce, candidates []Candidate) {
func createElection() (candidates []Candidate) {
	var form *tview.Form
	n := 3
	app := tview.NewApplication()


@@ 52,18 52,10 @@ func createElection() (merkleRoot []byte, rendezvousNonce Nonce, candidates []Ca
	done := func() {
		// TODO: ensure none of the candidates are empty
		app.Stop()
		rendezvousNonce = NewNonce()
		content := []merkletree.Content{rendezvousNonce}
		for i := 0; i < n-1; i++ {
			eo := Candidate(form.GetFormItem(i).(*tview.InputField).GetText())
			candidates = append(candidates, eo)
			content = append(content, eo)
			candidates = append(candidates,
				Candidate(form.GetFormItem(i).(*tview.InputField).GetText()))
		}
		optionsMerkle, err := merkletree.NewTree(content)
		if err != nil {
			panic(err)
		}
		merkleRoot = optionsMerkle.MerkleRoot()
	}
	form = tview.NewForm().
		AddInputField("1.", "", 50, nil, nil).


@@ 74,15 66,15 @@ func createElection() (merkleRoot []byte, rendezvousNonce Nonce, candidates []Ca
	if err := app.SetRoot(form, true).EnableMouse(true).Run(); err != nil {
		panic(err)
	}
	return merkleRoot, rendezvousNonce, candidates
	return candidates
}

func joinElection() (merkleRoot []byte, masterID peer.ID) {
func joinElection() (electionKey string) {
	app := tview.NewApplication()
	var form *tview.Form
	done := func() {
		app.Stop()
		merkleRoot, masterID = splitElectionKey(form.GetFormItem(0).(*tview.InputField).GetText())
		electionKey = form.GetFormItem(0).(*tview.InputField).GetText()
	}
	form = tview.NewForm().
		AddInputField("Election key:", "", 100, nil, nil).


@@ 90,7 82,7 @@ func joinElection() (merkleRoot []byte, masterID peer.ID) {
	if err := app.SetRoot(form, true).EnableMouse(true).Run(); err != nil {
		panic(err)
	}
	return merkleRoot, masterID
	return electionKey
}

// displays a voting UI to the user and returns the encoded ballot