From f3db4d6e97e358ef181b9aaf8f2cf260ca1a0f31 Mon Sep 17 00:00:00 2001 From: David Florness Date: Sat, 9 Jan 2021 16:31:06 -0500 Subject: [PATCH] Switch to Matrix --- .gitignore | 2 +- Makefile | 10 +- cmd/tallyard/main.go | 82 +++++ election.go | 88 ----- election/election.go | 35 ++ election/map.go | 50 +++ merkle.go => election/merkle.go | 23 +- election/msg.go | 224 +++++++++++++ election/version.go | 3 + election/voter.go | 305 +++++++++++++++++ go.mod | 10 +- go.sum | 456 -------------------------- main.go | 224 ------------- linalg.go => math/linalg.go | 2 +- linalg_test.go => math/linalg_test.go | 2 +- poly.go => math/poly.go | 2 +- matrix/crypto_logger.go | 2 +- matrix/data.go | 2 +- matrix/main.go | 139 -------- matrix/state_store.go | 2 +- ui.go | 188 ----------- ui/tui.go | 355 ++++++++++++++++++++ voter.go | 390 ---------------------- 23 files changed, 1073 insertions(+), 1523 deletions(-) create mode 100644 cmd/tallyard/main.go delete mode 100644 election.go create mode 100644 election/election.go create mode 100644 election/map.go rename merkle.go => election/merkle.go (62%) create mode 100644 election/msg.go create mode 100644 election/version.go create mode 100644 election/voter.go delete mode 100644 main.go rename linalg.go => math/linalg.go (98%) rename linalg_test.go => math/linalg_test.go (98%) rename poly.go => math/poly.go (99%) delete mode 100644 matrix/main.go delete mode 100644 ui.go create mode 100644 ui/tui.go delete mode 100644 voter.go diff --git a/.gitignore b/.gitignore index 5c299e7..14e1081 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ .\#* .DS_Store compiled/ -tallyard +/tallyard diff --git a/Makefile b/Makefile index e3762b1..3efc190 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,11 @@ GOSRC != find . -name '*.go' -GOSRC += go.mod go.sum GO = go RM ?= rm -f -tallyard: $(GOSRC) - $(GO) build -o $@ +tallyard: $(GOSRC) go.mod go.sum + $(GO) build -o $@ cmd/tallyard/main.go + +clean: + $(RM) tallyard + +.PHONY: clean diff --git a/cmd/tallyard/main.go b/cmd/tallyard/main.go new file mode 100644 index 0000000..789acc7 --- /dev/null +++ b/cmd/tallyard/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "fmt" + "os" + + "github.com/kyoh86/xdg" + "maunium.net/go/mautrix" + "maunium.net/go/mautrix/event" + + "tallyard.xyz/election" + "tallyard.xyz/matrix" + "tallyard.xyz/ui" +) + +var gobStorePath string = xdg.DataHome() + "/tallyard/gob.dat" +var electionFilter *mautrix.Filter = &mautrix.Filter{ + Room: mautrix.RoomFilter{ + Timeline: mautrix.FilterPart{ + Types: []event.Type{ + election.CreateElectionMessage, + election.JoinElectionMessage, + election.StartElectionMessage, + election.EvalMessage, + election.SumMessage, + election.ResultMessage, + }, + }, + }, +} + +func main() { + data, err := matrix.GetData() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + client, err := mautrix.NewClient(data.Homeserver, data.UserID, data.AccessToken) + if err != nil { + panic(err) + } + + localVoter := election.NewLocalVoter(client.UserID) + + elections := election.NewElectionsMap() + + syncer := client.Syncer.(*mautrix.DefaultSyncer) + syncer.OnEvent(client.Store.(*mautrix.InMemoryStore).UpdateState) + syncer.OnEventType(election.CreateElectionMessage, func(source mautrix.EventSource, evt *event.Event) { + election.OnCreateElectionMessage(source, evt, elections) + }) + syncer.OnEventType(election.JoinElectionMessage, func(source mautrix.EventSource, evt *event.Event) { + election.OnJoinElectionMessage(source, evt, elections) + }) + syncer.OnEventType(election.StartElectionMessage, func(source mautrix.EventSource, evt *event.Event) { + election.OnStartElectionMessage(source, evt, elections) + }) + syncer.OnEventType(election.EvalMessage, func(source mautrix.EventSource, evt *event.Event) { + election.OnEvalMessage(source, evt, elections, localVoter) + }) + syncer.OnEventType(election.SumMessage, func(source mautrix.EventSource, evt *event.Event) { + election.OnSumMessage(source, evt, elections) + }) + syncer.OnEventType(election.ResultMessage, func(source mautrix.EventSource, evt *event.Event) { + election.OnResultMessage(source, evt, elections) + }) + + go func() { + res, err := client.CreateFilter(electionFilter) + if err != nil { + panic(err) + } + client.Store.SaveFilterID(client.UserID, res.FilterID) + err = client.Sync() + if err != nil { + panic(err) + } + }() + + ui.TUI(client, elections, localVoter) +} diff --git a/election.go b/election.go deleted file mode 100644 index 7b7cdec..0000000 --- a/election.go +++ /dev/null @@ -1,88 +0,0 @@ -package main - -import ( - "fmt" - "strings" - "sync" - - "github.com/cbergoon/merkletree" - "github.com/libp2p/go-libp2p" - "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 - localVoter *LocalVoter - masterID peer.ID - merkleRoot []byte - remoteVoters map[peer.ID]*Voter - rendezvousNonce Nonce -} - -func NewElectionWithCandidates(candidates []Candidate) *Election { - localVoter := NewLocalVoter(libp2p.RandomIdentity) - e := &Election{ - Candidates: candidates, - localVoter: localVoter, - masterID: localVoter.ID(), - remoteVoters: make(map[peer.ID]*Voter), - } - e.rendezvousNonce = NewNonce() - content := []merkletree.Content{e.rendezvousNonce} - for _, cand := range e.Candidates { - content = append(content, cand) - } - optionsMerkle, err := merkletree.NewTree(content) - if err != nil { - panic(err) - } - e.merkleRoot = optionsMerkle.MerkleRoot() - e.electionKey = fmt.Sprintf("%s0%s", - base58.Encode(e.merkleRoot), - localVoter.ID()) - return e -} - -func NewElectionWithElectionKey(electionKey string) *Election { - var ( - candidates []Candidate - localVoter *LocalVoter - rendezvousNonce Nonce - ) - localVoter = NewLocalVoter(libp2p.RandomIdentity) - e := &Election{ - Candidates: candidates, - electionKey: electionKey, - localVoter: localVoter, - remoteVoters: make(map[peer.ID]*Voter), - rendezvousNonce: rendezvousNonce, - } - zeroi := strings.IndexByte(electionKey, '0') - if zeroi == -1 { - panic("invalid election key") - } - logger.Info("merkle root:", electionKey[:zeroi]) - var err error - e.merkleRoot, err = base58.Decode(electionKey[:zeroi]) - if err != nil { - panic(err) - } - e.masterID, err = peer.Decode(electionKey[zeroi+1:]) - if err != nil { - panic(err) - } - logger.Info("master ID:", e.masterID) - return e -} diff --git a/election/election.go b/election/election.go new file mode 100644 index 0000000..b4b1c42 --- /dev/null +++ b/election/election.go @@ -0,0 +1,35 @@ +package election + +import ( + "sync" + + "maunium.net/go/mautrix/id" +) + +type Election struct { + sync.RWMutex + + Candidates []Candidate + CreateEventId id.EventID + CreationTimestamp int64 + Creator id.UserID + RoomID id.RoomID + Started bool + Title string + Voters map[id.UserID]*Voter +} + +func NewElection(candidates []Candidate, createEventId id.EventID, + creationTimestamp int64, creator id.UserID, roomID id.RoomID, + started bool, title string) *Election { + return &Election{ + Candidates: candidates, + CreateEventId: createEventId, + CreationTimestamp: creationTimestamp, + Creator: creator, + RoomID: roomID, + Started: started, + Title: title, + Voters: make(map[id.UserID]*Voter), + } +} diff --git a/election/map.go b/election/map.go new file mode 100644 index 0000000..ad23435 --- /dev/null +++ b/election/map.go @@ -0,0 +1,50 @@ +package election + +import ( + "sort" + "sync" + + "maunium.net/go/mautrix/id" +) + +type ElectionsMap struct{ + sync.RWMutex + M map[id.EventID]*Election + L []*Election // sorted list of elections by CreationTimestamp (newest to oldest) +} + +func NewElectionsMap() *ElectionsMap { + return &ElectionsMap{ + M: make(map[id.EventID]*Election), + L: make([]*Election, 0), + } +} + +func (em *ElectionsMap) Get(eventID id.EventID) *Election { + em.RLock() + defer em.RUnlock() + return em.M[eventID] +} + +func (em *ElectionsMap) GetOk(eventID id.EventID) (*Election, bool) { + em.RLock() + defer em.RUnlock() + el, ok := em.M[eventID] + return el, ok +} + +func (em *ElectionsMap) Set(eventID id.EventID, el *Election) { + em.Lock() + defer em.Unlock() + + em.M[eventID] = el + + i := sort.Search(len(em.L), func(i int) bool { + return em.L[i].CreationTimestamp < el.CreationTimestamp + }) + newList := make([]*Election, len(em.L) + 1) + copy(newList[:i], em.L[:i]) + newList[i] = el + copy(newList[i+1:], em.L[i:]) + em.L = newList +} diff --git a/merkle.go b/election/merkle.go similarity index 62% rename from merkle.go rename to election/merkle.go index 67bac8d..b0feab4 100644 --- a/merkle.go +++ b/election/merkle.go @@ -1,11 +1,8 @@ -package main +package election import ( - "bytes" "crypto/rand" "crypto/sha256" - "fmt" - "os" "github.com/cbergoon/merkletree" "github.com/mr-tron/base58/base58" @@ -51,21 +48,3 @@ func (n Nonce) CalculateHash() ([]byte, error) { func (n Nonce) Equals(other merkletree.Content) (bool, error) { return n == other.(Nonce), nil } - -func verifyElectionInfo(election *Election, merkleRoot []byte) { - content := []merkletree.Content{election.rendezvousNonce} - var err error - for _, eo := range election.Candidates { - content = append(content, eo) - } - optionsMerkle, err := merkletree.NewTree(content) - if err != nil { - panic(err) - } - if bytes.Compare(optionsMerkle.MerkleRoot(), merkleRoot) == 0 { - fmt.Println("election info verification succeeded!") - } else { - fmt.Println("election info verification failed; exiting") - os.Exit(1) - } -} diff --git a/election/msg.go b/election/msg.go new file mode 100644 index 0000000..e79f20a --- /dev/null +++ b/election/msg.go @@ -0,0 +1,224 @@ +package election + +import ( + "encoding/base64" + "math/big" + "reflect" + + log "github.com/sirupsen/logrus" + "golang.org/x/crypto/nacl/box" + "maunium.net/go/mautrix" + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" +) + +var ( + // sent when user wants to create an election + CreateElectionMessage = event.Type{ + Type: "xyz.tallyard.new", + Class: event.MessageEventType, + } + // sent when a user wants to join an election + JoinElectionMessage = event.Type{ + Type: "xyz.tallyard.join", + Class: event.MessageEventType, + } + // sent when a user wants to start a previously-create election. The + // user who sends this message must be the one who created the election + StartElectionMessage = event.Type{ + Type: "xyz.tallyard.start", + Class: event.MessageEventType, + } + // indicate's user's evaluation of their polynomial using others' inputs + EvalMessage = event.Type{ + Type: "xyz.tallyard.eval", + Class: event.MessageEventType, + } + // indicates a user's individual summation + SumMessage = event.Type{ + Type: "xyz.tallyard.sum", + Class: event.MessageEventType, + } + // everyone announces their result at the end + ResultMessage = event.Type{ + Type: "xyz.tallyard.result", + Class: event.MessageEventType, + } +) + +type CreateElectionContent struct { + Candidates []Candidate `json:"candidates"` + Title string `json:"title"` + Version string `json:"version"` +} + +type JoinElectionContent struct { + CreateEventId id.EventID `json:"create_event_id"` + Input string `json:"input"` + NaclPublicKey string `json:"nacl_public_key"` +} + +type StartElectionContent struct { + CreateEventId id.EventID `json:"create_event_id"` + Voters []id.UserID `json:"voters"` +} + +type EvalMessageContent struct { + CreateEventId id.EventID `json:"create_event_id"` + Outputs map[id.UserID]string `json:"outputs"` +} + +type SumMessageContent struct { + CreateEventId id.EventID `json:"create_event_id"` + Sum string `json:"sum"` +} + +type ResultMessageContent struct { + CreateEventId id.EventID `json:"create_event_id"` + Result string `json:"result"` +} + +func init() { + event.TypeMap[CreateElectionMessage] = reflect.TypeOf(CreateElectionContent{}) + event.TypeMap[JoinElectionMessage] = reflect.TypeOf(JoinElectionContent{}) + event.TypeMap[StartElectionMessage] = reflect.TypeOf(StartElectionContent{}) + event.TypeMap[EvalMessage] = reflect.TypeOf(EvalMessageContent{}) + event.TypeMap[SumMessage] = reflect.TypeOf(SumMessageContent{}) + event.TypeMap[ResultMessage] = reflect.TypeOf(ResultMessageContent{}) +} + +func DebugCB(source mautrix.EventSource, evt *event.Event) { + return + // fmt.Printf("%[5]d: <%[1]s> %[4]s (%[2]s/%[3]s)\n", evt.Sender, evt.Type.String(), evt.ID, evt.Content.AsMessage().Body, source) +} + +func OnCreateElectionMessage(source mautrix.EventSource, evt *event.Event, elections *ElectionsMap) { + DebugCB(source, evt) + // TODO: check version + content, ok := evt.Content.Parsed.(*CreateElectionContent) + if !ok { + log.Warnf("ignoring %s's create since we couldn't cast message content to CreateElectionContent", evt.Sender) + return + } + elections.Set(evt.ID, NewElection(content.Candidates, evt.ID, + evt.Timestamp, evt.Sender, evt.RoomID, false, content.Title)) +} + +func OnJoinElectionMessage(source mautrix.EventSource, evt *event.Event, elections *ElectionsMap) { + DebugCB(source, evt) + content, ok := evt.Content.Parsed.(*JoinElectionContent) + if !ok { + log.Warnf("ignoring %s's join since we couldn't cast message content to JoinElectionContent", evt.Sender) + return + } + bytes, err := base64.StdEncoding.DecodeString(content.Input) + if err != nil { + log.Warnf("ignoring %s's join since we couldn't decode their input", evt.Sender) + return + } + input := new(big.Int).SetBytes(bytes) + el := elections.Get(content.CreateEventId) + el.Lock() + defer el.Unlock() + if el.Started { + log.Warnf("ignoring %s's join since the election has already started", evt.Sender) + return + } + var pubKey [32]byte + bytes, err = base64.StdEncoding.DecodeString(content.NaclPublicKey) + if err != nil { + log.Warnf("ignoring %s's join since we couldn't decode their public key", evt.Sender) + return + } + copy(pubKey[:], bytes) + el.Voters[evt.Sender] = NewVoter(evt.Sender, input, &pubKey) +} + +func OnStartElectionMessage(source mautrix.EventSource, evt *event.Event, elections *ElectionsMap) { + DebugCB(source, evt) + content, ok := evt.Content.Parsed.(*StartElectionContent) + if !ok { + log.Warnf("ignoring %s's election start since we couldn't cast message content to StartElectionContent", evt.Sender) + return + } + el := elections.Get(content.CreateEventId) + el.Lock() + defer el.Unlock() + if evt.Sender != el.Creator { + log.Warnf("ignoring %s's election start since they didn't start the election", evt.Sender) + return + } + el.Started = true +} + +func OnEvalMessage(source mautrix.EventSource, evt *event.Event, elections *ElectionsMap, localVoter *LocalVoter) { + DebugCB(source, evt) + content, ok := evt.Content.Parsed.(*EvalMessageContent) + if !ok { + log.Warn("ignoring eval message since we couldn't cast message content to EvalMessageContent") + return + } + encodedEncryptedOutput, ok := content.Outputs[localVoter.UserID] + if !ok { + log.Errorf("our user ID was not included in an eval message! The election will be unable to finish; blame %s", evt.Sender) + return + } + encryptedOutput, err := base64.StdEncoding.DecodeString(encodedEncryptedOutput) + if err != nil { + log.Errorf("couldn't decode encrypted output: %s", err) + return + } + el := elections.Get(content.CreateEventId) + el.Lock() + defer el.Unlock() + voter := el.Voters[evt.Sender] + + var decryptNonce [24]byte + copy(decryptNonce[:], encryptedOutput[:24]) + decryptedOutput, ok := box.Open(nil, encryptedOutput[24:], &decryptNonce, voter.PubKey, localVoter.PrivKey) + + if !ok { + log.Errorf("decryption error") + return + } + + voter.Output = new(big.Int).SetBytes(decryptedOutput) +} + +func OnSumMessage(source mautrix.EventSource, evt *event.Event, elections *ElectionsMap) { + DebugCB(source, evt) + content, ok := evt.Content.Parsed.(*SumMessageContent) + if !ok { + log.Warnf("ignoring %s's sum since we couldn't cast message content to SumMessageContent", evt.Sender) + return + } + sum, ok := new(big.Int).SetString(content.Sum, 64) + if !ok { + log.Warnf("ignoring %s's sum since we couldn't base64 decode the sum", evt.Sender) + return + } + el := elections.Get(content.CreateEventId) + el.Lock() + defer el.Unlock() + voter := el.Voters[evt.Sender] + voter.Sum = sum +} + +func OnResultMessage(source mautrix.EventSource, evt *event.Event, elections *ElectionsMap) { + DebugCB(source, evt) + content, ok := evt.Content.Parsed.(*ResultMessageContent) + if !ok { + log.Warnf("ignoring %s's result since we couldn't cast message content to ResultMessageContent", evt.Sender) + return + } + result, ok := new(big.Int).SetString(content.Result, 64) + if !ok { + log.Warnf("ignoring %s's result since we couldn't base64 decode the result", evt.Sender) + return + } + el := elections.Get(content.CreateEventId) + el.Lock() + defer el.Unlock() + voter := el.Voters[evt.Sender] + voter.Result = result +} diff --git a/election/version.go b/election/version.go new file mode 100644 index 0000000..49df425 --- /dev/null +++ b/election/version.go @@ -0,0 +1,3 @@ +package election + +const Version string = "0.0.0" diff --git a/election/voter.go b/election/voter.go new file mode 100644 index 0000000..909707d --- /dev/null +++ b/election/voter.go @@ -0,0 +1,305 @@ +package election + +import ( + "crypto/rand" + "encoding/base64" + "fmt" + "math/big" + + log "github.com/sirupsen/logrus" + "golang.org/x/crypto/nacl/box" + "maunium.net/go/mautrix" + "maunium.net/go/mautrix/id" + "tallyard.xyz/math" +) + +type Voter struct { + Input *big.Int + Output *big.Int + PubKey *[32]byte + Result *big.Int + Sum *big.Int + UserID id.UserID +} + +type LocalVoter struct { + *Voter + PrivKey *[32]byte + ballot []byte + poly *math.Poly +} + +func NewVoter(userID id.UserID, input *big.Int, pubKey *[32]byte) *Voter { + return &Voter{ + UserID: userID, + Input: input, + PubKey: pubKey, + } +} + +func NewLocalVoter(userID id.UserID) *LocalVoter { + pubKey, privKey, err := box.GenerateKey(rand.Reader) + if err != nil { + panic(err) + } + input, err := math.RandomBigInt(1024, false) + if err != nil { + panic(err) + } + return &LocalVoter{ + Voter: NewVoter(userID, input, pubKey), + PrivKey: privKey, + } +} + +func CreateElection(client *mautrix.Client, candidates []Candidate, title string, roomID id.RoomID) (id.EventID, error) { + resp, err := client.SendMessageEvent(roomID, CreateElectionMessage, CreateElectionContent{ + Candidates: candidates, + Title: title, + Version: Version, + }) + return resp.EventID, err +} + +func StartElection(client *mautrix.Client, election *Election) error { + // TODO check that we created the election + election.RLock() + defer election.RUnlock() + voters := make([]id.UserID, 0, len(election.Voters)) + for userID := range election.Voters { + voters = append(voters, userID) + } + _, err := client.SendMessageEvent(election.RoomID, StartElectionMessage, StartElectionContent{ + CreateEventId: election.CreateEventId, + Voters: voters, + }) + return err +} + +func (localVoter *LocalVoter) JoinElection(client *mautrix.Client, election *Election) error { + election.RLock() + defer election.RUnlock() + _, err := client.SendMessageEvent(election.RoomID, JoinElectionMessage, JoinElectionContent{ + CreateEventId: election.CreateEventId, + Input: base64.StdEncoding.EncodeToString(localVoter.Input.Bytes()), + NaclPublicKey: base64.StdEncoding.EncodeToString((*localVoter.PubKey)[:]), + }) + return err +} + +// func handleCmd(cmd string, rw *bufio.ReadWriter, stream network.Stream, election *Election) { +// localVoter := election.localVoter +// switch cmd { +// case "info": +// rw.WriteString(fmt.Sprintf("%s\n", election.rendezvousNonce)) +// for _, option := range election.Candidates { +// rw.WriteString(fmt.Sprintf("%s\n", option)) +// } +// rw.Flush() +// case "close": +// election.Lock() +// defer election.Unlock() +// if peer := stream.Conn().RemotePeer(); peer != election.masterID { +// log.Warning("received close command from non-master:", peer) +// return +// } +// if election.closed { +// log.Warning("election already closed") +// return +// } +// str, err := rw.ReadString('\n') +// if err != nil && err != io.EOF { +// panic(err) +// } +// str = stripNewline(str) +// numPeers, err := strconv.Atoi(str) +// if err != nil { +// panic(err) +// } +// election.close <- numPeers +// election.closed = true +// case "shake": +// election.Lock() +// defer election.Unlock() +// peerID := stream.Conn().RemotePeer() +// if election.closed { +// log.Warning("peer attempted to shake after "+ +// "election was closed:", peerID) +// return +// } +// if _, exists := election.RemoteVoters[peerID]; exists { +// log.Warning("peer attempted to shake after having already done so", peerID) +// return +// } +// fmt.Printf("found voter: %s\n", peerID) +// election.RemoteVoters[peerID] = &Voter{ +// addrInfo: peer.AddrInfo{ +// ID: peerID, +// Addrs: []multiaddr.Multiaddr{stream.Conn().RemoteMultiaddr()}, +// }, +// } +// case "eval": // peer is giving their input and requesting output from our poly +// localVoter.polyMu.RLock() +// defer localVoter.polyMu.RUnlock() +// if localVoter.poly == nil { +// log.Warning("peer attempted to eval before we had our poly:", +// stream.Conn().RemotePeer()) +// return +// } +// inputb58, err := rw.ReadString('\n') +// if err != nil && err != io.EOF { +// log.Warning("unable to read input from peer during eval:", +// stream.Conn().RemotePeer()) +// return +// } +// inputb58 = stripNewline(inputb58) +// inputBytes, err := base58.Decode(inputb58) +// if err != nil { +// log.Warning("unable to base58 decode input from peer during eval:", +// stream.Conn().RemotePeer()) +// return +// } +// peer, exists := election.RemoteVoters[stream.Conn().RemotePeer()] +// if !exists { +// log.Warning("received eval command from unrecognized peer") +// return +// } +// peer.inputMu.Lock() +// defer peer.inputMu.Unlock() +// peer.input = new(big.Int).SetBytes(inputBytes) +// log.Infof("%s input: %s", peer.addrInfo.ID, peer.input) +// output := localVoter.poly.Eval(peer.input) +// rw.WriteString(base58.Encode(output.Bytes())) +// rw.Flush() +// case "sum": +// localVoter.sumMu.RLock() +// defer localVoter.sumMu.RUnlock() +// if localVoter.sum == nil { +// log.Info("peer attempted to fetch sum "+ +// "before we computed it:", stream.Conn().RemotePeer()) +// return +// } +// rw.WriteString(base58.Encode(localVoter.sum.Bytes())) +// rw.Flush() +// default: +// log.Warningf("uknown command %s", cmd) +// } +// } + +// func (election *Election) StartVoting() { +// localVoter := election.localVoter + +// var err error +// localVoter.inputMu.Lock() +// localVoter.input, err = math.RandomBigInt(128, false) +// localVoter.inputMu.Unlock() +// if err != nil { +// panic(err) +// } +// log.Infof("our input: %s", localVoter.input) + +// localVoter.ballot = vote(election.Candidates) +// log.Infof("our ballot: %v", localVoter.ballot) + +// // no +1 since we want degree k-1 where k is total number of voters +// localVoter.polyMu.Lock() +// localVoter.poly = math.NewRandomPoly(uint(len(election.RemoteVoters)), +// 1024, localVoter.ballot) +// localVoter.polyMu.Unlock() +// log.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, "eval", base58.Encode(localVoter.input.Bytes())) +// logger.Infof("voter %s output: %s", voter.addrInfo.ID, voter.output) +// wg.Done() +// }(voter) +// } +// wg.Wait() + +// // calculate sum +// localVoter.sumMu.Lock() +// localVoter.sum = localVoter.poly.Eval(localVoter.input) +// for _, voter := range election.RemoteVoters { +// localVoter.sum.Add(localVoter.sum, voter.output) +// } +// 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, "sum") +// logger.Infof("voter %s sum: %s", +// voter.addrInfo.ID, voter.sum) +// wg.Done() +// }(voter) +// } +// wg.Wait() + +// mat := constructPolyMatrix(election) +// mat.RREF() + +// constant := mat[0][len(mat[0])-1] +// if !constant.IsInt() { +// panic("constant term is not an integer") +// } + +// 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) +// result = append(make([]byte, diff), result...) + +// printResults(result, election.Candidates) + +// // temporary +// select {} +// } + +// func constructPolyMatrix(election *Election) Matrix { +// mat := make(Matrix, len(election.remoteVoters) + 1) // row for everyone (including ourselves) + +// i := 0 +// for _, voter := range election.RemoteVoters { +// mat[i] = make([]big.Rat, len(mat) + 1) // includes column for sum +// row := mat[i] +// row[0].SetInt64(1) +// var j int64 +// for j = 1; j <= int64(len(election.RemoteVoters)); j++ { +// row[j].SetInt(new(big.Int).Exp(voter.input, big.NewInt(j), nil)) +// } +// row[j].SetInt(voter.sum) +// i++ +// } + +// // row for ourselves +// mat[i] = make([]big.Rat, len(mat) + 1) +// row := mat[i] +// row[0].SetInt64(1) +// localVoter := election.localVoter +// var j int64 +// for j = 1; j <= int64(len(election.RemoteVoters)); j++ { +// row[j].SetInt(new(big.Int).Exp(localVoter.input, big.NewInt(j), nil)) +// } +// row[j].SetInt(localVoter.sum) + +// return mat +// } + +func printResults(result []byte, candidates []Candidate) { + log.Infof("result: %v", result) + fmt.Println("=== Results ===") + n := len(candidates) + for i, cand := range candidates { + for j, vs := range candidates { + if i != j { + fmt.Printf("%s over %s: %d\n", cand, vs, result[i * n + j]) + } + } + } +} diff --git a/go.mod b/go.mod index c4dee1b..052c7e4 100644 --- a/go.mod +++ b/go.mod @@ -4,16 +4,14 @@ go 1.13 require ( github.com/cbergoon/merkletree v0.2.0 - github.com/ipfs/go-log v1.0.2 + github.com/gdamore/tcell v1.3.0 + github.com/kr/pretty v0.1.0 // indirect github.com/kyoh86/xdg v1.2.0 - github.com/libp2p/go-libp2p v0.6.1 - github.com/libp2p/go-libp2p-core v0.5.0 - github.com/libp2p/go-libp2p-discovery v0.2.0 - github.com/libp2p/go-libp2p-kad-dht v0.5.0 github.com/mr-tron/base58 v1.2.0 - github.com/multiformats/go-multiaddr v0.2.1 github.com/rivo/tview v0.0.0-20200528200248-fe953220389f github.com/sirupsen/logrus v1.2.0 + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect maunium.net/go/mautrix v0.7.13 ) diff --git a/go.sum b/go.sum index e81a493..33a2439 100644 --- a/go.sum +++ b/go.sum @@ -1,152 +1,18 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= -github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= -github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= -github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= -github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018 h1:6xT9KW8zLC5IlbaIF5Q7JNieBoACT7iW0YTxQHR0in0= -github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4= -github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= -github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/tcell v1.3.0 h1:r35w0JBADPZCVQijYebl6YMWWtHRqVEGt7kL2eBADRM= github.com/gdamore/tcell v1.3.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM= -github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= -github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= -github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= -github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= -github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= -github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= -github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M= -github.com/ipfs/go-cid v0.0.5 h1:o0Ix8e/ql7Zb5UVUJEUfjsWCIY8t48++9lR8qi6oiJU= -github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog= -github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= -github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= -github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= -github.com/ipfs/go-datastore v0.3.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= -github.com/ipfs/go-datastore v0.4.0/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-datastore v0.4.4 h1:rjvQ9+muFaJ+QZ7dN5B1MSDNQ0JVZKkkES/rMZmA8X8= -github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= -github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= -github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8= -github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaHzfGTzuE3s= -github.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk= -github.com/ipfs/go-ds-badger v0.2.1/go.mod h1:Tx7l3aTph3FMFrRS838dcSJh+jjA7cX9DrGVwx/NOwE= -github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc= -github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8= -github.com/ipfs/go-ds-leveldb v0.4.1/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= -github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= -github.com/ipfs/go-ipfs-util v0.0.1 h1:Wz9bL2wB2YBJqggkA4dD7oSmqB4cAnpNbGrlHJulv50= -github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= -github.com/ipfs/go-log v0.0.1 h1:9XTUN/rW64BCG1YhPK9Hoy3q8nr4gOmHHBpgFdfw6Lc= -github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= -github.com/ipfs/go-log v1.0.2 h1:s19ZwJxH8rPWzypjcDpqPLIyV7BnbLqvpli3iZoqYK0= -github.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk= -github.com/ipfs/go-log/v2 v2.0.2 h1:xguurydRdfKMJjKyxNXNU8lYP0VZH1NUwJRwUorjuEw= -github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= -github.com/ipfs/go-todocounter v0.0.2 h1:9UBngSQhylg2UDcxSAtpkT+rEWFr26hDPXVStE8LFyc= -github.com/ipfs/go-todocounter v0.0.2/go.mod h1:l5aErvQc8qKE2r7NDMjmq5UNAvuZy0rC8BHOplkWvZ4= -github.com/jackpal/gateway v1.0.5 h1:qzXWUJfuMdlLMtt0a3Dgt+xkWQiA5itDEITVJtuSwMc= -github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= -github.com/jackpal/go-nat-pmp v1.0.1 h1:i0LektDkO1QlrTm/cSuP+PyBCDnYvjPLGl4LdWEMiaA= -github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs= -github.com/jbenet/go-cienv v0.1.0 h1:Vc/s0QbQtoxX8MwwSLWWh+xNNZvM3Lw7NsTcHrvvhMc= -github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= -github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2 h1:vhC1OXXiT9R2pczegwz6moDvuRpggaroAXhPIseh57A= -github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs= -github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= -github.com/jbenet/goprocess v0.1.3 h1:YKyIEECS/XvcfHtBzxtjBBbWK+MbvA6dG8ASiqwvr10= -github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d h1:68u9r4wEvL3gYg2jvAOgROwZ3H+Y3hIDk4tbbmIjcYQ= -github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -156,256 +22,31 @@ github.com/kyoh86/xdg v1.2.0 h1:CERuT/ShdTDj+A2UaX3hQ3mOV369+Sj+wyn2nIRIIkI= github.com/kyoh86/xdg v1.2.0/go.mod h1:/mg8zwu1+qe76oTFUBnyS7rJzk7LLC0VGEzJyJ19DHs= github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libp2p/go-addr-util v0.0.1 h1:TpTQm9cXVRVSKsYbgQ7GKc3KbbHVTnbostgGaDEP+88= -github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= -github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= -github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= -github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= -github.com/libp2p/go-conn-security-multistream v0.1.0 h1:aqGmto+ttL/uJgX0JtQI0tD21CIEy5eYd1Hlp0juHY0= -github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc= -github.com/libp2p/go-eventbus v0.1.0 h1:mlawomSAjjkk97QnYiEmHsLu7E136+2oCWSHRUvMfzQ= -github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4= -github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= -github.com/libp2p/go-flow-metrics v0.0.2/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= -github.com/libp2p/go-flow-metrics v0.0.3 h1:8tAs/hSdNvUiLgtlSy3mxwxWP4I9y/jlkPFT7epKdeM= -github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= -github.com/libp2p/go-libp2p v0.5.0/go.mod h1:Os7a5Z3B+ErF4v7zgIJ7nBHNu2LYt8ZMLkTQUB3G/wA= -github.com/libp2p/go-libp2p v0.6.1 h1:mxabyJf4l6AmotDOKObwSfBNBWjL5VYXysVFLUMAuB8= -github.com/libp2p/go-libp2p v0.6.1/go.mod h1:CTFnWXogryAHjXAKEbOf1OWY+VeAP3lDMZkfEI5sT54= -github.com/libp2p/go-libp2p-autonat v0.1.1 h1:WLBZcIRsjZlWdAZj9CiBSvU2wQXoUOiS1Zk1tM7DTJI= -github.com/libp2p/go-libp2p-autonat v0.1.1/go.mod h1:OXqkeGOY2xJVWKAGV2inNF5aKN/djNA3fdpCWloIudE= -github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro= -github.com/libp2p/go-libp2p-blankhost v0.1.4 h1:I96SWjR4rK9irDHcHq3XHN6hawCRTPUADzkJacgZLvk= -github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU= -github.com/libp2p/go-libp2p-circuit v0.1.4 h1:Phzbmrg3BkVzbqd4ZZ149JxCuUWu2wZcXf/Kr6hZJj8= -github.com/libp2p/go-libp2p-circuit v0.1.4/go.mod h1:CY67BrEjKNDhdTk8UgBX1Y/H5c3xkAcs3gnksxY7osU= -github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= -github.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I= -github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI= -github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0= -github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= -github.com/libp2p/go-libp2p-core v0.2.5/go.mod h1:6+5zJmKhsf7yHn1RbmYDu08qDUpIUxGdqHuEZckmZOA= -github.com/libp2p/go-libp2p-core v0.3.0 h1:F7PqduvrztDtFsAa/bcheQ3azmNo+Nq7m8hQY5GiUW8= -github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw= -github.com/libp2p/go-libp2p-core v0.3.1/go.mod h1:thvWy0hvaSBhnVBaW37BvzgVV68OUhgJJLAa6almrII= -github.com/libp2p/go-libp2p-core v0.4.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= -github.com/libp2p/go-libp2p-core v0.5.0 h1:FBQ1fpq2Fo/ClyjojVJ5AKXlKhvNc/B6U0O+7AN1ffE= -github.com/libp2p/go-libp2p-core v0.5.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= -github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= -github.com/libp2p/go-libp2p-discovery v0.2.0 h1:1p3YSOq7VsgaL+xVHPi8XAmtGyas6D2J6rWBEfz/aiY= -github.com/libp2p/go-libp2p-discovery v0.2.0/go.mod h1:s4VGaxYMbw4+4+tsoQTqh7wfxg97AEdo4GYBt6BadWg= -github.com/libp2p/go-libp2p-kad-dht v0.5.0 h1:kDMtCftpQOL2s84/dZmw5z4NmBe6ByeDLKpcn6TcyxU= -github.com/libp2p/go-libp2p-kad-dht v0.5.0/go.mod h1:42YDfiKXzIgaIexiEQ3rKZbVPVPziLOyHpXbOCVd814= -github.com/libp2p/go-libp2p-kbucket v0.2.3 h1:XtNfN4WUy0cfeJoJgWCf1lor4Pp3kBkFJ9vQ+Zs+VUM= -github.com/libp2p/go-libp2p-kbucket v0.2.3/go.mod h1:opWrBZSWnBYPc315q497huxY3sz1t488X6OiXUEYWKA= -github.com/libp2p/go-libp2p-loggables v0.1.0 h1:h3w8QFfCt2UJl/0/NW4K829HX/0S4KD31PQ7m8UXXO8= -github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90= -github.com/libp2p/go-libp2p-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3g+OtR+EMMODbKo= -github.com/libp2p/go-libp2p-mplex v0.2.1 h1:E1xaJBQnbSiTHGI1gaBKmKhu1TUKkErKJnE8iGvirYI= -github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE= -github.com/libp2p/go-libp2p-mplex v0.2.2 h1:+Ld7YDAfVERQ0E+qqjE7o6fHwKuM0SqTzYiwN1lVVSA= -github.com/libp2p/go-libp2p-mplex v0.2.2/go.mod h1:74S9eum0tVQdAfFiKxAyKzNdSuLqw5oadDq7+L/FELo= -github.com/libp2p/go-libp2p-nat v0.0.5 h1:/mH8pXFVKleflDL1YwqMg27W9GD8kjEx7NY0P6eGc98= -github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE= -github.com/libp2p/go-libp2p-netutil v0.1.0 h1:zscYDNVEcGxyUpMd0JReUZTrpMfia8PmLKcKF72EAMQ= -github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU= -github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY= -github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY= -github.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI= -github.com/libp2p/go-libp2p-peerstore v0.1.4 h1:d23fvq5oYMJ/lkkbO4oTwBp/JP+I/1m5gZJobNXCE/k= -github.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs= -github.com/libp2p/go-libp2p-peerstore v0.2.0 h1:XcgJhI8WyUOCbHyRLNEX5542YNj8hnLSJ2G1InRjDhk= -github.com/libp2p/go-libp2p-peerstore v0.2.0/go.mod h1:N2l3eVIeAitSg3Pi2ipSrJYnqhVnMNQZo9nkSCuAbnQ= -github.com/libp2p/go-libp2p-pnet v0.2.0 h1:J6htxttBipJujEjz1y0a5+eYoiPcFHhSYHH6na5f0/k= -github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= -github.com/libp2p/go-libp2p-record v0.1.2 h1:M50VKzWnmUrk/M5/Dz99qO9Xh4vs8ijsK+7HkJvRP+0= -github.com/libp2p/go-libp2p-record v0.1.2/go.mod h1:pal0eNcT5nqZaTV7UGhqeGqxFgGdsU/9W//C8dqjQDk= -github.com/libp2p/go-libp2p-routing v0.1.0 h1:hFnj3WR3E2tOcKaGpyzfP4gvFZ3t8JkQmbapN0Ct+oU= -github.com/libp2p/go-libp2p-routing v0.1.0/go.mod h1:zfLhI1RI8RLEzmEaaPwzonRvXeeSHddONWkcTcB54nE= -github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8= -github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g= -github.com/libp2p/go-libp2p-secio v0.2.1 h1:eNWbJTdyPA7NxhP7J3c5lT97DC5d+u+IldkgCYFTPVA= -github.com/libp2p/go-libp2p-secio v0.2.1/go.mod h1:cWtZpILJqkqrSkiYcDBh5lA3wbT2Q+hz3rJQq3iftD8= -github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4= -github.com/libp2p/go-libp2p-swarm v0.2.2 h1:T4hUpgEs2r371PweU3DuH7EOmBIdTBCwWs+FLcgx3bQ= -github.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaTNyBcHImCxRpPKU= -github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= -github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= -github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= -github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= -github.com/libp2p/go-libp2p-testing v0.1.1 h1:U03z3HnGI7Ni8Xx6ONVZvUFOAzWYmolWf5W5jAOPNmU= -github.com/libp2p/go-libp2p-testing v0.1.1/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= -github.com/libp2p/go-libp2p-transport-upgrader v0.1.1 h1:PZMS9lhjK9VytzMCW3tWHAXtKXmlURSc3ZdvwEcKCzw= -github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA= -github.com/libp2p/go-libp2p-transport-upgrader v0.2.0 h1:5EhPgQhXZNyfL22ERZTUoVp9UVVbNowWNVtELQaKCHk= -github.com/libp2p/go-libp2p-transport-upgrader v0.2.0/go.mod h1:mQcrHj4asu6ArfSoMuyojOdjx73Q47cYD7s5+gZOlns= -github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8= -github.com/libp2p/go-libp2p-yamux v0.2.1 h1:Q3XYNiKCC2vIxrvUJL+Jg1kiyeEaIDNKLjgEjo3VQdI= -github.com/libp2p/go-libp2p-yamux v0.2.1/go.mod h1:1FBXiHDk1VyRM1C0aez2bCfHQ4vMZKkAQzZbkSQt5fI= -github.com/libp2p/go-libp2p-yamux v0.2.5 h1:MuyItOqz03oi8npvjgMJxgnhllJLZnO/dKVOpTZ9+XI= -github.com/libp2p/go-libp2p-yamux v0.2.5/go.mod h1:Zpgj6arbyQrmZ3wxSZxfBmbdnWtbZ48OpsfmQVTErwA= -github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= -github.com/libp2p/go-maddr-filter v0.0.5 h1:CW3AgbMO6vUvT4kf87y4N+0P8KUl2aqLYhrGyDUbLSg= -github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M= -github.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0= -github.com/libp2p/go-mplex v0.1.0 h1:/nBTy5+1yRyY82YaO6HXQRnO5IAGsXTjEJaR3LdTPc0= -github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU= -github.com/libp2p/go-mplex v0.1.1 h1:huPH/GGRJzmsHR9IZJJsrSwIM5YE2gL4ssgl1YWb/ps= -github.com/libp2p/go-mplex v0.1.1/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= -github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-msgio v0.0.4 h1:agEFehY3zWJFUHK6SEMR7UYmk2z6kC3oeCM7ybLhguA= -github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-nat v0.0.4 h1:KbizNnq8YIf7+Hn7+VFL/xE0eDrkPru2zIO9NMwL8UQ= -github.com/libp2p/go-nat v0.0.4/go.mod h1:Nmw50VAvKuk38jUBcmNh6p9lUJLoODbJRvYAa/+KSDo= -github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0= -github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.0.4 h1:d27YZvLoTyMhIN4njrkr8zMDOM4lfpHIp6A+TK9fovg= -github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-reuseport v0.0.1 h1:7PhkfH73VXfPJYKQ6JwS5I/eVcoyYi9IMNGc6FWpFLw= -github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= -github.com/libp2p/go-reuseport-transport v0.0.2 h1:WglMwyXyBu61CMkjCCtnmqNqnjib0GIEjMiHTwR/KN4= -github.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs= -github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14= -github.com/libp2p/go-stream-muxer-multistream v0.2.0 h1:714bRJ4Zy9mdhyTLJ+ZKiROmAFwUHpeRidG+q7LTQOg= -github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc= -github.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc= -github.com/libp2p/go-tcp-transport v0.1.1 h1:yGlqURmqgNA2fvzjSgZNlHcsd/IulAnKM8Ncu+vlqnw= -github.com/libp2p/go-tcp-transport v0.1.1/go.mod h1:3HzGvLbx6etZjnFlERyakbaYPdfjg2pWP97dFZworkY= -github.com/libp2p/go-ws-transport v0.2.0 h1:MJCw2OrPA9+76YNRvdo1wMnSOxb9Bivj6sVFY1Xrj6w= -github.com/libp2p/go-ws-transport v0.2.0/go.mod h1:9BHJz/4Q5A9ludYWKoGCFC5gUElzlHoKzu0yY9p/klM= -github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.2.3 h1:xX8A36vpXb59frIzWFdEgptLMsOANMFq2K7fPRlunYI= -github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.3.3 h1:mWuzZRCAeTBFdynLlsYgA/EIeMOLr8XY04wa52NRhsE= -github.com/libp2p/go-yamux v1.3.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s= github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac= github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0= github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g= -github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= -github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= -github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= -github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= -github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= -github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= -github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= -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= -github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= -github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= -github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= -github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= -github.com/multiformats/go-multiaddr v0.2.0 h1:lR52sFwcTCuQb6bTfnXF6zA2XfyYvyd+5a9qECv/J90= -github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= -github.com/multiformats/go-multiaddr v0.2.1 h1:SgG/cw5vqyB5QQe5FPe2TqggU9WtrA9X4nZw7LlVqOI= -github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE= -github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= -github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= -github.com/multiformats/go-multiaddr-dns v0.2.0 h1:YWJoIDwLePniH7OU5hBnDZV6SWuvJqJ0YtN6pLeH9zA= -github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= -github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= -github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= -github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= -github.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU= -github.com/multiformats/go-multiaddr-net v0.1.0/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= -github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= -github.com/multiformats/go-multiaddr-net v0.1.2 h1:P7zcBH9FRETdPkDrylcXVjQLQ2t1JQtNItZULWNWgeg= -github.com/multiformats/go-multiaddr-net v0.1.2/go.mod h1:QsWt3XK/3hwvNxZJp92iMQKME1qHfpYmyIjFVsSOY6Y= -github.com/multiformats/go-multiaddr-net v0.1.3 h1:q/IYAvoPKuRzGeERn3uacWgm0LIWkLZBAvO5DxSzq3g= -github.com/multiformats/go-multiaddr-net v0.1.3/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= -github.com/multiformats/go-multibase v0.0.1 h1:PN9/v21eLywrFWdFNsFKaU04kLJzuYzmrJR+ubhT9qA= -github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= -github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= -github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= -github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= -github.com/multiformats/go-multihash v0.0.9/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= -github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= -github.com/multiformats/go-multihash v0.0.13 h1:06x+mk/zj1FoMsgNejLpy6QTvJqlSt/BhLEy87zidlc= -github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= -github.com/multiformats/go-multistream v0.1.1 h1:JlAdpIFhBhGRLxe9W6Om0w++Gd6KMWoFPZL/dEnm9nI= -github.com/multiformats/go-multistream v0.1.1/go.mod h1:KmHZ40hzVxiaiwlj3MEbYgK9JFk2/9UktWZAF54Du38= -github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.5 h1:XVZwSo04Cs3j/jS0uAEPpT3JY6DzMcVLLoWOSnCxOjg= -github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/tview v0.0.0-20200528200248-fe953220389f h1:tRx/LLIP2PSA7johw9xhf+6NUCLC4BbMhpGdm110MGI= github.com/rivo/tview v0.0.0-20200528200248-fe953220389f/go.mod h1:6lkG1x+13OShEf0EaOCaTQYyB7d5nSbb181KtjlS+84= github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= -github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= -github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= -github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= -github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc= github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= @@ -415,92 +56,24 @@ github.com/tidwall/pretty v1.0.1 h1:WE4RBSZ1x6McVVC8S/Md+Qse8YUv6HRObAx6ke00NY8= github.com/tidwall/pretty v1.0.1/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/sjson v1.1.1 h1:7h1vk049Jnd5EH9NyzNiEuwYW4b5qgreBbqRC19AS3U= github.com/tidwall/sjson v1.1.1/go.mod h1:yvVuSnpEQv5cYIrO+AT6kw4QVfd5SDZoGIS7/5+fZFs= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= -github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= -github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= -github.com/whyrusleeping/go-logging v0.0.1 h1:fwpzlmT0kRC/Fmd0MdmGgJG/CXIZ6gFq46FQZjprUcc= -github.com/whyrusleeping/go-logging v0.0.1/go.mod h1:lDPYj54zutzG1XYfHAhcc7oNXEburHQBn+Iqd4yS4vE= -github.com/whyrusleeping/mafmt v1.2.8 h1:TCghSl5kkwEE0j+sU/gudyhVMRlpBin8fMBBHg59EbA= -github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= -github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= -github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds= -github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= -github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= -go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw= -golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69 h1:rOhMmluY6kLMhdnrivzec6lLgaVbMHMn2ISQXJeJ5EM= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= @@ -510,43 +83,14 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8= -gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= maunium.net/go/maulogger/v2 v2.1.1/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A= maunium.net/go/mautrix v0.7.13 h1:qfnvLxvQafvLgHbdZF/+9qs9gyArYf8fUnzfQbjgQaU= maunium.net/go/mautrix v0.7.13/go.mod h1:Jn0ijwXwMFvJFIN9IljirIVKpZQbZP/Dk7pdX2qDmXk= diff --git a/main.go b/main.go deleted file mode 100644 index 9f80cdc..0000000 --- a/main.go +++ /dev/null @@ -1,224 +0,0 @@ -package main - -import ( - "bufio" - "context" - "flag" - "fmt" - "io" - "os" - "sync" - - "github.com/ipfs/go-log" - "github.com/libp2p/go-libp2p" - "github.com/libp2p/go-libp2p-core/host" - "github.com/libp2p/go-libp2p-core/network" - "github.com/libp2p/go-libp2p-core/peer" - "github.com/libp2p/go-libp2p-core/protocol" - "github.com/libp2p/go-libp2p-core/routing" - dht "github.com/libp2p/go-libp2p-kad-dht" -) - -const protocolID = protocol.ID("/tallyard/0.0.0") - -var logger = log.Logger("tallyard") - -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 - localVoter.kdht, err = dht.New(localVoter.ctx, h) - if err != nil { - return localVoter.kdht, err - } - logger.Info("boostrapping the DHT") - if err = localVoter.kdht.Bootstrap(localVoter.ctx); err != nil { - panic(err) - } - return localVoter.kdht, err - }) - - var err error - localVoter.Host, err = libp2p.New(localVoter.ctx, append(hostOpts, routing)...) - if err != nil { - panic(err) - } - - logger.Info("host: ", localVoter.ID()) - logger.Info("addrs: ", localVoter.Addrs()) - - return localVoter -} - -func (localVoter *LocalVoter) Bootstrap() { - var wg sync.WaitGroup - for _, peerAddr := range dht.DefaultBootstrapPeers { - peerInfo, _ := peer.AddrInfoFromP2pAddr(peerAddr) - wg.Add(1) - go func() { - defer wg.Done() - if err := localVoter.Connect(localVoter.ctx, *peerInfo); err != nil { - logger.Warning(err) - } else { - logger.Info("connection established with bootstrap node:", *peerInfo) - } - }() - } - wg.Wait() -} - -func (election *Election) SetupMaster() { - fmt.Println("share this with peers:") - fmt.Printf("%s\n", election.electionKey) - - logger.Info("waiting for incoming streams and finding voters...") - - election.Lock() - ch := make(chan int, 1) - election.close = ch - go findPeers(ch, election) - election.Unlock() - - localVoter := election.localVoter - localVoter.SetStreamHandler(protocolID, func(stream network.Stream) { - streamHandler(stream, election) - }) - - fmt.Println("press ENTER to solidify group of voters and start voting") - _, err := bufio.NewReader(os.Stdin).ReadString('\n') - if err != nil { - panic(err) - } - - logger.Info("ENTER has been pressed; closing election") - election.Lock() - n := len(election.remoteVoters) - election.close <- n - close(election.close) - election.Unlock() - election.RLock() - for _, voter := range election.remoteVoters { - stream, err := localVoter.NewStream(localVoter.ctx, voter.addrInfo.ID, protocolID) - if err != nil { - panic(err) - } - writer := bufio.NewWriter(stream) - writer.WriteString(fmt.Sprintf("close\n%d", n)) - writer.Flush() - stream.Close() - } - election.RUnlock() -} - -func (election *Election) SetupSlave() { - localVoter := election.localVoter - - // have candidates and nonce been loaded from elections file? - // if not, fetch them from master - if len(election.Candidates) == 0 || election.rendezvousNonce == "" { - logger.Info("attempting to open stream with master peer...") - stream, err := localVoter.NewStream(localVoter.ctx, election.masterID, protocolID) - rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream)) - if err != nil { - panic(err) - } - logger.Info("opened stream with master peer") - - logger.Info("fetching election info from master") - _, err = rw.WriteString("info") - if err != nil { - panic(err) - } - rw.Flush() - stream.Close() // only stops writing - // first field is the rendezvous string, which is used for peer - // discovery - str, err := rw.ReadString('\n') - if err != nil && err != io.EOF { - panic(err) - } - str = stripNewline(str) - election.rendezvousNonce = Nonce(str) - // remaining fields are the candidates - for { - str, err := rw.ReadString('\n') - if err != nil && err != io.EOF { - panic(err) - } - str = stripNewline(str) - if str != "" { - election.Candidates = append(election.Candidates, Candidate(str)) - } - if err == io.EOF { - break - } - } - logger.Info("done fetching election info") - } - - logger.Info("checking authenticity of election info...") - 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 - localVoter.SetStreamHandler(protocolID, func(stream network.Stream) { - streamHandler(stream, election) - }) - - findPeers(ch, election) -} - -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 and the candidates are given via positional arguments") - flag.Parse() - if *verbose { - log.SetLogLevel("tallyard", "info") - } else { - log.SetLogLevel("dht", "critical") - log.SetLogLevel("relay", "critical") - log.SetLogLevel("tallyard", "critical") - } - - var election *Election - - if *master { - // we are the master and the candidates are the positional - // arguments - candidates := []Candidate{} - for _, candidate := range flag.Args() { - candidates = append( - candidates, - Candidate(candidate)) - } - // TODO: ensure at least 2 candidates - election = NewElectionWithCandidates(candidates) - } else if electionKey := flag.Arg(0); electionKey != "" { - // we are a slave the and election key was given via CLI args - election = NewElectionWithElectionKey(electionKey) - } else { - // create/join election via TUI - election = tui() - if election == nil { - // tui form wasn't submitted (maybe the user hit ^C) - os.Exit(0) - } - } - - election.localVoter.Bootstrap() - - if election.masterID == election.localVoter.ID() { - election.SetupMaster() - } else { - election.SetupSlave() - } - - election.StartVoting() -} diff --git a/linalg.go b/math/linalg.go similarity index 98% rename from linalg.go rename to math/linalg.go index 42c7355..9ed2b17 100644 --- a/linalg.go +++ b/math/linalg.go @@ -1,4 +1,4 @@ -package main +package math import ( "math/big" diff --git a/linalg_test.go b/math/linalg_test.go similarity index 98% rename from linalg_test.go rename to math/linalg_test.go index d0e9655..54d7e28 100644 --- a/linalg_test.go +++ b/math/linalg_test.go @@ -1,4 +1,4 @@ -package main +package math import ( "math/big" diff --git a/poly.go b/math/poly.go similarity index 99% rename from poly.go rename to math/poly.go index d1647ad..22e995f 100644 --- a/poly.go +++ b/math/poly.go @@ -1,4 +1,4 @@ -package main +package math import ( "crypto/rand" diff --git a/matrix/crypto_logger.go b/matrix/crypto_logger.go index 3b80c7e..6d9db04 100644 --- a/matrix/crypto_logger.go +++ b/matrix/crypto_logger.go @@ -1,4 +1,4 @@ -package main +package matrix import ( log "github.com/sirupsen/logrus" diff --git a/matrix/data.go b/matrix/data.go index a50a8de..6e396ce 100644 --- a/matrix/data.go +++ b/matrix/data.go @@ -1,4 +1,4 @@ -package main +package matrix import ( "bufio" diff --git a/matrix/main.go b/matrix/main.go deleted file mode 100644 index edfc12f..0000000 --- a/matrix/main.go +++ /dev/null @@ -1,139 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/kyoh86/xdg" - log "github.com/sirupsen/logrus" - "maunium.net/go/mautrix" - "maunium.net/go/mautrix/crypto" - "maunium.net/go/mautrix/event" -) - -type MockSharedStore struct{} - -var StartMessage = event.Type{"xyz.tallyard.start", event.MessageEventType} -var client *mautrix.Client -var olmMachine *crypto.OlmMachine -var gobStorePath string = xdg.DataHome() + "/tallyard/gob.dat" - -func debugCB(source mautrix.EventSource, evt *event.Event) { - fmt.Printf("%[5]d: <%[1]s> %[4]s (%[2]s/%[3]s)\n", evt.Sender, evt.Type.String(), evt.ID, evt.Content.AsMessage().Body, source) -} - -func joinRoomCB(source mautrix.EventSource, evt *event.Event) { - debugCB(source, evt) - - memberEvtContent := evt.Content.AsMember() - if memberEvtContent.Membership != event.MembershipInvite { - return - } - - _, err := client.JoinRoomByID(evt.RoomID) - if err != nil { - panic(err) - } -} - -func encryptedCB(source mautrix.EventSource, evt *event.Event) { - debugCB(source, evt) - - encryptedEvtContent := evt.Content.AsEncrypted() - fmt.Println(encryptedEvtContent) - - decrypted, err := olmMachine.DecryptMegolmEvent(evt) - if err != nil { - log.WithError(err).Warn("couldn't decrypt melogm event") - return - } - debugCB(source, decrypted) -} - -func main() { - data, err := GetData() - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - fmt.Println(data) - - client, err = mautrix.NewClient(data.Homeserver, data.UserID, data.AccessToken) - if err != nil { - panic(err) - } - - syncer := client.Syncer.(*mautrix.DefaultSyncer) - syncer.OnEventType(event.StateAliases, debugCB) - syncer.OnEventType(event.StateCanonicalAlias, debugCB) - syncer.OnEventType(event.StateCreate, debugCB) - syncer.OnEventType(event.StateJoinRules, debugCB) - syncer.OnEventType(event.StateHistoryVisibility, debugCB) - syncer.OnEventType(event.StateGuestAccess, debugCB) - syncer.OnEventType(event.StateMember, joinRoomCB) - syncer.OnEventType(event.StatePowerLevels, debugCB) - syncer.OnEventType(event.StateRoomName, debugCB) - syncer.OnEventType(event.StateTopic, debugCB) - syncer.OnEventType(event.StateRoomAvatar, debugCB) - syncer.OnEventType(event.StatePinnedEvents, debugCB) - syncer.OnEventType(event.StateTombstone, debugCB) - syncer.OnEventType(event.StateEncryption, debugCB) - syncer.OnEventType(event.EventRedaction, debugCB) - syncer.OnEventType(event.EventMessage, debugCB) - syncer.OnEventType(event.EventEncrypted, encryptedCB) - syncer.OnEventType(event.EventReaction, debugCB) - syncer.OnEventType(event.EventSticker, debugCB) - syncer.OnEventType(event.EphemeralEventReceipt, debugCB) - syncer.OnEventType(event.EphemeralEventTyping, debugCB) - syncer.OnEventType(event.EphemeralEventPresence, debugCB) - syncer.OnEventType(event.AccountDataDirectChats, debugCB) - syncer.OnEventType(event.AccountDataPushRules, debugCB) - syncer.OnEventType(event.AccountDataRoomTags, debugCB) - syncer.OnEventType(event.AccountDataFullyRead, debugCB) - syncer.OnEventType(event.AccountDataIgnoredUserList, debugCB) - syncer.OnEventType(event.ToDeviceRoomKey, debugCB) - syncer.OnEventType(event.ToDeviceRoomKeyRequest, debugCB) - syncer.OnEventType(event.ToDeviceForwardedRoomKey, debugCB) - syncer.OnEventType(event.ToDeviceEncrypted, debugCB) - syncer.OnEventType(event.ToDeviceRoomKeyWithheld, debugCB) - syncer.OnEventType(event.ToDeviceVerificationRequest, debugCB) - syncer.OnEventType(event.ToDeviceVerificationStart, debugCB) - syncer.OnEventType(event.ToDeviceVerificationAccept, debugCB) - syncer.OnEventType(event.ToDeviceVerificationKey, debugCB) - syncer.OnEventType(event.ToDeviceVerificationMAC, debugCB) - syncer.OnEventType(event.ToDeviceVerificationCancel, debugCB) - syncer.OnEventType(event.ToDeviceOrgMatrixRoomKeyWithheld, debugCB) - - resp, err := client.JoinedRooms() - fmt.Println("Joined rooms:") - for _, joinedRoom := range resp.JoinedRooms { - fmt.Println(joinedRoom) - } - - gobStore, err := crypto.NewGobStore(gobStorePath) - if err != nil { - panic(err) - } - stateStore := &TallyardStateStore{mautrix.NewInMemoryStore()} - logger := CryptoMachineLogger{} - olmMachine = crypto.NewOlmMachine(client, logger, gobStore, stateStore) - if err = olmMachine.Load(); err != nil { - panic(err) - } - syncer.OnEventType(event.StateMember, func(_ mautrix.EventSource, evt *event.Event) { - olmMachine.HandleMemberEvent(evt) - }) - syncer.OnSync(func(resp *mautrix.RespSync, since string) bool { - stateStore.UpdateStateStore(resp) - olmMachine.ProcessSyncResponse(resp, since) - if err := olmMachine.CryptoStore.Flush(); err != nil { - log.WithError(err).Error("Could not flush crypto store") - } - return true - }); - - err = client.Sync() - if err != nil { - panic(err) - } -} diff --git a/matrix/state_store.go b/matrix/state_store.go index 9c853ca..29c7485 100644 --- a/matrix/state_store.go +++ b/matrix/state_store.go @@ -1,4 +1,4 @@ -package main +package matrix import ( "errors" diff --git a/ui.go b/ui.go deleted file mode 100644 index 57e0c2a..0000000 --- a/ui.go +++ /dev/null @@ -1,188 +0,0 @@ -package main - -import ( - "fmt" - "os" - "sort" - "strconv" - "unicode" - - "github.com/rivo/tview" -) - -func tui() (election *Election) { - app := tview.NewApplication() - done := func(buttonIndex int, buttonLabel string) { - app.Stop() - switch buttonLabel { - case "Create Election": - candidates := getCandidatesTUI() - // TODO: slaves should check that len candidates >= 2 - if len(candidates) == 0 { - fmt.Printf("no candidates entered; exiting\n") - os.Exit(0) - } - election = NewElectionWithCandidates(candidates) - case "Join Election": - electionKey := getElectionKeyTUI() - if electionKey == "" { - fmt.Printf("no election key given; exiting\n") - os.Exit(0) - } - election = NewElectionWithElectionKey(electionKey) - } - } - modal := tview.NewModal(). - SetText("Welcome to tallyard!"). - AddButtons([]string{"Create Election", "Join Election"}). - SetDoneFunc(done) - if err := app.SetRoot(modal, false).EnableMouse(true).Run(); err != nil { - panic(err) - } - return election -} - -func getCandidatesTUI() (candidates []Candidate) { - var form *tview.Form - n := 3 - app := tview.NewApplication() - plus := func() { - form.AddInputField(fmt.Sprintf("%d.", n), "", 50, nil, nil) - n++ - } - minus := func() { - // TODO: ensure from joiner that there are at least two - // candidates - if n > 3 { - form.RemoveFormItem(n - 2) - n-- - } - } - done := func() { - // TODO: ensure none of the candidates are empty - app.Stop() - for i := 0; i < n-1; i++ { - candidates = append(candidates, - Candidate(form.GetFormItem(i).(*tview.InputField).GetText())) - } - } - form = tview.NewForm(). - AddInputField("1.", "", 50, nil, nil). - AddInputField("2.", "", 50, nil, nil). - AddButton("+", plus). - AddButton("-", minus). - AddButton("Done", done) - if err := app.SetRoot(form, true).EnableMouse(true).Run(); err != nil { - panic(err) - } - return candidates -} - -func getElectionKeyTUI() (electionKey string) { - app := tview.NewApplication() - var form *tview.Form - done := func() { - app.Stop() - electionKey = form.GetFormItem(0).(*tview.InputField).GetText() - } - form = tview.NewForm(). - AddInputField("Election key:", "", 100, nil, nil). - AddButton("Continue", done) - if err := app.SetRoot(form, true).EnableMouse(true).Run(); err != nil { - panic(err) - } - return electionKey -} - -// displays a voting UI to the user and returns the encoded ballot -func vote(candidates []Candidate) []byte { - ranks := make([]int, len(candidates)) - app := tview.NewApplication() - form := tview.NewForm() - - for _, eo := range candidates { - // TODO: support more than 99 candidates - form.AddInputField(string(eo), "", 2, - func(textToCheck string, lastChar rune) bool { - return len(textToCheck) < 3 && unicode.IsDigit(lastChar) - }, nil) - } - - form.AddButton("Submit", func() { - app.Stop() - for i := 0; i < len(candidates); i++ { - rank, err := strconv.Atoi(form.GetFormItem(i).(*tview.InputField).GetText()) - if err != nil { - panic(err) - } - ranks[i] = rank - } - }) - - if err := app.SetRoot(form, true).EnableMouse(true).Run(); err != nil { - panic(err) - } - - return GetBallotFromRankings(ranks) -} - -func GetBallotFromRankings(ranks []int) []byte { - n := len(ranks) - candidates := make([]int, n) - - for i := 0; i < n; i++ { - candidates[i] = i - } - - // sort candidates by ranking - cr := CandidateRanking{candidates, ranks} - sort.Sort(&cr) - - // TODO: support more than 255 voters (limit from usage of byte) - prefMatrix := make([][]byte, n) - - for len(candidates) > 0 { - r := ranks[candidates[0]] - i := 1 - for i < len(candidates) && ranks[candidates[i]] == r { - i++ - } - // i is now index of the first candidate with worse (i.e. higher - // in value) rank - row := make([]byte, n) - for j := i; j < len(candidates); j++ { - row[candidates[j]] = 1 - } - for j := 0; j < i; j++ { - prefMatrix[candidates[j]] = row - } - candidates = candidates[i:] - } - - // convert 2D array into 1D array - barray := make([]byte, 0, n*n) - for _, row := range prefMatrix { - barray = append(barray, row...) - } - - return barray -} - -type CandidateRanking struct { - candidates []int // becomes list of candidate IDs sorted by rank - ranks []int // maps candidate ID to rank -} - -func (cr *CandidateRanking) Len() int { - return len(cr.ranks) -} - -func (cr *CandidateRanking) Less(i, j int) bool { - return cr.ranks[cr.candidates[i]] < cr.ranks[cr.candidates[j]] -} - -func (cr *CandidateRanking) Swap(i, j int) { - tmp := cr.candidates[i] - cr.candidates[i] = cr.candidates[j] - cr.candidates[j] = tmp -} diff --git a/ui/tui.go b/ui/tui.go new file mode 100644 index 0000000..5780638 --- /dev/null +++ b/ui/tui.go @@ -0,0 +1,355 @@ +package ui + +import ( + "fmt" + "sort" + "strconv" + "strings" + "time" + "unicode" + + "github.com/gdamore/tcell" + "github.com/rivo/tview" + "maunium.net/go/mautrix" + "maunium.net/go/mautrix/event" + "maunium.net/go/mautrix/id" + "tallyard.xyz/election" +) + +func TUI(client *mautrix.Client, elections *election.ElectionsMap, localVoter *election.LocalVoter) { + alive := true + app := tview.NewApplication() + resp, err := client.JoinedRooms() + if err != nil { + panic(err) + } + list := tview.NewList() + for i, roomID := range resp.JoinedRooms { + name := string(roomID) + room := client.Store.LoadRoom(roomID) + if room != nil { + if roomNameEvent, ok := room.State[event.StateRoomName][""]; ok { + name = roomNameEvent.Content.AsRoomName().Name + } + } + list.AddItem(name, string(roomID), rune('a'+i), nil) + } + update := func() { + for i, roomID := range resp.JoinedRooms { + room := client.Store.LoadRoom(roomID) + if room == nil { + continue + } + if roomNameEvent, ok := room.State[event.StateRoomName][""]; ok { + name := roomNameEvent.Content.AsRoomName().Name + app.QueueUpdateDraw(func() { + list.SetItemText(i, name, string(roomID)) + }) + } + } + } + go func() { + for alive { + time.Sleep(1 * time.Second) // syncing right away can be slow + update() + } + }() + list.SetSelectedFunc(func(i int, _ string, _ string, _ rune) { + app.Stop() + alive = false + RoomTUI(client, resp.JoinedRooms[i], elections, localVoter) + }) + if err := app.SetRoot(list, true).SetFocus(list).Run(); err != nil { + panic(err) + } + alive = false +} + +func RoomTUI(client *mautrix.Client, roomID id.RoomID, elections *election.ElectionsMap, localVoter *election.LocalVoter) { + alive := true + app := tview.NewApplication() + list := tview.NewList(). + AddItem("Create Election", "start a new election in this room", '+', nil) + n := 0 + update := func() { + elections.RLock() + defer elections.RUnlock() + if n == len(elections.L) { + return + } + n = len(elections.L) + for i, el := range elections.L { + title := el.Title + if title == "" { + title = "" + } + var f func(string, string, rune, func()) *tview.List + if i+1 < list.GetItemCount() { + f = func(mainText string, secondaryText string, shortcut rune, selected func()) *tview.List { + return list.InsertItem(i+1, mainText, secondaryText, shortcut, selected) + } + } else { + f = list.AddItem + } + f(title, fmt.Sprintf("created by %s, ID: %s", el.Creator, el.CreateEventId), rune('a'+i), nil) + i++ + } + } + go func() { + for alive { + app.QueueUpdateDraw(func() { + update() + }) + time.Sleep(1 * time.Second) + } + }() + list.SetSelectedFunc(func(i int, _ string, _ string, _ rune) { + app.Stop() + alive = false + + if i > 0 { + // user wants to join election + elections.RLock() + defer elections.RUnlock() + el := elections.L[i-1] + if joinElectionConfirmation(el) { + _, electionMember := el.Voters[localVoter.UserID] + if !electionMember { + localVoter.JoinElection(client, el) + } + ElectionTUI(el, localVoter) + } else { + RoomTUI(client, roomID, elections, localVoter) + } + return + } + + // user wants to create election + title, candidates := CreateElectionTUI() + fmt.Println("title", title) + fmt.Println("candidates", candidates) + eventID, err := election.CreateElection(client, candidates, title, roomID) + if err != nil { + panic(err) + } + el, ok := elections.GetOk(eventID) + for !ok { + time.Sleep(10 * time.Millisecond) + el, ok = elections.GetOk(eventID) + } + err = localVoter.JoinElection(client, el) + if err != nil { + panic(err) + } + }) + if err := app.SetRoot(list, true).SetFocus(list).Run(); err != nil { + panic(err) + } + alive = false +} + +func joinElectionConfirmation(el *election.Election) (shouldJoin bool) { + app := tview.NewApplication() + + var buttons []string + var text string + + // TODO: handle when election starts while in modal + el.RLock() + if el.Started { + buttons = []string{"Ok"} + text = "Election has already started, sorry" + } else { + buttons = []string{"Yes", "No"} + text = "Join election?" + } + el.RUnlock() + + modal := tview.NewModal(). + SetText(text). + AddButtons(buttons). + SetDoneFunc(func(_ int, buttonLabel string) { + app.Stop() + if buttonLabel == "Yes" { + shouldJoin = true + } else { + shouldJoin = false + } + }) + if err := app.SetRoot(modal, false).SetFocus(modal).Run(); err != nil { + panic(err) + } + return +} + +func CreateElectionTUI() (title string, candidates []election.Candidate) { + var form *tview.Form + n := 2 + app := tview.NewApplication() + plus := func() { + form.AddInputField(fmt.Sprintf("%d.", n+1), "", 50, nil, nil) + n++ + } + minus := func() { + // TODO: ensure from joiner that there are at least two + // candidates + if n > 2 { + form.RemoveFormItem(n) // don't need -1 because of Title + n-- + } + } + done := func() { + // TODO: ensure none of the candidates are empty + app.Stop() + title = form.GetFormItem(0).(*tview.InputField).GetText() + for i := 1; i <= n; i++ { + candidates = append(candidates, + election.Candidate(form.GetFormItem(i).(*tview.InputField).GetText())) + } + } + form = tview.NewForm(). + AddInputField("Title", "", 50, nil, nil). + AddInputField("1.", "", 50, nil, nil). + AddInputField("2.", "", 50, nil, nil). + AddButton("+", plus). + AddButton("-", minus). + AddButton("Done", done) + if err := app.SetRoot(form, true).SetFocus(form).Run(); err != nil { + panic(err) + } + return title, candidates +} + +func ElectionTUI(election *election.Election, localVoter *election.LocalVoter) { + votersTextView := tview.NewTextView() + frame := tview.NewFrame(votersTextView) + if election.Creator == localVoter.UserID { + frame.AddText("Press enter to start the election", false, tview.AlignCenter, tcell.ColorWhite) + } else { + frame.AddText("Waiting for election to start...", false, tview.AlignCenter, tcell.ColorWhite) + } + app := tview.NewApplication() + app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + app.Stop() + if event.Key() == tcell.KeyEnter { + fmt.Println("ready to rock") + } + return event + }) + update := func() { + election.RLock() + voters := make([]string, 0, len(election.Voters)) + for voterUserID := range election.Voters { + voters = append(voters, voterUserID.String()) + } + election.RUnlock() + sort.Strings(voters) + text := strings.Join(voters, "\n") + text = "Joined voters:\n" + text + app.QueueUpdateDraw(func() { + votersTextView.SetText(text) + }) + } + go func() { + for { + update() + time.Sleep(1 * time.Second) + } + }() + if err := app.SetRoot(frame, true).SetFocus(frame).Run(); err != nil { + panic(err) + } +} + +// displays a voting UI to the user and returns the encoded ballot +func Vote(candidates []election.Candidate) []byte { + ranks := make([]int, len(candidates)) + app := tview.NewApplication() + form := tview.NewForm() + + for _, eo := range candidates { + // TODO: support more than 99 candidates + form.AddInputField(string(eo), "", 2, + func(textToCheck string, lastChar rune) bool { + return len(textToCheck) < 3 && unicode.IsDigit(lastChar) + }, nil) + } + + form.AddButton("Submit", func() { + app.Stop() + for i := 0; i < len(candidates); i++ { + rank, err := strconv.Atoi(form.GetFormItem(i).(*tview.InputField).GetText()) + if err != nil { + panic(err) + } + ranks[i] = rank + } + }) + + if err := app.SetRoot(form, true).SetFocus(form).Run(); err != nil { + panic(err) + } + + return GetBallotFromRankings(ranks) +} + +func GetBallotFromRankings(ranks []int) []byte { + n := len(ranks) + candidates := make([]int, n) + + for i := 0; i < n; i++ { + candidates[i] = i + } + + // sort candidates by ranking + cr := CandidateRanking{candidates, ranks} + sort.Sort(&cr) + + // TODO: support more than 255 voters (limit from usage of byte) + prefMatrix := make([][]byte, n) + + for len(candidates) > 0 { + r := ranks[candidates[0]] + i := 1 + for i < len(candidates) && ranks[candidates[i]] == r { + i++ + } + // i is now index of the first candidate with worse (i.e. higher + // in value) rank + row := make([]byte, n) + for j := i; j < len(candidates); j++ { + row[candidates[j]] = 1 + } + for j := 0; j < i; j++ { + prefMatrix[candidates[j]] = row + } + candidates = candidates[i:] + } + + // convert 2D array into 1D array + barray := make([]byte, 0, n*n) + for _, row := range prefMatrix { + barray = append(barray, row...) + } + + return barray +} + +type CandidateRanking struct { + candidates []int // becomes list of candidate IDs sorted by rank + ranks []int // maps candidate ID to rank +} + +func (cr *CandidateRanking) Len() int { + return len(cr.ranks) +} + +func (cr *CandidateRanking) Less(i, j int) bool { + return cr.ranks[cr.candidates[i]] < cr.ranks[cr.candidates[j]] +} + +func (cr *CandidateRanking) Swap(i, j int) { + tmp := cr.candidates[i] + cr.candidates[i] = cr.candidates[j] + cr.candidates[j] = tmp +} diff --git a/voter.go b/voter.go deleted file mode 100644 index e0b660c..0000000 --- a/voter.go +++ /dev/null @@ -1,390 +0,0 @@ -package main - -import ( - "bufio" - "context" - "fmt" - "io" - "math/big" - "os" - "strconv" - "sync" - "time" - - "github.com/libp2p/go-libp2p-core/host" - "github.com/libp2p/go-libp2p-core/network" - "github.com/libp2p/go-libp2p-core/peer" - discovery "github.com/libp2p/go-libp2p-discovery" - dht "github.com/libp2p/go-libp2p-kad-dht" - "github.com/mr-tron/base58/base58" - "github.com/multiformats/go-multiaddr" -) - -type Voter struct { - sum *big.Int - // may get more than 1 eval from peer; doesn't need to be RW because we - // never serve it to peers - inputMu sync.Mutex - input *big.Int - output *big.Int - addrInfo peer.AddrInfo -} - -type LocalVoter struct { - Voter - host.Host - ctx context.Context - ballot []byte - kdht *dht.IpfsDHT - poly *Poly - - // mutexs only used for atomicity; atomicity.Value sucks because we lose - // type safety with interface{} - polyMu sync.RWMutex // poly is computed after ballot; don't want R/W data races - sumMu sync.RWMutex // sum is computed in a loop; don - inputMu sync.RWMutex // TODO remove by generating input right away -} - -func stripNewline(str string) string { - if str == "" { - return str - } - if str[len(str)-1] == '\n' { - return str[:len(str)-1] - } - return str -} - -func handleCmd(cmd string, rw *bufio.ReadWriter, stream network.Stream, election *Election) { - localVoter := election.localVoter - switch cmd { - case "info": - rw.WriteString(fmt.Sprintf("%s\n", election.rendezvousNonce)) - for _, option := range election.Candidates { - rw.WriteString(fmt.Sprintf("%s\n", option)) - } - rw.Flush() - case "close": - election.Lock() - defer election.Unlock() - if peer := stream.Conn().RemotePeer(); peer != election.masterID { - logger.Warning("received close command from non-master:", peer) - return - } - if election.closed { - logger.Warning("election already closed") - return - } - str, err := rw.ReadString('\n') - if err != nil && err != io.EOF { - panic(err) - } - str = stripNewline(str) - numPeers, err := strconv.Atoi(str) - if err != nil { - panic(err) - } - election.close <- numPeers - election.closed = true - case "shake": - election.Lock() - defer election.Unlock() - peerID := stream.Conn().RemotePeer() - if election.closed { - logger.Warning("peer attempted to shake after "+ - "election was closed:", peerID) - return - } - if _, exists := election.remoteVoters[peerID]; exists { - logger.Warning("peer attempted to shake after having already done so", peerID) - return - } - fmt.Printf("found voter: %s\n", peerID) - election.remoteVoters[peerID] = &Voter{ - addrInfo: peer.AddrInfo{ - ID: peerID, - Addrs: []multiaddr.Multiaddr{stream.Conn().RemoteMultiaddr()}, - }, - } - case "eval": // peer is giving their input and requesting output from our poly - 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 - } - inputb58, err := rw.ReadString('\n') - if err != nil && err != io.EOF { - logger.Warning("unable to read input from peer during eval:", - stream.Conn().RemotePeer()) - return - } - inputb58 = stripNewline(inputb58) - inputBytes, err := base58.Decode(inputb58) - if err != nil { - logger.Warning("unable to base58 decode input from peer during eval:", - stream.Conn().RemotePeer()) - return - } - peer, exists := election.remoteVoters[stream.Conn().RemotePeer()] - if !exists { - logger.Warning("received eval command from unrecognized peer") - return - } - peer.inputMu.Lock() - defer peer.inputMu.Unlock() - peer.input = new(big.Int).SetBytes(inputBytes) - logger.Infof("%s input: %s", peer.addrInfo.ID, peer.input) - output := localVoter.poly.Eval(peer.input) - rw.WriteString(base58.Encode(output.Bytes())) - rw.Flush() - case "sum": - 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(localVoter.sum.Bytes())) - rw.Flush() - default: - logger.Warningf("uknown command %s", cmd) - } -} - -func streamHandler(stream network.Stream, election *Election) { - logger.Info("got a new stream:", stream) - logger.Info("remote peer:", stream.Conn().RemotePeer()) - rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream)) - - cmd, err := rw.ReadString('\n') - if err != nil && err != io.EOF { - panic(err) - } - cmd = stripNewline(cmd) - - logger.Info("cmd:", cmd) - handleCmd(cmd, rw, stream, election) - stream.Close() -} - -func findPeers(closeElection <-chan int, election *Election) { - localVoter := election.localVoter - routingDiscovery := discovery.NewRoutingDiscovery(localVoter.kdht) - logger.Info("announcing ourselves") - discovery.Advertise(localVoter.ctx, routingDiscovery, string(election.rendezvousNonce)) - logger.Info("successfully announced!") - - fmt.Println("finding other voters...") - peerChan, err := routingDiscovery.FindPeers(localVoter.ctx, string(election.rendezvousNonce)) - if err != nil { - panic(err) - } - numPeers := -1 - for { - if numPeers != -1 && numPeers == len(election.remoteVoters) { - break - } - select { - case peer := <-peerChan: - if peer.ID == localVoter.ID() { - continue - } - fmt.Printf("found voter: %s\n", peer.ID) - logger.Info("connecting to:", peer) - err = localVoter.Connect(localVoter.ctx, peer) - if err != nil { - logger.Warn("couldn't connect to peer: ", err) - continue - } - stream, err := localVoter.NewStream(localVoter.ctx, peer.ID, protocolID) - if err != nil { - logger.Warn("couldn't open stream with peer: ", err) - continue - } - writer := bufio.NewWriter(stream) - writer.WriteString("shake") - writer.Flush() - stream.Close() - election.remoteVoters[peer.ID] = &Voter{addrInfo: peer} - case numPeers = <-closeElection: - if len(election.remoteVoters) > numPeers { - logger.Fatalf("found more peers than master! %d > %d", - len(election.remoteVoters), numPeers) - os.Exit(1) - } - } - } - logger.Info("done finding peers") -} - -func (voter *Voter) fetchNumber(election *Election, cmd string, args ...string) *big.Int { - printErr := func(err error, msg string) { - logger.Errorf("%s: %s while fetching `%s'; retrying in 2 seconds", - voter.addrInfo.ID, msg, cmd) - time.Sleep(time.Second * 2) - } -retry: - localVoter := election.localVoter - stream, err := localVoter.NewStream(localVoter.ctx, voter.addrInfo.ID, protocolID) - if err != nil { - printErr(err, "couldn't open stream") - goto retry - } - rw := bufio.NewReadWriter(bufio.NewReader(stream), bufio.NewWriter(stream)) - out := cmd - for _, arg := range args { - out += "\n" + arg - } - _, err = rw.WriteString(out) - if err != nil { - printErr(err, "couldn't write to stream") - goto retry - } - err = rw.Flush() - if err != nil { - printErr(err, "couldn't flush stream") - goto retry - } - err = stream.Close() // only closes writing - if err != nil { - printErr(err, "couldn't close stream") - goto retry - } - retB58, err := rw.ReadString('\n') - if err != nil && err != io.EOF { - printErr(err, "couldn't read string from stream") - goto retry - } - retB58 = stripNewline(retB58) - if retB58 == "" { - printErr(err, "empty string") - goto retry - } - retBytes, err := base58.Decode(retB58) - if err != nil { - printErr(err, "couldn't base58-decode contents from stream") - goto retry - } - return new(big.Int).SetBytes(retBytes) -} - -func (election *Election) StartVoting() { - localVoter := election.localVoter - - var err error - localVoter.inputMu.Lock() - localVoter.input, err = RandomBigInt(128, false) - localVoter.inputMu.Unlock() - if err != nil { - panic(err) - } - logger.Infof("our input: %s", localVoter.input) - - localVoter.ballot = vote(election.Candidates) - logger.Infof("our ballot: %v", localVoter.ballot) - - // no +1 since we want degree k-1 where k is total number of voters - localVoter.polyMu.Lock() - localVoter.poly = NewRandomPoly(uint(len(election.remoteVoters)), - 1024, localVoter.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, "eval", base58.Encode(localVoter.input.Bytes())) - logger.Infof("voter %s output: %s", voter.addrInfo.ID, voter.output) - wg.Done() - }(voter) - } - wg.Wait() - - // calculate sum - localVoter.sumMu.Lock() - localVoter.sum = localVoter.poly.Eval(localVoter.input) - for _, voter := range election.remoteVoters { - localVoter.sum.Add(localVoter.sum, voter.output) - } - 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, "sum") - logger.Infof("voter %s sum: %s", - voter.addrInfo.ID, voter.sum) - wg.Done() - }(voter) - } - wg.Wait() - - mat := constructPolyMatrix(election) - mat.RREF() - - constant := mat[0][len(mat[0])-1] - if !constant.IsInt() { - panic("constant term is not an integer") - } - - 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) - result = append(make([]byte, diff), result...) - - printResults(result, election.Candidates) - - // temporary - select {} -} - -func constructPolyMatrix(election *Election) Matrix { - mat := make(Matrix, len(election.remoteVoters) + 1) // row for everyone (including ourselves) - - i := 0 - for _, voter := range election.remoteVoters { - mat[i] = make([]big.Rat, len(mat) + 1) // includes column for sum - row := mat[i] - row[0].SetInt64(1) - var j int64 - for j = 1; j <= int64(len(election.remoteVoters)); j++ { - row[j].SetInt(new(big.Int).Exp(voter.input, big.NewInt(j), nil)) - } - row[j].SetInt(voter.sum) - i++ - } - - // row for ourselves - mat[i] = make([]big.Rat, len(mat) + 1) - row := mat[i] - row[0].SetInt64(1) - localVoter := election.localVoter - var j int64 - for j = 1; j <= int64(len(election.remoteVoters)); j++ { - row[j].SetInt(new(big.Int).Exp(localVoter.input, big.NewInt(j), nil)) - } - row[j].SetInt(localVoter.sum) - - return mat -} - -func printResults(result []byte, candidates []Candidate) { - logger.Infof("result: %v", result) - fmt.Println("=== Results ===") - n := len(candidates) - for i, cand := range candidates { - for j, vs := range candidates { - if i != j { - fmt.Printf("%s over %s: %d\n", cand, vs, result[i * n + j]) - } - } - } -} -- 2.38.4