@@ 1,6 1,7 @@
module gitlab.com/edwargix/tallyard
require (
+ github.com/cbergoon/merkletree v0.2.0
github.com/gogo/protobuf v1.3.1
github.com/google/uuid v1.1.1
github.com/ipfs/go-datastore v0.4.2
@@ 17,6 18,7 @@ require (
github.com/libp2p/go-libp2p-secio v0.2.1
github.com/libp2p/go-libp2p-swarm v0.2.2
github.com/libp2p/go-libp2p-tls v0.1.3
+ github.com/mr-tron/base58 v1.2.0
github.com/multiformats/go-multiaddr v0.2.0
github.com/multiformats/go-multiaddr-net v0.1.2
github.com/rivo/tview v0.0.0-20200528200248-fe953220389f
@@ 20,6 20,8 @@ github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVa
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
+github.com/cbergoon/merkletree v0.2.0 h1:Bttqr3OuoiZEo4ed1L7fTasHka9II+BF9fhBfbNEEoQ=
+github.com/cbergoon/merkletree v0.2.0/go.mod h1:5c15eckUgiucMGDOCanvalj/yJnD+KAZj1qyJtRW5aM=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@@ 281,6 283,8 @@ github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVq
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc=
github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
+github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
+github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44=
@@ 2,11 2,16 @@ package main
import (
"bufio"
+ "bytes"
"context"
+ "crypto/sha256"
"fmt"
"io"
+ "os"
+ "strings"
"sync"
+ "github.com/cbergoon/merkletree"
"github.com/ipfs/go-log"
"github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p-core/host"
@@ 15,21 20,40 @@ import (
"github.com/libp2p/go-libp2p-core/protocol"
dht "github.com/libp2p/go-libp2p-kad-dht"
routing "github.com/libp2p/go-libp2p-routing"
+ "github.com/mr-tron/base58/base58"
"github.com/rivo/tview"
"github.com/whyrusleeping/go-logging"
)
+type ElectionOption string
+
var (
Logger = log.Logger("tallyard")
ProtocolID = protocol.ID("/tallyard/0.0.0")
- electionOptions []string
+ electionOptions []ElectionOption
+ optionsMerkle *merkletree.MerkleTree
+
+ merkleRoot []byte
masterID peer.ID
+
ctx context.Context
h host.Host
kdht *dht.IpfsDHT
)
+func (eo ElectionOption) CalculateHash() ([]byte, error) {
+ h := sha256.New()
+ if _, err := h.Write([]byte(string(eo))); err != nil {
+ return nil, err
+ }
+ return h.Sum(nil), nil
+}
+
+func (eo ElectionOption) Equals(other merkletree.Content) (bool, error) {
+ return string(eo) == string(other.(ElectionOption)), nil
+}
+
func masterStreamHandler(stream network.Stream) {
Logger.Info("got a new stream!")
writer := bufio.NewWriter(stream)
@@ 64,9 88,16 @@ func createElection() {
AddButton("Done", func() {
// TODO: ensure none of the candidates are empty
app.Stop()
+ var content []merkletree.Content
for i := 0; i < n-1; i++ {
- electionOptions = append(electionOptions,
- form.GetFormItem(i).(*tview.InputField).GetText())
+ eo := ElectionOption(form.GetFormItem(i).(*tview.InputField).GetText())
+ electionOptions = append(electionOptions, eo)
+ content = append(content, eo)
+ }
+ var err error
+ optionsMerkle, err = merkletree.NewTree(content)
+ if err != nil {
+ panic(err)
}
})
if err := app.SetRoot(form, true).EnableMouse(true).Run(); err != nil {
@@ 78,11 109,20 @@ func joinElection() {
app := tview.NewApplication()
var form *tview.Form
form = tview.NewForm().
- AddInputField("Master peer ID:", "", 50, nil, nil).
+ AddInputField("Election key:", "", 50, nil, nil).
AddButton("Continue", func() {
- var err error
app.Stop()
- masterID, err = peer.Decode(form.GetFormItem(0).(*tview.InputField).GetText())
+ electionKey := form.GetFormItem(0).(*tview.InputField).GetText()
+
+ zeroi := strings.IndexByte(electionKey, '0')
+ var err error
+ merkleRoot, err = base58.Decode(electionKey[:zeroi])
+ if err != nil {
+ panic(err)
+ }
+ Logger.Info("merkle root:", merkleRoot)
+
+ masterID, err = peer.Decode(electionKey[zeroi+1:])
if err != nil {
panic(err)
}
@@ 93,6 133,24 @@ func joinElection() {
}
}
+func checkMerkle() {
+ var content []merkletree.Content
+ var err error
+ for _, eo := range electionOptions {
+ content = append(content, eo)
+ }
+ optionsMerkle, err = merkletree.NewTree(content)
+ if err != nil {
+ panic(err)
+ }
+ if bytes.Compare(optionsMerkle.MerkleRoot(), merkleRoot) == 0 {
+ fmt.Println("options merkle verification succeeded!")
+ } else {
+ fmt.Println("options merkle verification failed; exiting")
+ os.Exit(1)
+ }
+}
+
func bootstrap() {
var err error
@@ 135,6 193,10 @@ func bootstrap() {
wg.Wait()
if masterID == "" { // we are the master
+ fmt.Println("share this with peers:")
+ fmt.Printf("%s0%s\n",
+ base58.Encode(optionsMerkle.MerkleRoot()),
+ h.ID())
Logger.Info("waiting for incoming streams...")
h.SetStreamHandler(ProtocolID, masterStreamHandler)
} else { // we are a slave
@@ 155,10 217,12 @@ func bootstrap() {
if str[len(str)-1] == '\n' {
str = str[:len(str)-1]
}
+ electionOptions = append(electionOptions, ElectionOption(str))
fmt.Println(str)
}
stream.Close()
Logger.Info("stream with master peer closed")
+ checkMerkle()
}
}