A election.go => election.go +67 -0
@@ 0,0 1,67 @@
+package main
+
+import (
+ "strings"
+ "sync"
+
+ "github.com/libp2p/go-libp2p-core/peer"
+ "github.com/mr-tron/base58/base58"
+)
+
+type Election struct {
+ sync.RWMutex
+
+ Candidates []Candidate
+ // for slave: signifies when election was closed by master
+ //
+ // for master: signifies when user hits ENTER to close the election
+ //
+ // the number of peers known by master is passed through it
+ close chan<- int
+ // used by handleCmd to prevent closing election more than once
+ closed bool
+ ElectionKey string
+ masterID peer.ID
+ merkleRoot []byte
+ remoteVoters map[peer.ID]*Voter
+ rendezvousNonce Nonce
+}
+
+func NewElection() *Election {
+ return &Election{
+ remoteVoters: make(map[peer.ID]*Voter),
+ }
+}
+
+func (e *Election) MasterID() peer.ID {
+ if e.masterID != "" {
+ return e.masterID
+ }
+ zeroi := strings.IndexByte(e.ElectionKey, '0')
+ if zeroi == -1 {
+ panic("invalid election key")
+ }
+ var err error
+ e.masterID, err = peer.Decode(e.ElectionKey[zeroi+1:])
+ if err != nil {
+ panic(err)
+ }
+ logger.Info("masted ID:", e.masterID)
+ return e.masterID
+}
+
+func (e *Election) MerkleRoot() []byte {
+ if len(e.merkleRoot) > 0 {
+ return e.merkleRoot
+ }
+ zeroi := strings.IndexByte(e.ElectionKey, '0')
+ if zeroi == -1 {
+ panic("invalid election key")
+ }
+ logger.Info("merkle root:", e.ElectionKey[:zeroi])
+ merkleRoot, err := base58.Decode(e.ElectionKey[:zeroi])
+ if err != nil {
+ panic(err)
+ }
+ return merkleRoot
+}
M main.go => main.go +47 -42
@@ 24,39 24,43 @@ const protocolID = protocol.ID("/tallyard/0.0.0")
var logger = log.Logger("tallyard")
-func bootstrap(election *Election, me *Me, merkleRoot []byte, electionKey string, hostOpts ...libp2p.Option) {
- me.ctx = context.Background()
- election.remoteVoters = make(map[peer.ID]*Voter)
+func NewLocalVoter(hostOpts ...libp2p.Option) *LocalVoter {
+ localVoter := new(LocalVoter)
+ localVoter.ctx = context.Background()
routing := libp2p.Routing(func(h host.Host) (routing.PeerRouting, error) {
var err error
- me.kdht, err = dht.New(me.ctx, h)
+ localVoter.kdht, err = dht.New(localVoter.ctx, h)
if err != nil {
- return me.kdht, err
+ return localVoter.kdht, err
}
logger.Info("boostrapping the DHT")
- if err = me.kdht.Bootstrap(me.ctx); err != nil {
+ if err = localVoter.kdht.Bootstrap(localVoter.ctx); err != nil {
panic(err)
}
- return me.kdht, err
+ return localVoter.kdht, err
})
var err error
- me.Host, err = libp2p.New(me.ctx, append(hostOpts, routing)...)
+ localVoter.Host, err = libp2p.New(localVoter.ctx, append(hostOpts, routing)...)
if err != nil {
panic(err)
}
- logger.Info("host: ", me.ID())
- logger.Info("addrs: ", me.Addrs())
+ logger.Info("host: ", localVoter.ID())
+ logger.Info("addrs: ", localVoter.Addrs())
+ return localVoter
+}
+
+func (localVoter *LocalVoter) Bootstrap(election *Election) {
var wg sync.WaitGroup
for _, peerAddr := range dht.DefaultBootstrapPeers {
peerInfo, _ := peer.AddrInfoFromP2pAddr(peerAddr)
wg.Add(1)
go func() {
defer wg.Done()
- if err := me.Connect(me.ctx, *peerInfo); err != nil {
+ if err := localVoter.Connect(localVoter.ctx, *peerInfo); err != nil {
logger.Warning(err)
} else {
logger.Info("connection established with bootstrap node:", *peerInfo)
@@ 65,15 69,15 @@ func bootstrap(election *Election, me *Me, merkleRoot []byte, electionKey string
}
wg.Wait()
- if election.masterID == "" { // we are the master
+ if election.ElectionKey == "" { // we are the master
fmt.Println("share this with peers:")
- electionKey = fmt.Sprintf("%s0%s", base58.Encode(merkleRoot), me.ID())
- fmt.Printf("%s\n", electionKey)
+ election.ElectionKey = fmt.Sprintf("%s0%s", base58.Encode(election.MerkleRoot()), localVoter.ID())
+ fmt.Printf("%s\n", election.ElectionKey)
err := saveElectionInfo(
- electionKey,
- me.Peerstore().PrivKey(me.ID()),
- me.Peerstore().PubKey(me.ID()),
+ election.ElectionKey,
+ localVoter.Peerstore().PrivKey(localVoter.ID()),
+ localVoter.Peerstore().PubKey(localVoter.ID()),
)
if err != nil {
panic(err)
@@ 84,11 88,11 @@ func bootstrap(election *Election, me *Me, merkleRoot []byte, electionKey string
election.Lock()
ch := make(chan int, 1)
election.close = ch
- go findPeers(ch, election, me)
+ go findPeers(ch, election, localVoter)
election.Unlock()
- me.SetStreamHandler(protocolID, func(stream network.Stream) {
- streamHandler(stream, election, me)
+ localVoter.SetStreamHandler(protocolID, func(stream network.Stream) {
+ streamHandler(stream, election, localVoter)
})
fmt.Println("press ENTER to solidify group of voters and start voting")
@@ 106,7 110,7 @@ func bootstrap(election *Election, me *Me, merkleRoot []byte, electionKey string
election.Unlock()
election.RLock()
for _, voter := range election.remoteVoters {
- stream, err := me.NewStream(me.ctx, voter.addrInfo.ID, protocolID)
+ stream, err := localVoter.NewStream(localVoter.ctx, voter.addrInfo.ID, protocolID)
if err != nil {
panic(err)
}
@@ 118,16 122,16 @@ func bootstrap(election *Election, me *Me, merkleRoot []byte, electionKey string
election.RUnlock()
} else { // we are a slave
err := saveElectionInfo(
- electionKey,
- me.Peerstore().PrivKey(me.ID()),
- me.Peerstore().PubKey(me.ID()),
+ election.ElectionKey,
+ localVoter.Peerstore().PrivKey(localVoter.ID()),
+ localVoter.Peerstore().PubKey(localVoter.ID()),
)
if err != nil {
panic(err)
}
logger.Info("attempting to open stream with master peer...")
- stream, err := me.NewStream(me.ctx, election.masterID, protocolID)
+ stream, err := localVoter.NewStream(localVoter.ctx, election.MasterID(), protocolID)
rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
if err != nil {
panic(err)
@@ 157,7 161,7 @@ func bootstrap(election *Election, me *Me, merkleRoot []byte, electionKey string
}
str = stripNewline(str)
if str != "" {
- election.candidates = append(election.candidates, Candidate(str))
+ election.Candidates = append(election.Candidates, Candidate(str))
}
if err == io.EOF {
break
@@ 166,16 170,16 @@ func bootstrap(election *Election, me *Me, merkleRoot []byte, electionKey string
logger.Info("done fetching election info")
logger.Info("checking authenticity of election info...")
- verifyElectionInfo(election, merkleRoot)
+ verifyElectionInfo(election, election.MerkleRoot())
// channel used to signify when election is closed
ch := make(chan int, 1)
election.close = ch
// now that we have election info, begin handling streams
- me.SetStreamHandler(protocolID, func(stream network.Stream) {
- streamHandler(stream, election, me)
+ localVoter.SetStreamHandler(protocolID, func(stream network.Stream) {
+ streamHandler(stream, election, localVoter)
})
- findPeers(ch, election, me)
+ findPeers(ch, election, localVoter)
}
}
@@ 183,7 187,7 @@ func main() {
log.SetAllLoggers(log.LevelWarn)
verbose := flag.Bool("v", false, "enable verbose logging for debugging")
- master := flag.Bool("m", false, "indicate that this node is the " +
+ master := flag.Bool("m", false, "indicate that this node is the "+
"master and the candidates are given via positional arguments")
flag.Parse()
if *verbose {
@@ 198,35 202,34 @@ func main() {
election *Election
electionKey string
identityOpt libp2p.Option = libp2p.RandomIdentity
- me *Me = new(Me)
- merkleRoot []byte
)
if *master {
// we are the master and the candidates are the positional
// arguments
- election = new(Election)
+ election = NewElection()
for _, candidate := range flag.Args() {
- election.candidates = append(
- election.candidates,
+ election.Candidates = append(
+ election.Candidates,
Candidate(candidate),
)
}
+ election.GenMerkleFromCandidates()
} else if electionKey = flag.Arg(0); electionKey != "" {
// we are a slave slave the and election key was given via CLI
// args
- election = new(Election)
- merkleRoot, election.masterID = splitElectionKey(electionKey)
+ election = NewElection()
+ election.ElectionKey = electionKey
} else {
// create/join election via TUI
- election, merkleRoot, electionKey = tui()
+ election = tui()
if election == nil {
// tui form wasn't submitted (maybe the user hit ^C)
os.Exit(0)
}
}
- if electionKey != "" { // we are a slave
+ if electionKey != "" {
// try to recover election info if we've joined before
electionInfo, err := getElectionInfo(electionKey)
if err != nil {
@@ 243,7 246,9 @@ func main() {
}
}
- bootstrap(election, me, merkleRoot, electionKey, identityOpt)
+ localVoter := NewLocalVoter(identityOpt)
+
+ localVoter.Bootstrap(election)
- startVoting(election, me)
+ startVoting(election, localVoter)
}
M merkle.go => merkle.go +4 -26
@@ 6,10 6,8 @@ import (
"crypto/sha256"
"fmt"
"os"
- "strings"
"github.com/cbergoon/merkletree"
- "github.com/libp2p/go-libp2p-core/peer"
"github.com/mr-tron/base58/base58"
)
@@ 57,7 55,7 @@ func (n Nonce) Equals(other merkletree.Content) (bool, error) {
func verifyElectionInfo(election *Election, merkleRoot []byte) {
content := []merkletree.Content{election.rendezvousNonce}
var err error
- for _, eo := range election.candidates {
+ for _, eo := range election.Candidates {
content = append(content, eo)
}
optionsMerkle, err := merkletree.NewTree(content)
@@ 72,35 70,15 @@ func verifyElectionInfo(election *Election, merkleRoot []byte) {
}
}
-func splitElectionKey(electionKey string) (merkleRoot []byte, masterID peer.ID) {
- var err error
- zeroi := strings.IndexByte(electionKey, '0')
- if zeroi == -1 {
- panic("invalid election key")
- }
- logger.Info("merkle root:", electionKey[:zeroi])
- merkleRoot, err = base58.Decode(electionKey[:zeroi])
- if err != nil {
- panic(err)
- }
- masterID, err = peer.Decode(electionKey[zeroi+1:])
- if err != nil {
- panic(err)
- }
- logger.Info("master ID:", masterID)
- return merkleRoot, masterID
-}
-
-func (e *Election) createMerkle(candidates []Candidate) (merkleRoot []byte) {
+func (e *Election) GenMerkleFromCandidates() {
e.rendezvousNonce = NewNonce()
content := []merkletree.Content{e.rendezvousNonce}
- for _, cand := range candidates {
+ for _, cand := range e.Candidates {
content = append(content, cand)
}
optionsMerkle, err := merkletree.NewTree(content)
if err != nil {
panic(err)
}
- merkleRoot = optionsMerkle.MerkleRoot()
- return merkleRoot
+ e.merkleRoot = optionsMerkle.MerkleRoot()
}
M ui.go => ui.go +10 -11
@@ 10,27 10,26 @@ import (
"github.com/rivo/tview"
)
-func tui() (election *Election, merkleRoot []byte, electionKey string) {
+func tui() (election *Election) {
app := tview.NewApplication()
done := func(buttonIndex int, buttonLabel string) {
app.Stop()
- election = new(Election)
+ election = NewElection()
switch buttonLabel {
case "Create Election":
- election.candidates = createElection()
+ election.Candidates = getCandidatesTUI()
// TODO: slaves should check that len candidates >= 2
- if election.candidates == nil || len(election.candidates) == 0 {
+ if election.Candidates == nil || len(election.Candidates) == 0 {
fmt.Printf("no candidates entered; exiting\n")
os.Exit(0)
}
- merkleRoot = election.createMerkle(election.candidates)
+ election.GenMerkleFromCandidates()
case "Join Election":
- electionKey := joinElection()
- if electionKey == "" {
+ election.ElectionKey = getElectionKeyTUI()
+ if election.ElectionKey == "" {
fmt.Printf("no election key given; exiting\n")
os.Exit(0)
}
- merkleRoot, election.masterID = splitElectionKey(electionKey)
}
}
modal := tview.NewModal().
@@ 40,10 39,10 @@ func tui() (election *Election, merkleRoot []byte, electionKey string) {
if err := app.SetRoot(modal, false).EnableMouse(true).Run(); err != nil {
panic(err)
}
- return election, merkleRoot, electionKey
+ return election
}
-func createElection() (candidates []Candidate) {
+func getCandidatesTUI() (candidates []Candidate) {
var form *tview.Form
n := 3
app := tview.NewApplication()
@@ 79,7 78,7 @@ func createElection() (candidates []Candidate) {
return candidates
}
-func joinElection() (electionKey string) {
+func getElectionKeyTUI() (electionKey string) {
app := tview.NewApplication()
var form *tview.Form
done := func() {
M voter.go => voter.go +48 -65
@@ 30,13 30,12 @@ type Voter struct {
addrInfo peer.AddrInfo
}
-type Me struct {
+type LocalVoter struct {
Voter
host.Host
- ctx context.Context
- kdht *dht.IpfsDHT
-
- poly *Poly
+ ctx context.Context
+ kdht *dht.IpfsDHT
+ poly *Poly
// mutexs only used for atomicity; atomicity.Value sucks because we lose
// type safety with interface{}
@@ 45,22 44,6 @@ type Me struct {
inputMu sync.RWMutex // TODO remove by generating input right away
}
-type Election struct {
- sync.RWMutex
- // for slave: signifies when election was closed by master
- //
- // for master: signifies when user hits ENTER to close the election
- //
- // the number of peers know by master is passed through it
- close chan<- int
- closed bool // used by handleCmd to prevent closing election more than once
-
- candidates []Candidate
- masterID peer.ID
- remoteVoters map[peer.ID]*Voter
- rendezvousNonce Nonce
-}
-
func stripNewline(str string) string {
if str == "" {
return str
@@ 71,11 54,11 @@ func stripNewline(str string) string {
return str
}
-func handleCmd(cmd string, rw *bufio.ReadWriter, stream network.Stream, election *Election, me *Me) {
+func handleCmd(cmd string, rw *bufio.ReadWriter, stream network.Stream, election *Election, localVoter *LocalVoter) {
switch cmd {
case "info":
rw.WriteString(fmt.Sprintf("%s\n", election.rendezvousNonce))
- for _, option := range election.candidates {
+ for _, option := range election.Candidates {
rw.WriteString(fmt.Sprintf("%s\n", option))
}
rw.Flush()
@@ 122,9 105,9 @@ func handleCmd(cmd string, rw *bufio.ReadWriter, stream network.Stream, election
},
}
case "eval": // peer is giving their input and requesting output from our poly
- me.polyMu.RLock()
- defer me.polyMu.RUnlock()
- if me.poly == nil {
+ localVoter.polyMu.RLock()
+ defer localVoter.polyMu.RUnlock()
+ if localVoter.poly == nil {
logger.Warning("peer attempted to eval before we had our poly:",
stream.Conn().RemotePeer())
return
@@ 151,25 134,25 @@ func handleCmd(cmd string, rw *bufio.ReadWriter, stream network.Stream, election
defer peer.inputMu.Unlock()
peer.input = new(big.Int).SetBytes(inputBytes)
logger.Infof("%s input: %s", peer.addrInfo.ID, peer.input)
- output := me.poly.Eval(peer.input)
+ output := localVoter.poly.Eval(peer.input)
rw.WriteString(base58.Encode(output.Bytes()))
rw.Flush()
case "sum":
- me.sumMu.RLock()
- defer me.sumMu.RUnlock()
- if me.sum == nil {
+ localVoter.sumMu.RLock()
+ defer localVoter.sumMu.RUnlock()
+ if localVoter.sum == nil {
logger.Info("peer attempted to fetch sum "+
"before we computed it:", stream.Conn().RemotePeer())
return
}
- rw.WriteString(base58.Encode(me.sum.Bytes()))
+ rw.WriteString(base58.Encode(localVoter.sum.Bytes()))
rw.Flush()
default:
logger.Warningf("uknown command %s", cmd)
}
}
-func streamHandler(stream network.Stream, election *Election, me *Me) {
+func streamHandler(stream network.Stream, election *Election, localVoter *LocalVoter) {
logger.Info("got a new stream:", stream)
logger.Info("remote peer:", stream.Conn().RemotePeer())
rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream))
@@ 181,18 164,18 @@ func streamHandler(stream network.Stream, election *Election, me *Me) {
cmd = stripNewline(cmd)
logger.Info("cmd:", cmd)
- handleCmd(cmd, rw, stream, election, me)
+ handleCmd(cmd, rw, stream, election, localVoter)
stream.Close()
}
-func findPeers(closeElection <-chan int, election *Election, me *Me) {
- routingDiscovery := discovery.NewRoutingDiscovery(me.kdht)
+func findPeers(closeElection <-chan int, election *Election, localVoter *LocalVoter) {
+ routingDiscovery := discovery.NewRoutingDiscovery(localVoter.kdht)
logger.Info("announcing ourselves")
- discovery.Advertise(me.ctx, routingDiscovery, string(election.rendezvousNonce))
+ discovery.Advertise(localVoter.ctx, routingDiscovery, string(election.rendezvousNonce))
logger.Info("successfully announced!")
fmt.Println("finding other voters...")
- peerChan, err := routingDiscovery.FindPeers(me.ctx, string(election.rendezvousNonce))
+ peerChan, err := routingDiscovery.FindPeers(localVoter.ctx, string(election.rendezvousNonce))
if err != nil {
panic(err)
}
@@ 203,17 186,17 @@ func findPeers(closeElection <-chan int, election *Election, me *Me) {
}
select {
case peer := <-peerChan:
- if peer.ID == me.ID() {
+ if peer.ID == localVoter.ID() {
continue
}
fmt.Printf("found voter: %s\n", peer.ID)
logger.Info("connecting to:", peer)
- err = me.Connect(me.ctx, peer)
+ err = localVoter.Connect(localVoter.ctx, peer)
if err != nil {
logger.Warn("couldn't connect to peer: ", err)
continue
}
- stream, err := me.NewStream(me.ctx, peer.ID, protocolID)
+ stream, err := localVoter.NewStream(localVoter.ctx, peer.ID, protocolID)
if err != nil {
logger.Warn("couldn't open stream with peer: ", err)
continue
@@ 234,14 217,14 @@ func findPeers(closeElection <-chan int, election *Election, me *Me) {
logger.Info("done finding peers")
}
-func (voter *Voter) fetchNumber(election *Election, me *Me, cmd string, args ...string) *big.Int {
+func (voter *Voter) fetchNumber(election *Election, localVoter *LocalVoter, cmd string, args ...string) *big.Int {
printErr := func(err error, msg string) {
logger.Errorf("%s: %s while fetcing `%s'; retrying in 2 seconds",
voter.addrInfo.ID, msg, cmd)
time.Sleep(time.Second * 2)
}
retry:
- stream, err := me.NewStream(me.ctx, voter.addrInfo.ID, protocolID)
+ stream, err := localVoter.NewStream(localVoter.ctx, voter.addrInfo.ID, protocolID)
if err != nil {
printErr(err, "couldn't open stream")
goto retry
@@ 284,31 267,31 @@ retry:
return new(big.Int).SetBytes(retBytes)
}
-func startVoting(election *Election, me *Me) {
+func startVoting(election *Election, localVoter *LocalVoter) {
var err error
- me.inputMu.Lock()
- me.input, err = RandomBigInt(128, false)
- me.inputMu.Unlock()
+ localVoter.inputMu.Lock()
+ localVoter.input, err = RandomBigInt(128, false)
+ localVoter.inputMu.Unlock()
if err != nil {
panic(err)
}
- logger.Infof("our input: %s", me.input)
+ logger.Infof("our input: %s", localVoter.input)
- ballot := vote(election.candidates)
+ ballot := vote(election.Candidates)
logger.Infof("our ballot: %v", ballot)
// no +1 since we want degree k-1 where k is total number of voters
- me.polyMu.Lock()
- me.poly = NewRandomPoly(uint(len(election.remoteVoters)), 1024, ballot)
- me.polyMu.Unlock()
- logger.Infof("our constant: %s", me.poly.constant)
+ localVoter.polyMu.Lock()
+ localVoter.poly = NewRandomPoly(uint(len(election.remoteVoters)), 1024, ballot)
+ localVoter.polyMu.Unlock()
+ logger.Infof("our constant: %s", localVoter.poly.constant)
// get outputs
var wg sync.WaitGroup
for _, voter := range election.remoteVoters {
wg.Add(1)
go func(voter *Voter) {
- voter.output = voter.fetchNumber(election, me, "eval", base58.Encode(me.input.Bytes()))
+ voter.output = voter.fetchNumber(election, localVoter, "eval", base58.Encode(localVoter.input.Bytes()))
logger.Infof("voter %s output: %s", voter.addrInfo.ID, voter.output)
wg.Done()
}(voter)
@@ 316,19 299,19 @@ func startVoting(election *Election, me *Me) {
wg.Wait()
// calculate sum
- me.sumMu.Lock()
- me.sum = me.poly.Eval(me.input)
+ localVoter.sumMu.Lock()
+ localVoter.sum = localVoter.poly.Eval(localVoter.input)
for _, voter := range election.remoteVoters {
- me.sum.Add(me.sum, voter.output)
+ localVoter.sum.Add(localVoter.sum, voter.output)
}
- me.sumMu.Unlock()
- logger.Infof("our sum: %s", me.sum)
+ localVoter.sumMu.Unlock()
+ logger.Infof("our sum: %s", localVoter.sum)
// get sums
for _, voter := range election.remoteVoters {
wg.Add(1)
go func(voter *Voter) {
- voter.sum = voter.fetchNumber(election, me, "sum")
+ voter.sum = voter.fetchNumber(election, localVoter, "sum")
logger.Infof("voter %s sum: %s",
voter.addrInfo.ID, voter.sum)
wg.Done()
@@ 336,7 319,7 @@ func startVoting(election *Election, me *Me) {
}
wg.Wait()
- mat := constructPolyMatrix(election, me)
+ mat := constructPolyMatrix(election, localVoter)
mat.RREF()
constant := mat[0][len(mat[0])-1]
@@ 347,16 330,16 @@ func startVoting(election *Election, me *Me) {
result := constant.Num().Bytes()
// number of bytes we need to insert at the front since they're zero
- diff := (len(election.candidates)*len(election.candidates)) - len(result)
+ diff := (len(election.Candidates)*len(election.Candidates)) - len(result)
result = append(make([]byte, diff), result...)
- printResults(result, election.candidates)
+ printResults(result, election.Candidates)
// temporary
select {}
}
-func constructPolyMatrix(election *Election, me *Me) Matrix {
+func constructPolyMatrix(election *Election, localVoter *LocalVoter) Matrix {
mat := make(Matrix, len(election.remoteVoters) + 1) // row for everyone (including ourselves)
i := 0
@@ 378,9 361,9 @@ func constructPolyMatrix(election *Election, me *Me) Matrix {
row[0].SetInt64(1)
var j int64
for j = 1; j <= int64(len(election.remoteVoters)); j++ {
- row[j].SetInt(new(big.Int).Exp(me.input, big.NewInt(j), nil))
+ row[j].SetInt(new(big.Int).Exp(localVoter.input, big.NewInt(j), nil))
}
- row[j].SetInt(me.sum)
+ row[j].SetInt(localVoter.sum)
return mat
}