~edwargix/tallyard

4675b943ab8e844c8d5198ec600bec861845e021 — David Florness 5 years ago 7d3a9ff
Use merkle tree in election key to verify election options
3 files changed, 76 insertions(+), 6 deletions(-)

M go.mod
M go.sum
M main.go
M go.mod => go.mod +2 -0
@@ 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

M go.sum => go.sum +4 -0
@@ 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=

M main.go => main.go +70 -6
@@ 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()
	}
}