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