From dde4183f882cffc9d52589d9d70bde80d40f8922 Mon Sep 17 00:00:00 2001 From: David Florness Date: Mon, 5 Apr 2021 20:04:18 -0400 Subject: [PATCH] Implement zero-knowledge proofs for evaluations and summations Candidates had to be limited to 5 since 5*5=25 is the largest square less than 32, thu number of bytes in a BLS12-381 point: https://github.com/ConsenSys/gurvy/blob/e350ead0219d4f30aa72ec85d1857b573d2f136a/bls381/fr/element.go#L596 --- cmd/tallyard/main.go | 3 +- election/election.go | 2 +- election/event.go | 22 ++ election/map.go | 2 +- election/msg.go | 555 +++++++++++++++++++++++++++++++++--------- election/result.go | 58 ++--- election/version.go | 2 +- election/voter.go | 342 +++++++++++++++++++++++--- go.mod | 3 +- go.sum | 418 ++++++++++++++++++++++++++++++- math/lagrange.go | 31 +++ math/lagrange_test.go | 81 ++++++ math/linalg.go | 73 ------ math/linalg_test.go | 36 --- math/poly.go | 183 ++++++++++---- math/zk.go | 117 +++++++++ ui/tui.go | 84 ++++--- 17 files changed, 1634 insertions(+), 378 deletions(-) create mode 100644 math/lagrange.go create mode 100644 math/lagrange_test.go delete mode 100644 math/linalg.go delete mode 100644 math/linalg_test.go create mode 100644 math/zk.go diff --git a/cmd/tallyard/main.go b/cmd/tallyard/main.go index 1e35bf0..4ad9307 100644 --- a/cmd/tallyard/main.go +++ b/cmd/tallyard/main.go @@ -115,7 +115,7 @@ func main() { // user likely hit C-c return } - el.LocalVoter.Poly = math.NewRandomPoly(uint(len(*el.FinalJoinIDs)-1), 1024, *ballot) + el.LocalVoter.Poly = math.NewRandomPoly(*ballot, len(*el.FinalJoinIDs), &el.LocalVoter.Input) el.Save() } @@ -172,6 +172,7 @@ var electionFilter = &mautrix.Filter{ election.CreateElectionMessage, election.JoinElectionMessage, election.StartElectionMessage, + election.KeysMessage, election.EvalsMessage, election.SumMessage, }, diff --git a/election/election.go b/election/election.go index 3b50842..f6b8993 100644 --- a/election/election.go +++ b/election/election.go @@ -17,7 +17,7 @@ type Election struct { FinalJoinIDs *[]id.EventID `json:"final_voters,omitempty"` Joins map[id.EventID]*Voter `json:"joins"` LocalVoter *LocalVoter `json:"local_voter,omitempty"` - Result *[]byte `json:"result,omitempty"` + Result *[32]byte `json:"result,omitempty"` RoomID id.RoomID `json:"room_id"` StartID *id.EventID `json:"start_id,omitempty"` Title string `json:"title"` diff --git a/election/event.go b/election/event.go index 81af48d..8a3a1a1 100644 --- a/election/event.go +++ b/election/event.go @@ -55,6 +55,8 @@ func (store *EventStore) UnmarshalJSON(b []byte) error { event.Type = JoinElectionMessage case StartElectionMessage.Type: event.Type = StartElectionMessage + case KeysMessage.Type: + event.Type = KeysMessage case EvalsMessage.Type: event.Type = EvalsMessage case SumMessage.Type: @@ -82,6 +84,11 @@ type StartEvent struct { *StartElectionContent } +type KeysEvent struct { + *event.Event + *KeysMessageContent +} + type EvalsEvent struct { *event.Event *EvalsMessageContent @@ -137,6 +144,21 @@ func (store *EventStore) GetStartEvent(roomID id.RoomID, startID id.EventID) *St } } +func (store *EventStore) GetKeysEvent(roomID id.RoomID, keysID id.EventID) *KeysEvent { + evt, err := store.getAndHandleEvent(roomID, keysID, KeysMessage) + if err != nil { + log.Warnf("an error occurred getting keys event '%s': %s", keysID, err) + return nil + } + if evt == nil { + return nil + } + return &KeysEvent{ + evt, + evt.Content.Parsed.(*KeysMessageContent), + } +} + func (store *EventStore) GetEvalsEvent(roomID id.RoomID, evalsID id.EventID) *EvalsEvent { evt, err := store.getAndHandleEvent(roomID, evalsID, EvalsMessage) if err != nil { diff --git a/election/map.go b/election/map.go index 2c33359..1280af2 100644 --- a/election/map.go +++ b/election/map.go @@ -37,7 +37,7 @@ type ElectionsMap struct { save func(*ElectionsMap) `json:"-"` } -const electionsMapVersion = 5 +const electionsMapVersion = 6 func NewElectionsMap(userID id.UserID, save func(*ElectionsMap)) *ElectionsMap { return &ElectionsMap{ diff --git a/election/msg.go b/election/msg.go index bff3d4e..cc4dde0 100644 --- a/election/msg.go +++ b/election/msg.go @@ -1,18 +1,24 @@ package election import ( + "bytes" "encoding/base64" "fmt" - "math/big" "reflect" "time" + "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/crypto/hash/mimc/bls381" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gurvy" + "github.com/consensys/gurvy/bls381/fr" log "github.com/sirupsen/logrus" "golang.org/x/crypto/nacl/box" "golang.org/x/mod/semver" "maunium.net/go/mautrix" "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/id" + "tallyard.xyz/math" ) var ( @@ -32,6 +38,11 @@ var ( Type: "xyz.tallyard.start", Class: event.MessageEventType, } + // contains keys needed to do zero knowledge proofs + KeysMessage = event.Type{ + Type: "xyz.tallyard.keys", + Class: event.MessageEventType, + } // indicate's user's evaluations of their polynomial using others' inputs EvalsMessage = event.Type{ Type: "xyz.tallyard.evals", @@ -57,6 +68,7 @@ type JoinElectionContent struct { CreateID id.EventID `json:"create_id"` Input string `json:"input"` PubKey string `json:"pub_key"` + HashSeed string `json:"hash_seed"` } type StartElectionContent struct { @@ -66,12 +78,32 @@ type StartElectionContent struct { JoinIDs []id.EventID `json:"join_ids"` } +type KeysMessageContent struct { + Version string `json:"version"` + + EvalProvingKeyURI id.ContentURI `json:"eval_proving_key_uri"` + JoinID id.EventID `json:"join_id"` + StartID id.EventID `json:"start_id"` + SumProvingKeyURI id.ContentURI `json:"sum_proving_key_uri"` +} + type EvalsMessageContent struct { Version string `json:"version"` - Evals map[id.EventID]string `json:"evals"` - JoinID id.EventID `json:"join_id"` - StartID id.EventID `json:"start_id"` + Evals []Eval `json:"evals"` + JoinID id.EventID `json:"join_id"` + KeysIDs []id.EventID `json:"keys_ids"` +} + +type Eval struct { + // encrypted for specific voter + Output string `json:"output"` + // public; used by everyone in summation proofs + OutputHash string `json:"output_hash"` + // encrypted for specific voter + POutputHash string `json:"poutput_hash"` + // encrypted for specific voter + Proof string `json:"proof"` } type SumMessageContent struct { @@ -80,20 +112,14 @@ type SumMessageContent struct { EvalsIDs []id.EventID `json:"evals_ids"` JoinID id.EventID `json:"join_id"` Sum string `json:"sum"` -} - -type ResultMessageContent struct { - Version string `json:"version"` - - JoinID id.EventID `json:"join_id"` - Result string `json:"result"` - SumIDs []id.EventID `json:"sums"` + Proofs []string `json:"proofs"` } func init() { event.TypeMap[CreateElectionMessage] = reflect.TypeOf(CreateElectionContent{}) event.TypeMap[JoinElectionMessage] = reflect.TypeOf(JoinElectionContent{}) event.TypeMap[StartElectionMessage] = reflect.TypeOf(StartElectionContent{}) + event.TypeMap[KeysMessage] = reflect.TypeOf(KeysMessageContent{}) event.TypeMap[EvalsMessage] = reflect.TypeOf(EvalsMessageContent{}) event.TypeMap[SumMessage] = reflect.TypeOf(SumMessageContent{}) } @@ -113,7 +139,8 @@ func (elections *ElectionsMap) SetupEventHooks(client *mautrix.Client, syncer ma eventStore.Processing = make(map[id.EventID]struct{}) } - wrapper := func(f func(*event.Event) bool) func(*event.Event) bool { + // TODO: this is a freaking mess + wrapper := func(f func(*event.Event, *mautrix.Client) bool) func(*event.Event) bool { return func(evt *event.Event) bool { if evt.Unsigned.RedactedBecause != nil { log.Debugf("event %s was redacted", evt.ID) @@ -140,7 +167,7 @@ func (elections *ElectionsMap) SetupEventHooks(client *mautrix.Client, syncer ma eventStore.Processing[evt.ID] = struct{}{} eventStore.Unlock() } - success := f(evt) + success := f(evt, client) eventStore.Lock() // see EventStore doc for success explanation if success { @@ -153,12 +180,18 @@ func (elections *ElectionsMap) SetupEventHooks(client *mautrix.Client, syncer ma return success } } + wrapperNoClient := func(f func(*event.Event) bool) func(*event.Event) bool { + return wrapper(func(evt *event.Event, _ *mautrix.Client) bool { + return f(evt) + }) + } - eventHandlers[CreateElectionMessage] = wrapper(elections.onCreateElectionMessage) - eventHandlers[JoinElectionMessage] = wrapper(elections.onJoinElectionMessage) - eventHandlers[StartElectionMessage] = wrapper(elections.onStartElectionMessage) - eventHandlers[EvalsMessage] = wrapper(elections.onEvalsMessage) - eventHandlers[SumMessage] = wrapper(elections.onSumMessage) + eventHandlers[CreateElectionMessage] = wrapperNoClient(elections.onCreateElectionMessage) + eventHandlers[JoinElectionMessage] = wrapperNoClient(elections.onJoinElectionMessage) + eventHandlers[StartElectionMessage] = wrapperNoClient(elections.onStartElectionMessage) + eventHandlers[KeysMessage] = wrapper(elections.onKeysMessage) + eventHandlers[EvalsMessage] = wrapperNoClient(elections.onEvalsMessage) + eventHandlers[SumMessage] = wrapperNoClient(elections.onSumMessage) for eventType, handler := range eventHandlers { func(handler func(*event.Event) bool) { @@ -206,6 +239,11 @@ func (elections *ElectionsMap) onCreateElectionMessage(evt *event.Event) (succes return } + if len(content.Candidates) < 2 || len(content.Candidates) > 5 { + warnf("the number of candidates must be between 2 and 5 (inclusive), but was %s", len(content.Candidates)) + return + } + elections.AddElection(NewElection( content.Candidates, *evt, @@ -216,6 +254,8 @@ func (elections *ElectionsMap) onCreateElectionMessage(evt *event.Event) (succes return true } +var zero *fr.Element = new(fr.Element).SetZero() + func (elections *ElectionsMap) onJoinElectionMessage(evt *event.Event) (success bool) { errorf, warnf, debugf := logFuncs(fmt.Sprintf("ignoring %s's join msg (%s) since %s", evt.Sender, evt.ID, "%s")) @@ -225,37 +265,55 @@ func (elections *ElectionsMap) onJoinElectionMessage(evt *event.Event) (success return } + // Version if incompatibleVersion(content.Version) { debugf("the version is incompatible") return } + // CreateID createEvt := elections.EventStore.GetCreateEvent(evt.RoomID, content.CreateID) if createEvt == nil { debugf("we couldn't get the create event, %s", content.CreateID) return } - bytes, err := base64.StdEncoding.DecodeString(content.Input) + // HashSeed + if content.HashSeed == "" { + warnf("the hash seed is empty") + return + } + hashSeed, err := base64.StdEncoding.DecodeString(content.HashSeed) if err != nil { - warnf("we couldn't decode their input: %s", err) + warnf("we couldn't decode their hash seed: %s", err) + return + } + if len(hashSeed) < 32 { + warnf("their hash seed is fewer than 32 bytes") return } - input := new(big.Int).SetBytes(bytes) + // Input + byts, err := base64.StdEncoding.DecodeString(content.Input) + if err != nil { + warnf("we couldn't decode their input: %s", err) + return + } + input := new(fr.Element).SetBytes(byts) // zero is not allowed - if input.Cmp(new(big.Int)) == 0 { + if input.Cmp(zero) == 0 { warnf("their input is zero") return } - bytes, err = base64.StdEncoding.DecodeString(content.PubKey) + // PubKey + byts, err = base64.StdEncoding.DecodeString(content.PubKey) if err != nil { warnf("we couldn't decode their public key: %s", err) return } var pubKey [32]byte - copy(pubKey[:], bytes) + copy(pubKey[:], byts) el := elections.GetElection(createEvt.ID) if el == nil { @@ -269,7 +327,7 @@ func (elections *ElectionsMap) onJoinElectionMessage(evt *event.Event) (success defer el.Save() defer el.Unlock() - el.Joins[evt.ID] = NewVoter(input, evt, &pubKey) + el.Joins[evt.ID] = NewVoter(input, evt, &pubKey, hashSeed) return true } @@ -308,7 +366,7 @@ func (elections *ElectionsMap) onStartElectionMessage(evt *event.Event) (success } if createEvt.Sender != evt.Sender { - warnf("they didn't create the election") + warnf("they didn't create the election; %s did", createEvt.Sender) return } @@ -331,6 +389,126 @@ func (elections *ElectionsMap) onStartElectionMessage(evt *event.Event) (success el.StartID = &evt.ID el.FinalJoinIDs = &content.JoinIDs + for i, joinID := range *el.FinalJoinIDs { + // TODO: gross + joinIndex := i + el.Joins[joinID].JoinIDIndex = &joinIndex + } + + return true +} + +func (elections *ElectionsMap) onKeysMessage(evt *event.Event, client *mautrix.Client) (success bool) { + errorf, warnf, debugf := logFuncs(fmt.Sprintf("ignoring %s's keys msg (%s) since %s", evt.Sender, evt.ID, "%s")) + + content, ok := evt.Content.Parsed.(*KeysMessageContent) + if !ok { + warnf("we couldn't cast message content to KeysMessageContent") + return + } + + // Version + if incompatibleVersion(content.Version) { + debugf("the version is incompatible") + return + } + + joinEvt := elections.EventStore.GetJoinEvent(evt.RoomID, content.JoinID) + if joinEvt == nil { + debugf("we couldn't get the join event, %s", content.JoinID) + return + } + if evt.Sender != joinEvt.Sender { + warnf("they did not send the join event; %s did", joinEvt.Sender) + return + } + + // process the start event so that JoinIDIndex is availabe below + if elections.EventStore.GetStartEvent(evt.RoomID, content.StartID) == nil { + debugf("we couldn't process the start event, %s", content.StartID) + return + } + + el := elections.GetElection(joinEvt.CreateID) + if el == nil { + // should never happen because we retrieved the join event above + errorf("election %s doesn't exist", joinEvt.ID) + return + } + + var evalProvingKey groth16.ProvingKey + { + // yes, I know we should be using Download here + byts, err := client.DownloadBytes(content.EvalProvingKeyURI) + if err != nil { + errorf("couldn't download eval proving key: %s", err) + return + } + tryEvalPk: + var buf bytes.Buffer + _, err = buf.Write(byts) + if err != nil { + errorf("couldn't write to eval bytes buffer: %s") + return + } + evalProvingKey = groth16.NewProvingKey(gurvy.BLS381) + _, err = evalProvingKey.ReadFrom(&buf) + if err != nil { + log.Errorf("couldn't read eval key from downloaded eval proving keys file: %s", err) + log.Debug("trying again...") + goto tryEvalPk + } + } + + var sumProvingKey groth16.ProvingKey + { + byts, err := client.DownloadBytes(content.SumProvingKeyURI) + if err != nil { + errorf("couldn't download sum proving key: %s", err) + return + } + trySumPk: + var buf bytes.Buffer + _, err = buf.Write(byts) + if err != nil { + errorf("couldn't write to sum bytes buffer: %s") + return + } + sumProvingKey = groth16.NewProvingKey(gurvy.BLS381) + _, err = sumProvingKey.ReadFrom(&buf) + if err != nil { + log.Errorf("couldn't read sum key from downloaded sum keys file: %s", err) + log.Debug("trying again...") + goto trySumPk + } + } + + var voter *Voter + { + var exists bool + // join should exist because we processed the start event above + if voter, exists = el.Joins[content.JoinID]; !exists { + warnf("the join event (%s) does not point to the election", content.JoinID) + return + } + if voter.JoinIDIndex == nil { + warnf("the join ID (%s) was not included in the start event (%s)", content.JoinID, content.StartID) + return + } + } + + if voter.KeysID != nil { + warnf("voter already sent a keys message") + return + } + + el.Lock() + defer el.Save() + defer el.Unlock() + + voter.EvalProvingKey = &evalProvingKey + voter.SumProvingKey = &sumProvingKey + voter.KeysID = &evt.ID return true } @@ -344,46 +522,52 @@ func (elections *ElectionsMap) onEvalsMessage(evt *event.Event) (success bool) { return } + // Version if incompatibleVersion(content.Version) { debugf("the version is incompatible") return } - startEvt := elections.EventStore.GetStartEvent(evt.RoomID, content.StartID) - if startEvt == nil { - debugf("we couldn't get the start event, %s", content.StartID) - return - } - + // JoinID joinEvt := elections.EventStore.GetJoinEvent(evt.RoomID, content.JoinID) if joinEvt == nil { debugf("we couldn't get the join event, %s", content.JoinID) return } + if evt.Sender != joinEvt.Sender { + warnf("they did not send the join event; %s did", joinEvt.Sender) + return + } - // ensure keys of Evals are in JoinIDs of start event - if len(content.Evals) != len(startEvt.JoinIDs) { - warnf("the number of evals is wrong (%s instead of %s)", - len(content.Evals), len(startEvt.JoinIDs)) + el := elections.GetElection(joinEvt.CreateID) + if el == nil { + // should never happen because we got the start event above + errorf("election %s doesn't exist", joinEvt.CreateID) + return + } + + // KeysIDs + if len(content.KeysIDs) != len(*el.FinalJoinIDs) { + warnf("the number of keys IDs is wrong (%s instead of %s)", + len(content.KeysIDs), len(*el.FinalJoinIDs)) return } - for _, joinID := range startEvt.JoinIDs { - if _, exists := content.Evals[joinID]; !exists { - warnf("it doesn't include an eval for join %s", joinID) + for i, keysID := range content.KeysIDs { + keysEvent := elections.EventStore.GetKeysEvent(evt.RoomID, keysID) + if keysEvent == nil { + debugf("we couldn't get the keys event, %s", keysID) + return + } + if keysEvent.JoinID != (*el.FinalJoinIDs)[i] { + warnf("the join ID (%s) of a key (%s) does not match the start event's join ID (%s)", + keysEvent.JoinID, keysID, (*el.FinalJoinIDs)[i]) return } - } - // is the voter's join ID in the start event's list of join IDs? - if _, exists := content.Evals[joinEvt.ID]; !exists { - warnf("the join event %s is not listed in the start event's join IDs", joinEvt.ID) - return } - el := elections.GetElection(startEvt.CreateID) - if el == nil { - // should never happen because we retrieved the start/join - // events above - errorf("election %s doesn't exist", startEvt.CreateID) + if len(content.Evals) != len(*el.FinalJoinIDs) { + warnf("the number of evals is wrong (%s instead of %s)", + len(content.Evals), len(*el.FinalJoinIDs)) return } @@ -393,54 +577,147 @@ func (elections *ElectionsMap) onEvalsMessage(evt *event.Event) (success bool) { voter := el.Joins[joinEvt.ID] if voter == nil { - // should never happen because we called getJoinEvent above + // should never happen because we called GetJoinEvent above errorf("voter %s doesn't exist", joinEvt.ID) return } - + if voter.JoinIDIndex == nil { + warnf("the voter's join ID was not included in the start event") + return + } if voter.EvalsID != nil { warnf("voter submitted multiple evals events") return } - voter.EvalsID = &evt.ID + // we're not participating in this election if el.LocalVoter == nil { return true + } else if el.LocalVoter.JoinIDIndex == nil { + warnf("we didn't join the election in time (or the election creator excluded us)") + return } + ourEval := content.Evals[*el.LocalVoter.JoinIDIndex] - encodedEncryptedOutput, exists := content.Evals[el.LocalVoter.JoinEvt.ID] - // If our ID doesn't exist in the keys of evalsEvtContent.Evals, our - // JoinID wasn't included in the startEvtContent.JoinIDs (since we - // checked that the two are equivalent above). I'm checking membership - // in Evals instead of startEvt.JoinIDs because maps are easier than - // slices for that. - if !exists { - debugf("we didn't join the election in time (or the election creator excluded us)") - return + // decrypt output + var output *fr.Element + { + encryptedOutput, err := base64.StdEncoding.DecodeString(ourEval.Output) + if err != nil { + warnf("couldn't decode the encrypted output for us: %s", err) + return + } + var decryptNonce [24]byte + copy(decryptNonce[:], encryptedOutput[:24]) + decryptedOutput, ok := box.Open(nil, encryptedOutput[24:], &decryptNonce, &voter.PubKey, &el.LocalVoter.PrivKey) + if !ok { + warnf("couldn't decrypt eval output for us") + return + } + output = new(fr.Element).SetBytes(decryptedOutput) } - encryptedOutput, err := base64.StdEncoding.DecodeString(encodedEncryptedOutput) - if err != nil { - warnf("couldn't decode %s's encrypted output: %s", evt.Sender, err) - return + + // decrypt proof + var proof groth16.Proof + { + encryptedProof, err := base64.StdEncoding.DecodeString(ourEval.Proof) + if err != nil { + warnf("couldn't decode the encrypted proof for us: %s", err) + return + } + var decryptNonce [24]byte + copy(decryptNonce[:], encryptedProof[:24]) + decryptedProofBytes, ok := box.Open(nil, encryptedProof[24:], &decryptNonce, &voter.PubKey, &el.LocalVoter.PrivKey) + if !ok { + warnf("couldn't decrypt eval proof for us") + return + } + proof = groth16.NewProof(gurvy.BLS381) + _, err = proof.ReadFrom(bytes.NewBuffer(decryptedProofBytes)) + if err != nil { + errorf("couldn't read proof from bytes buffer: %s", err) + return + } } - var decryptNonce [24]byte - copy(decryptNonce[:], encryptedOutput[:24]) - decryptedOutput, ok := box.Open(nil, encryptedOutput[24:], &decryptNonce, &voter.PubKey, &el.LocalVoter.PrivKey) - if !ok { - warnf("couldn't decrypt eval for us") - return + + // decrypt poutputHash + var poutputHash []byte + { + encryptedPOutputHash, err := base64.StdEncoding.DecodeString(ourEval.POutputHash) + if err != nil { + warnf("couldn't decode the encrypted poutput hash for us: %s", err) + return + } + var decryptNonce [24]byte + copy(decryptNonce[:], encryptedPOutputHash[:24]) + poutputHash, ok = box.Open(nil, encryptedPOutputHash[24:], &decryptNonce, &voter.PubKey, &el.LocalVoter.PrivKey) + if !ok { + warnf("couldn't decrypt poutput hash for us") + return + } + } + + outputHashes := make([][]byte, len(*el.FinalJoinIDs)) + var err error + for i, eval := range content.Evals { + outputHashes[i], err = base64.StdEncoding.DecodeString(eval.OutputHash) + if err != nil { + warnf("we couldn't decode the %d th output hash", i) + return + } + } + + // verify proof + { + var publicCircuit math.EvalCircuit + publicCircuit.Coeffs = make([]frontend.Variable, len(*el.FinalJoinIDs)-1) + publicCircuit.BallotBits = make([]frontend.Variable, len(el.Candidates)*len(el.Candidates)) + publicCircuit.Input.Assign(&el.LocalVoter.Input) + publicCircuit.Output.Assign(output) + publicCircuit.PInput.Assign(&voter.Input) + publicCircuit.POutputHash.Assign(poutputHash) + publicCircuit.HashSeed = el.LocalVoter.HashSeed + if el.LocalVoter.EvalVerifyingKey == nil { + // should never happen because we processed all keys + // events above + errorf("our evals verifying key is nil") + return + } + err = groth16.Verify(proof, *el.LocalVoter.EvalVerifyingKey, &publicCircuit) + if err != nil { + warnf("poly eval proof verification failed: %s", err) + return + } } - voter.Eval = new(big.Int).SetBytes(decryptedOutput) + // ensure our output hashes to the given hash + { + outputBytes := output.Bytes() + outputHash, err := bls381.Sum(el.LocalVoter.HashSeed, outputBytes[:]) + if err != nil { + errorf("couldn't hash output: %s", err) + return + } + // el.LocalVoter.JoinIDIndex checked for nilness above + if bytes.Compare(outputHashes[*el.LocalVoter.JoinIDIndex], outputHash) != 0 { + warnf("our given output did not hash to the given hash") + return + } + } + + voter.Output = output + voter.OutputHashes = &outputHashes + + // calculate our sum if we have everyone's outputs for _, voterID := range *el.FinalJoinIDs { - if el.Joins[voterID].Eval == nil { + if el.Joins[voterID].Output == nil { return true } } - sum := big.NewInt(0) + sum := new(fr.Element).SetZero() for _, voterID := range *el.FinalJoinIDs { - sum.Add(sum, el.Joins[voterID].Eval) + sum.Add(sum, el.Joins[voterID].Output) } el.LocalVoter.Sum = sum @@ -456,6 +733,7 @@ func (elections *ElectionsMap) onSumMessage(evt *event.Event) (success bool) { return } + // Version if incompatibleVersion(content.Version) { debugf("the version is incompatible") return @@ -466,50 +744,35 @@ func (elections *ElectionsMap) onSumMessage(evt *event.Event) (success bool) { debugf("we couldn't get the join event, %s", content.JoinID) return } + if evt.Sender != joinEvt.Sender { + warnf("they did not send the join event; %s did", joinEvt.Sender) + return + } if len(content.EvalsIDs) == 0 { warnf("evals length is zero") return } - joinIDs := make(map[id.EventID]id.EventID) - var startID id.EventID + el := elections.GetElection(joinEvt.CreateID) + if el == nil { + // should never happen because we retrieved the start/join + // events above + errorf("election %s doesn't exist", joinEvt.CreateID) + return + } - for _, evalsID := range content.EvalsIDs { + for i, evalsID := range content.EvalsIDs { evalsEvt := elections.EventStore.GetEvalsEvent(evt.RoomID, evalsID) if evalsEvt == nil { debugf("we couldn't get an evals event, %s", evalsID) return } - - // ensure all evals have the same start ID - if startID == "" { - startID = evalsEvt.StartID - } else if evalsEvt.StartID != startID { - warnf("at least two evals have different startIDs (%s, %s, ...)", startID, evalsEvt.StartID) - return - } - - // ensure no two evals use the same join ID. This'll also catch - // duplicates in EvalsIDs - if same, exists := joinIDs[evalsEvt.JoinID]; exists { - warnf("at least two evals use the same join ID (%s, %s, ...)", same, evalsID) + if evalsEvt.JoinID != (*el.FinalJoinIDs)[i] { + warnf("the join ID (%s) of an evals (%s) does not match the start event's join ID (%s)", + evalsEvt.JoinID, evalsID, (*el.FinalJoinIDs)[i]) return } - - joinIDs[evalsEvt.JoinID] = evalsID - - // Remember, we don't need to check that the evals's join ID is - // in the start event because we successfully retrieved the - // evals event above. Recursion is wonderful. - } - - el := elections.GetElection(joinEvt.CreateID) - if el == nil { - // should never happen because we retrieved the start/join - // events above - errorf("election %s doesn't exist", joinEvt.CreateID) - return } el.Lock() @@ -528,14 +791,84 @@ func (elections *ElectionsMap) onSumMessage(evt *event.Event) (success bool) { return } - bytes, err := base64.StdEncoding.DecodeString(content.Sum) - if err != nil { - warnf("we couldn't decode their sum: %s", err) - return + var sum *fr.Element + { + sumBytes, err := base64.StdEncoding.DecodeString(content.Sum) + if err != nil { + warnf("we couldn't decode their sum: %s", err) + return + } + sum = new(fr.Element).SetBytes(sumBytes) + } + + var proof groth16.Proof + { + encrypted, err := base64.StdEncoding.DecodeString(content.Proofs[*el.LocalVoter.JoinIDIndex]) + if err != nil { + warnf("couldn't decode sum proof for us: %s", err) + return + } + var nonce [24]byte + copy(nonce[:], encrypted[:24]) + decrypted, ok := box.Open(nil, encrypted[24:], &nonce, &voter.PubKey, &el.LocalVoter.PrivKey) + if !ok { + warnf("couldn't decrypt sum proof for us: %s", err) + return + } + var buf bytes.Buffer + _, err = buf.Write(decrypted) + if err != nil { + errorf("couldn't write decrypted sum proof to buffer: %s", err) + return + } + proof = groth16.NewProof(gurvy.BLS381) + _, err = proof.ReadFrom(&buf) + if err != nil { + errorf("couldn't read decrypted sum proof from buffer: %s", err) + return + } + } + + // verify proof + { + var publicCircuit math.SumCircuit + n := len(*el.FinalJoinIDs) + + hashSeeds := make([]string, n) + hashSelects := make([]frontend.Variable, n) + outputHashes := make([]frontend.Variable, n) + voterJoinIDIndex := *voter.JoinIDIndex + for i, joinID := range *el.FinalJoinIDs { + evaler := el.Joins[joinID] + hashSeeds[i] = evaler.HashSeed + if i == voterJoinIDIndex { + hashSelects[i].Assign(1) + } else { + hashSelects[i].Assign(0) + } + outputHashes[i].Assign((*evaler.OutputHashes)[voterJoinIDIndex]) + } + publicCircuit.HashSeeds = hashSeeds + publicCircuit.HashSelects = hashSelects + publicCircuit.OutputHashes = outputHashes + publicCircuit.Sum.Assign(sum) + publicCircuit.Outputs = make([]frontend.Variable, n) + + if el.LocalVoter.SumVerifyingKey == nil { + // should never happen because we processed all keys + // events above + errorf("our sum verifying key is nil") + return + } + err := groth16.Verify(proof, *el.LocalVoter.SumVerifyingKey, &publicCircuit) + if err != nil { + warnf("sum proof verification failed: %s", err) + return + } } voter.SumID = &evt.ID - voter.Sum = new(big.Int).SetBytes(bytes) + voter.Sum = sum // if we've recieved all sums, calculate the election result! for _, voterID := range *el.FinalJoinIDs { diff --git a/election/result.go b/election/result.go index 679735c..f20a667 100644 --- a/election/result.go +++ b/election/result.go @@ -2,7 +2,6 @@ package election import ( "fmt" - "math/big" "sort" "time" @@ -13,16 +12,14 @@ import ( func (el *Election) calculateResult() { // this assumes all sums have been received and successfully validated // and processed - M := constructPolyMatrix(el) - M.RREF() - constant := M[0][len(M[0])-1] - if !constant.IsInt() { - panic("constant term is not an integer") + + points := make([]math.Point, len(*el.FinalJoinIDs)) + for i, joinID := range *el.FinalJoinIDs { + points[i][0] = &el.Joins[joinID].Input + points[i][1] = el.Joins[joinID].Sum } - result := constant.Num().Bytes() - // number of bytes we need to insert at the front since they're zero - diff := (len(el.Candidates) * len(el.Candidates)) - len(result) - result = append(make([]byte, diff), result...) + + result := math.LagrangeEvalAtZero(points).Bytes() el.Result = &result } @@ -33,16 +30,25 @@ func (el *Election) PrintResults() { time.Sleep(time.Millisecond * 200) } } - result := *el.Result candidates := el.Candidates - log.Debugf("result: %v", result) + var getRank func(i, j int) byte + { + result := *el.Result + log.Debugf("result: %v", result) + n := len(candidates) + l := len(result) + getRank = func(i, j int) byte { + return result[l - n*n + i*n+j] + } + } + 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]) + fmt.Printf("%s over %s: %d\n", cand, vs, getRank(i, j)) } } } @@ -69,8 +75,8 @@ func (el *Election) PrintResults() { p[i] = make([]int, len(candidates)) for j := range candidates { if i != j { - if result[i*n+j] > result[j*n+i] { - p[i][j] = int(result[i*n+j]) + if getRank(i, j) > getRank(j, i) { + p[i][j] = int(getRank(i, j)) } else { p[i][j] = 0 } @@ -113,23 +119,3 @@ func (el *Election) PrintResults() { } } } - -func constructPolyMatrix(el *Election) math.Matrix { - mat := make(math.Matrix, len(*el.FinalJoinIDs)) - - i := 0 - for _, voterJoinId := range *el.FinalJoinIDs { - voter := el.Joins[voterJoinId] - 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(*el.FinalJoinIDs)-1); j++ { - row[j].SetInt(new(big.Int).Exp(&voter.Input, big.NewInt(j), nil)) - } - row[j].SetInt(voter.Sum) - i++ - } - - return mat -} diff --git a/election/version.go b/election/version.go index a318485..3262f1c 100644 --- a/election/version.go +++ b/election/version.go @@ -1,3 +1,3 @@ package election -const Version string = "v0.3.0" +const Version string = "v0.4.0" diff --git a/election/voter.go b/election/voter.go index 6cab9ff..10171b7 100644 --- a/election/voter.go +++ b/election/voter.go @@ -1,13 +1,19 @@ package election import ( + "bytes" "crypto/rand" "encoding/base64" "errors" "fmt" "io" - "math/big" + "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/backend/r1cs" + "github.com/consensys/gnark/crypto/hash/mimc/bls381" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gurvy" + "github.com/consensys/gurvy/bls381/fr" "golang.org/x/crypto/nacl/box" "maunium.net/go/mautrix" "maunium.net/go/mautrix/event" @@ -16,27 +22,39 @@ import ( ) type Voter struct { - Input big.Int `json:"input"` - JoinEvt event.Event `json:"join_evt"` - PubKey [32]byte `json:"pub_key"` - - Eval *big.Int `json:"eval,omitempty"` - EvalsID *id.EventID `json:"evals_id,omitempty"` - Sum *big.Int `json:"sum,omitempty"` - SumID *id.EventID `json:"sum_id,omitempty"` + Input fr.Element `json:"input"` + JoinEvt event.Event `json:"join_evt"` + PubKey [32]byte `json:"pub_key"` + HashSeed string `json:"hash_seed"` + + JoinIDIndex *int `json:"join_id_index,omitempty"` + Output *fr.Element `json:"output,omitempty"` + OutputHashes *[][]byte `json:"output_hashes,omitempty"` + KeysID *id.EventID `json:"keys_id,omitempty"` + EvalsID *id.EventID `json:"evals_id,omitempty"` + Sum *fr.Element `json:"sum,omitempty"` + SumID *id.EventID `json:"sum_id,omitempty"` + + EvalProvingKey *groth16.ProvingKey `json:"eval_proving_key"` + SumProvingKey *groth16.ProvingKey `json:"sum_proving_key"` } type LocalVoter struct { *Voter - Poly *math.Poly `json:"poly,omitempty"` - PrivKey [32]byte `json:"priv_key"` + + PrivKey [32]byte `json:"priv_key"` + + EvalVerifyingKey *groth16.VerifyingKey `json:"eval_verifying_key"` + Poly *math.Poly `json:"poly,omitempty"` + SumVerifyingKey *groth16.VerifyingKey `json:"sum_verifying_key"` } -func NewVoter(input *big.Int, joinEvt *event.Event, pubKey *[32]byte) *Voter { +func NewVoter(input *fr.Element, joinEvt *event.Event, pubKey *[32]byte, hashSeed []byte) *Voter { return &Voter{ - Input: *new(big.Int).Set(input), - JoinEvt: *joinEvt, - PubKey: *pubKey, + Input: *new(fr.Element).Set(input), + JoinEvt: *joinEvt, + PubKey: *pubKey, + HashSeed: string(hashSeed), } } @@ -71,23 +89,30 @@ func (el *Election) JoinElection(client *mautrix.Client, eventStore *EventStore) return err } - input, err := math.RandomBigInt(1024, false) + input, err := new(fr.Element).SetRandom() if err != nil { return err } + inputBytes := input.Bytes() + + var seedBytes [32]byte + if _, err := io.ReadFull(rand.Reader, seedBytes[:]); err != nil { + return fmt.Errorf("couldn't read random bytes: %s", err) + } resp, err := client.SendMessageEvent(el.RoomID, JoinElectionMessage, JoinElectionContent{ - Version: Version, + Version: Version, + CreateID: el.CreateEvt.ID, - Input: base64.StdEncoding.EncodeToString(input.Bytes()), + Input: base64.StdEncoding.EncodeToString(inputBytes[:]), PubKey: base64.StdEncoding.EncodeToString((*pubKey)[:]), + HashSeed: base64.StdEncoding.EncodeToString(seedBytes[:]), }) if err != nil { - return err + return fmt.Errorf("couldn't send join messages: %s", err) } - joinEvt := eventStore.GetJoinEvent(el.RoomID, resp.EventID) - if joinEvt == nil { + if eventStore.GetJoinEvent(el.RoomID, resp.EventID) == nil { return fmt.Errorf("couldn't process our own join event, %s", resp.EventID) } @@ -95,7 +120,7 @@ func (el *Election) JoinElection(client *mautrix.Client, eventStore *EventStore) defer el.Save() defer el.Unlock() - el.LocalVoter = NewLocalVoter(el.Joins[joinEvt.ID], privKey) + el.LocalVoter = NewLocalVoter(el.Joins[resp.EventID], privKey) return nil } @@ -134,14 +159,133 @@ func (el *Election) StartElection(client *mautrix.Client, eventStore *EventStore return err } - startEvt := eventStore.GetStartEvent(el.RoomID, resp.EventID) - if startEvt == nil { + if eventStore.GetStartEvent(el.RoomID, resp.EventID) == nil { return fmt.Errorf("couldn't process our own start event, %s", resp.EventID) } return nil } +func (el *Election) SendProvingKeys(client *mautrix.Client, eventStore *EventStore) error { + // eval proving key + var ( + evalProvingKeyURI id.ContentURI + evalVerifyingKey groth16.VerifyingKey + ) + { + var evalCircuit math.EvalCircuit + evalCircuit.Coeffs = make([]frontend.Variable, len(*el.FinalJoinIDs)-1) + evalCircuit.BallotBits = make([]frontend.Variable, len(el.Candidates)*len(el.Candidates)) + evalCircuit.HashSeed = el.LocalVoter.HashSeed + r1cs, err := frontend.Compile(gurvy.BLS381, &evalCircuit) + if err != nil { + return fmt.Errorf("couldn't compile eval circuit: %s", err) + } + var evalProvingKey groth16.ProvingKey + evalProvingKey, evalVerifyingKey, err = groth16.Setup(r1cs) + if err != nil { + return fmt.Errorf("couldn't setup r1cs: %s", err) + } + + var evalPkBytes bytes.Buffer + // TODO: change back to WriteTo. There seems to be a bug in + // gnark/gurvy (or a misunderstanding on my part) where + // decompression fails + // ignoring ...'s keys msg ($tsSEYHiouPdJlaTqAOG2q_4t9MZa0OGHtJPD95jOvAg) since couldn't read sum key from bytes buffer: invalid compressed coordinate: square root doesn't exist + // The file was successfully processed on one side (who + // coincidentally was the sender) but not on the other + // NOTE: it's plausible this was fixed in gurvy (now gnark-crypto) 0.4.0: + // + // point.SetBytes can now be called concurently with same byte slice input + // + // https://github.com/ConsenSys/gnark-crypto/releases/tag/v0.4.0 + evalProvingKey.WriteRawTo(&evalPkBytes) + uploadResp, err := client.UploadMedia(mautrix.ReqUploadMedia{ + Content: &evalPkBytes, + ContentLength: int64(evalPkBytes.Len()), + ContentType: "application/octet-stream", + }) + if err != nil { + return fmt.Errorf("couldn't upload eval proving key: %s", err) + } + evalProvingKeyURI = uploadResp.ContentURI + } + + // sum proving key + var ( + sumProvingKeyURI id.ContentURI + sumVerifyingKey groth16.VerifyingKey + ) + { + var sumCircuit math.SumCircuit + numVoters := len(*el.FinalJoinIDs) + hashSeeds := make([]string, numVoters) + for i, joinID := range *el.FinalJoinIDs { + hashSeeds[i] = el.Joins[joinID].HashSeed + } + sumCircuit.HashSeeds = hashSeeds + sumCircuit.HashSelects = make([]frontend.Variable, numVoters) + sumCircuit.OutputHashes = make([]frontend.Variable, numVoters) + sumCircuit.Outputs = make([]frontend.Variable, numVoters) + + r1cs, err := frontend.Compile(gurvy.BLS381, &sumCircuit) + if err != nil { + return fmt.Errorf("couldn't compile sum circuit: %s", err) + } + + var sumProvingKey groth16.ProvingKey + sumProvingKey, sumVerifyingKey, err = groth16.Setup(r1cs) + if err != nil { + return fmt.Errorf("couldn't setup r1cs: %s", err) + } + + var sumPkBytes bytes.Buffer + // TODO: see above + sumProvingKey.WriteRawTo(&sumPkBytes) + uploadResp, err := client.UploadMedia(mautrix.ReqUploadMedia{ + Content: &sumPkBytes, + ContentLength: int64(sumPkBytes.Len()), + ContentType: "application/octet-stream", + }) + if err != nil { + return fmt.Errorf("couldn't upload sum proving key: %s", err) + } + sumProvingKeyURI = uploadResp.ContentURI + } + + resp, err := client.SendMessageEvent(el.RoomID, KeysMessage, KeysMessageContent{ + Version: Version, + + EvalProvingKeyURI: evalProvingKeyURI, + JoinID: el.LocalVoter.JoinEvt.ID, + StartID: *el.StartID, + SumProvingKeyURI: sumProvingKeyURI, + }) + if err != nil { + return fmt.Errorf("couldn't send keys message event: %s", err) + } + + if eventStore.GetKeysEvent(el.RoomID, resp.EventID) == nil { + return fmt.Errorf("couldn't process our own keys event, %s", resp.EventID) + } + + if el.LocalVoter.EvalProvingKey == nil { + return fmt.Errorf("our eval proving key wasn't set during processing") + } + if el.LocalVoter.SumProvingKey == nil { + return fmt.Errorf("our sum proving key wasn't set during processing") + } + + el.Lock() + defer el.Save() + defer el.Unlock() + + el.LocalVoter.EvalVerifyingKey = &evalVerifyingKey + el.LocalVoter.SumVerifyingKey = &sumVerifyingKey + + return nil +} + func (el *Election) SendEvals(client *mautrix.Client, eventStore *EventStore) error { el.RLock() if el.StartID == nil { @@ -149,24 +293,77 @@ func (el *Election) SendEvals(client *mautrix.Client, eventStore *EventStore) er return errors.New("SendEvals called before election started") } - evals := make(map[id.EventID]string) - for _, joinID := range *el.FinalJoinIDs { + evals := make([]Eval, len(*el.FinalJoinIDs)) + keysIDs := make([]id.EventID, len(*el.FinalJoinIDs)) + for i, joinID := range *el.FinalJoinIDs { voter := el.Joins[joinID] - eval := el.LocalVoter.Poly.Eval(&voter.Input) - var nonce [24]byte - if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { + + output, poutputHash, proof := el.LocalVoter.Poly.EvalAndProve(&voter.Input, *voter.EvalProvingKey, voter.HashSeed) + + // encrypt output + var outputBytes [32]byte + var encryptedOutput []byte + { + var outputNonce [24]byte + if _, err := io.ReadFull(rand.Reader, outputNonce[:]); err != nil { + el.RUnlock() + return fmt.Errorf("couldn't read random bytes for output nonce: %s", err) + } + outputBytes = output.Bytes() + encryptedOutput = box.Seal(outputNonce[:], outputBytes[:], &outputNonce, &voter.PubKey, &el.LocalVoter.PrivKey) + } + + // hash output + outputHash, err := bls381.Sum(voter.HashSeed, outputBytes[:]) + if err != nil { el.RUnlock() - return err + return fmt.Errorf("couldn't hash output: %s", err) + } + + // hash and encrypt poutput + var encryptedPOutputHash []byte + { + var poutputNonce [24]byte + if _, err := io.ReadFull(rand.Reader, poutputNonce[:]); err != nil { + el.RUnlock() + return fmt.Errorf("couldn't read random bytes for poutput nonce: %s", err) + } + encryptedPOutputHash = box.Seal(poutputNonce[:], poutputHash, &poutputNonce, &voter.PubKey, &el.LocalVoter.PrivKey) + } + + // encrypt proof + var encryptedProof []byte + { + var proofNonce [24]byte + if _, err := io.ReadFull(rand.Reader, proofNonce[:]); err != nil { + el.RUnlock() + return fmt.Errorf("couldn't read random bytes for proof nonce: %s", err) + } + var proofBuffer bytes.Buffer + _, err := proof.WriteTo(&proofBuffer) + if err != nil { + el.RUnlock() + return fmt.Errorf("couldn't write evals proof to buffer: %s", err) + } + encryptedProof = box.Seal(proofNonce[:], proofBuffer.Bytes(), &proofNonce, &voter.PubKey, &el.LocalVoter.PrivKey) } - encrypted := box.Seal(nonce[:], eval.Bytes(), &nonce, &voter.PubKey, &el.LocalVoter.PrivKey) - evals[voter.JoinEvt.ID] = base64.StdEncoding.EncodeToString(encrypted) + + evals[i] = Eval{ + Output: base64.StdEncoding.EncodeToString(encryptedOutput), + OutputHash: base64.StdEncoding.EncodeToString(outputHash), + POutputHash: base64.StdEncoding.EncodeToString(encryptedPOutputHash), + Proof: base64.StdEncoding.EncodeToString(encryptedProof), + } + + keysIDs[i] = *voter.KeysID } resp, err := client.SendMessageEvent(el.RoomID, EvalsMessage, EvalsMessageContent{ Version: Version, + Evals: evals, JoinID: el.LocalVoter.JoinEvt.ID, - StartID: *el.StartID, + KeysIDs: keysIDs, }) el.RUnlock() if err != nil { @@ -188,17 +385,88 @@ func (el *Election) SendSum(client *mautrix.Client, eventStore *EventStore) erro return errors.New("SendSum called before local sum was calculated") } - var evalsIDs []id.EventID - for _, joinID := range *el.FinalJoinIDs { + numVoters := len(*el.FinalJoinIDs) + + evalsIDs := make([]id.EventID, numVoters) + hashSeeds := make([]string, numVoters) + for i, joinID := range *el.FinalJoinIDs { voter := el.Joins[joinID] - evalsIDs = append(evalsIDs, *voter.EvalsID) + evalsIDs[i] = *voter.EvalsID + hashSeeds[i] = voter.HashSeed } + var r1cs r1cs.R1CS + { + var circuit math.SumCircuit + circuit.HashSeeds = hashSeeds + circuit.HashSelects = make([]frontend.Variable, numVoters) + circuit.OutputHashes = make([]frontend.Variable, numVoters) + circuit.Outputs = make([]frontend.Variable, numVoters) + var err error + r1cs, err = frontend.Compile(gurvy.BLS381, &circuit) + if err != nil { + el.RUnlock() + return fmt.Errorf("couldn't compile sum circuit: %s", err) + } + } + + proofs := make([]string, numVoters) + { + hashSelects := make([]frontend.Variable, numVoters) + outputHashes := make([]frontend.Variable, numVoters) + outputs := make([]frontend.Variable, numVoters) + + var witness math.SumCircuit + witness.HashSeeds = hashSeeds + witness.HashSelects = hashSelects + witness.OutputHashes = outputHashes + witness.Sum.Assign(el.LocalVoter.Sum) + witness.Outputs = outputs + + ourJoinIDIndex := *el.LocalVoter.JoinIDIndex + for i, joinID := range *el.FinalJoinIDs { + if i == ourJoinIDIndex { + hashSelects[i].Assign(1) + } else { + hashSelects[i].Assign(0) + } + evaler := el.Joins[joinID] + outputHashForUs := (*evaler.OutputHashes)[ourJoinIDIndex] + outputHashes[i].Assign(outputHashForUs) + outputs[i].Assign(evaler.Output) + } + + for i, joinID := range *el.FinalJoinIDs { + voter := *el.Joins[joinID] + proof, err := groth16.Prove(r1cs, *voter.SumProvingKey, &witness) + if err != nil { + el.RUnlock() + return fmt.Errorf("couldn't prove sum: %s", err) + } + var nonce [24]byte + if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { + el.RUnlock() + return fmt.Errorf("couldn't read random bytes for sum proof nonce: %s", err) + } + var buf bytes.Buffer + _, err = proof.WriteTo(&buf) + if err != nil { + el.RUnlock() + return fmt.Errorf("couldn't write sum proof to buffer: %s", err) + } + encryptedProof := box.Seal(nonce[:], buf.Bytes(), &nonce, &voter.PubKey, &el.LocalVoter.PrivKey) + proofs[i] = base64.StdEncoding.EncodeToString(encryptedProof) + } + } + + sumBytes := el.LocalVoter.Sum.Bytes() resp, err := client.SendMessageEvent(el.RoomID, SumMessage, SumMessageContent{ Version: Version, + EvalsIDs: evalsIDs, JoinID: el.LocalVoter.JoinEvt.ID, - Sum: base64.StdEncoding.EncodeToString(el.LocalVoter.Sum.Bytes()), + Sum: base64.StdEncoding.EncodeToString(sumBytes[:]), + Proofs: proofs, }) el.RUnlock() if err != nil { diff --git a/go.mod b/go.mod index 82bfdbf..b7efddc 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,9 @@ go 1.13 require ( github.com/cbergoon/merkletree v0.2.0 + github.com/consensys/gnark v0.3.8 + github.com/consensys/gurvy v0.3.8 github.com/gdamore/tcell/v2 v2.1.0 - github.com/kr/pretty v0.1.0 // indirect github.com/kyoh86/xdg v1.2.0 github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magefile/mage v1.11.0 // indirect diff --git a/go.sum b/go.sum index 2255a94..29fb2e5 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,48 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= +github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= +github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= +github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= +github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= +github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= +github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= 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-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= @@ -11,115 +55,473 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE 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/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= +github.com/consensys/bavard v0.1.7/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= +github.com/consensys/bavard v0.1.8-0.20210105233146-c16790d2aa8b/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= +github.com/consensys/gnark v0.3.8 h1:4t8tdqDLOAWRRycw4dsmI29v+ESii0up+vHAekn/Sgw= +github.com/consensys/gnark v0.3.8/go.mod h1:ZBBVbSG3McqjLh6ek5sHu/CXbQhx5bQJFxfqV7YR8Es= +github.com/consensys/goff v0.3.8/go.mod h1:sVd8qmGLPDVaNkeEc54IvC7/dGxuZdJBbo792ZLD5Ng= +github.com/consensys/goff v0.3.10/go.mod h1:xTldOBEHmFiYS0gPXd3NsaEqZWlnmeWcRLWgD3ba3xc= +github.com/consensys/gurvy v0.3.6/go.mod h1:PoRINoNBSpC1+qDcIgtH3EMHJkq+3CM1BGGcovuuOcU= +github.com/consensys/gurvy v0.3.8 h1:H2hvjvT2OFMgdMn5ZbhXqHt+F8DJ2clZW7Vmc0kFFxc= +github.com/consensys/gurvy v0.3.8/go.mod h1:sN75xnsiD593XnhbhvG2PkOy194pZBzqShWF/kwuW/g= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 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/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= +github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= +github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/ethereum/go-ethereum v1.9.24/go.mod h1:JIfVb6esrqALTExdz9hRYvrP0xBDf6wCncIu1hNwHpM= +github.com/ethereum/go-ethereum v1.9.25/go.mod h1:vMkFiYLHI4tgPw4k2j4MHKoovchFE8plZ0M9VMk4/oM= +github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fxamacker/cbor/v2 v2.2.0 h1:6eXqdDDe588rSYAi1HfZKbx6YYQO4mxQ9eC6xYpU/JQ= +github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= 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/v2 v2.0.1-0.20201017141208-acf90d56d591 h1:0WWUDZ1oxq7NxVyGo8M3KI5jbkiwNAdZFFzAdC68up4= github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA= github.com/gdamore/tcell/v2 v2.1.0 h1:UnSmozHgBkQi2PGsFr+rpdXuAPRRucMegpQp3Z3kDro= github.com/gdamore/tcell/v2 v2.1.0/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +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.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +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/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= +github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/kilic/bls12-381 v0.0.0-20201104083100-a288617c07f1/go.mod h1:gcwDl9YLyNc3H3wmPXamu+8evD8TYUa6BjTsWnvdn7A= +github.com/kilic/bls12-381 v0.0.0-20201226121925-69dacb279461/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +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/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 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= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 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/leanovate/gopter v0.2.8/go.mod h1:gNcbPWNEWRe4lm+bycKqxUYoH5uoVje5SkOJ3uoLer8= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -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/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/magefile/mage v1.10.0 h1:3HiXzCUY12kh9bIuyXShaVe529fJfyqoVM42o/uom2g= github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magefile/mage v1.11.0 h1:C/55Ywp9BpgVVclD3lRnSYCwXTYxmSppIgLeDYlNuls= github.com/magefile/mage v1.11.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= +github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= +github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 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.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rivo/tview v0.0.0-20210217110421-8a8f78a6dd01 h1:rtCzDXdaqhiRakJsz0bUj+3sOUjw82bJDcJrAzQ0u+M= github.com/rivo/tview v0.0.0-20210217110421-8a8f78a6dd01/go.mod h1:n2q/ydglZJ1kqxiNrnYO+FaX1H14vA0wKyIo953QakU= -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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.8.0 h1:nfhvjKcUMhBMVqbKHJlk5RPrrfYr/NMo3692g0dwfWU= github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/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 v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= +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/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= +github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw= +github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU= 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/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/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/tidwall/gjson v1.6.8/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI= github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/sjson v1.1.5/go.mod h1:VuJzsZnTowhSxWdOgsAnb886i4AjEyTkk7tNtsL7EYE= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +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-20181029021203-45a5f77698d3/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/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-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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d h1:1aflnvSoWWLI2k/dMUAl5lvU1YO4Mb4hz0gh+1rjcxU= golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 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-20181221193216-37e7f081c4d4/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-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191025090151-53bf42e6b339/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210105210732-16f7687f5001/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210218155724-8ebf48af031b h1:lAZ0/chPUDWwjqosYR0X4M490zQhMsiJ4K3DbA7o+3g= golang.org/x/sys v0.0.0-20210218155724-8ebf48af031b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +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-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/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +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/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/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-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= +gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= maunium.net/go/maulogger/v2 v2.2.2/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A= maunium.net/go/mautrix v0.8.3 h1:nKGdARVCf2w7thEN5GEbAjYrlLjKLX44jOdB1h+BV7U= maunium.net/go/mautrix v0.8.3/go.mod h1:LPbb/DeAmtOPKnGbJazL9g11cO3mMAaEbLE8udd98BU= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/math/lagrange.go b/math/lagrange.go new file mode 100644 index 0000000..07baae4 --- /dev/null +++ b/math/lagrange.go @@ -0,0 +1,31 @@ +package math + +import "github.com/consensys/gurvy/bls381/fr" + +// p[0] is x coordinate, p[1] is y coordinate +type Point [2]*fr.Element + +// uses https://en.wikipedia.org/wiki/Lagrange_polynomial#Definition but returns +// the evaluation at zero (the only one we care about) +func LagrangeEvalAtZero(points []Point) *fr.Element { + zero := new(fr.Element).SetZero() + + sum := new(fr.Element).SetZero() + tmp := new(fr.Element) + + for j, pointj := range points { + term := new(fr.Element).Set(pointj[1]) + for k, pointk := range points { + if k == j { + continue + } + // numerator + term.Mul(term, tmp.Sub(zero, pointk[0])) + // denominator + term.Div(term, tmp.Sub(pointj[0], pointk[0])) + } + sum.Add(sum, term) + } + + return sum +} diff --git a/math/lagrange_test.go b/math/lagrange_test.go new file mode 100644 index 0000000..d98391f --- /dev/null +++ b/math/lagrange_test.go @@ -0,0 +1,81 @@ +package math + +import ( + "testing" + + "github.com/consensys/gurvy/bls381/fr" +) + +func TestWikipedia(t *testing.T) { + // https://en.wikipedia.org/wiki/Lagrange_polynomial#Example_1 + points := make([]Point, 3) + points[0][0] = new(fr.Element).SetUint64(1) + points[0][1] = new(fr.Element).SetUint64(1) + points[1][0] = new(fr.Element).SetUint64(2) + points[1][1] = new(fr.Element).SetUint64(4) + points[2][0] = new(fr.Element).SetUint64(3) + points[2][1] = new(fr.Element).SetUint64(9) + r := LagrangeEvalAtZero(points) + if !r.Equal(new(fr.Element).SetZero()) { + t.Fail() + } + + // https://en.wikipedia.org/wiki/Lagrange_polynomial#Example_2 + points = make([]Point, 4) + points[0][0] = new(fr.Element).SetUint64(1) + points[0][1] = new(fr.Element).SetUint64(1) + points[1][0] = new(fr.Element).SetUint64(2) + points[1][1] = new(fr.Element).SetUint64(8) + points[2][0] = new(fr.Element).SetUint64(3) + points[2][1] = new(fr.Element).SetUint64(27) + points[3][0] = new(fr.Element).SetUint64(4) + points[3][1] = new(fr.Element).SetUint64(64) + if !r.Equal(new(fr.Element).SetZero()) { + t.Fail() + } +} + +func TestRandomPoly(t *testing.T) { + // a simple ballot where the first candidate is ranked above the second + ballot := make([][]byte, 2) + ballot[0] = make([]byte, 2) + ballot[1] = make([]byte, 2) + ballot[0][1] = 1 + + // pinput doesn't matter +genpinput: + pinput, err := new(fr.Element).SetRandom() + if err != nil { + panic(err) + } + if pinput.IsZero() { + goto genpinput + } + + zero := new(fr.Element).SetZero() + // vary the number of points + for n := 2; n <= 20; n++ { + poly := NewRandomPoly(ballot, n, pinput) + points := make([]Point, n) + for i := 0; i < n; i++ { + genx: + x, err := new(fr.Element).SetRandom() + // TODO: check that x doesn't repeat (very unlikely) + if err != nil { + panic(err) + } + if x.IsZero() { + goto genx + } + y := poly.eval(x) + points[i][0] = x + points[i][1] = y + } + lagrangeZeroOutput := LagrangeEvalAtZero(points) + actualZeroOutput := poly.eval(zero) + if lagrangeZeroOutput.Cmp(actualZeroOutput) != 0 { + t.Fatalf("lagrange zero output %s does not equal %s", + lagrangeZeroOutput, actualZeroOutput) + } + } +} diff --git a/math/linalg.go b/math/linalg.go deleted file mode 100644 index 9ed2b17..0000000 --- a/math/linalg.go +++ /dev/null @@ -1,73 +0,0 @@ -package math - -import ( - "math/big" -) - -type Matrix [][]big.Rat - -func (M Matrix) Rows() int { - return len(M) -} - -func (M Matrix) Cols() int { - if len(M) == 0 { - return 0 - } - return len(M[0]) -} - -func (M Matrix) RREF() { - lead := 0 - zero := big.NewRat(0, 1) - - for r := 0; r < M.Rows(); r++ { - if lead >= M.Cols() { - return - } - i := r - for M[i][lead].Cmp(zero) == 0 { - i++ - if M.Rows() == i { - i = r - lead++ - if M.Cols() == lead { - return - } - } - } - M[i], M[r] = M[r], M[i] - f := &big.Rat{} - f.Inv(&M[r][lead]) - for j := range M[r] { - M[r][j].Mul(&M[r][j], f) - } - for i = 0; i < M.Rows(); i++ { - if i != r { - f.Set(&M[i][lead]) - for j, e := range M[r] { - M[i][j].Sub(&M[i][j], new(big.Rat).Mul(&e, f)) - } - } - } - lead++ - } -} - -// assumes `M' and `other' are valid matrices -func (M Matrix) Equals(other Matrix) bool { - if len(M) != len(other) { - return false - } - if len(M) > 0 && len(M[0]) != len(other[0]) { - return false - } - for r := range M { - for c := range M[r] { - if M[r][c].Cmp(&other[r][c]) != 0 { - return false - } - } - } - return true -} diff --git a/math/linalg_test.go b/math/linalg_test.go deleted file mode 100644 index 54d7e28..0000000 --- a/math/linalg_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package math - -import ( - "math/big" - "testing" -) - -func TestRREF(t *testing.T) { - mat := Matrix{ - {*big.NewRat( 1, 1), *big.NewRat(2, 1), *big.NewRat(-1, 1), *big.NewRat( -4, 1)}, - {*big.NewRat( 2, 1), *big.NewRat(3, 1), *big.NewRat(-1, 1), *big.NewRat(-11, 1)}, - {*big.NewRat(-2, 1), *big.NewRat(0, 1), *big.NewRat(-3, 1), *big.NewRat( 22, 1)}, - } - mat.RREF() - result := Matrix{ - {*big.NewRat(1, 1), *big.NewRat(0, 1), *big.NewRat(0, 1), *big.NewRat(-8, 1)}, - {*big.NewRat(0, 1), *big.NewRat(1, 1), *big.NewRat(0, 1), *big.NewRat( 1, 1)}, - {*big.NewRat(0, 1), *big.NewRat(0, 1), *big.NewRat(1, 1), *big.NewRat(-2, 1)}, - } - if !mat.Equals(result) { - t.Fail() - } - - mat = Matrix{ - {*big.NewRat(1, 1), *big.NewRat(3, 1), *big.NewRat(-1, 1)}, - {*big.NewRat(0, 1), *big.NewRat(1, 1), *big.NewRat(7, 1)}, - } - mat.RREF() - result = Matrix{ - {*big.NewRat(1, 1), *big.NewRat(0, 1), *big.NewRat(-22, 1)}, - {*big.NewRat(0, 1), *big.NewRat(1, 1), *big.NewRat( 7, 1)}, - } - if !mat.Equals(result) { - t.Fail() - } -} diff --git a/math/poly.go b/math/poly.go index 1bdd25a..f54f755 100644 --- a/math/poly.go +++ b/math/poly.go @@ -1,69 +1,166 @@ package math import ( - "crypto/rand" - "math" - "math/big" + "fmt" + + "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/backend/r1cs" + "github.com/consensys/gnark/crypto/hash/mimc/bls381" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gurvy" + "github.com/consensys/gurvy/bls381/fr" ) type Poly struct { - Constant *big.Int `json:"constant"` - Coefs []*big.Int `json:"coefs"` + Ballot [][]byte `json:"ballot"` + Coeffs []*fr.Element `json:"coeffs"` + PInput *fr.Element `json:"pinput"` + + cache *polyCache `json:"-"` } -func RandomBigInt(numBytes uint, allowAllZeros bool) (*big.Int, error) { - randBytes := make([]byte, numBytes) - _, err := rand.Read(randBytes) - if err != nil { - return nil, err - } - bi := new(big.Int).SetBytes(randBytes) - if !allowAllZeros && bi.Cmp(big.NewInt(0)) == 0 { - return RandomBigInt(numBytes, allowAllZeros) - } - return bi, nil +type polyCache struct { + ballotBits []*fr.Element + constant *fr.Element + outputs map[*fr.Element]outputCache + poutput *fr.Element } -func NewPoly(coefficients []*big.Int, ballot []byte) *Poly { - constant := new(big.Int).SetBytes(ballot) - return &Poly{constant, coefficients} +type outputCache struct{ + output *fr.Element + poutputHash []byte + proof groth16.Proof } -// NewRandomPoly generates a random `degree' degree polynomial, using at least -// `entropy' bits of entropy for the random coefficients and inserting `ballot' -// as the constant term. -func NewRandomPoly(degree uint, entropy uint, ballot []byte) *Poly { - coefficients := make([]*big.Int, degree) +// NewRandomPoly generates a random polynomial of degree numVoters-1, using +// `ballot' as the constant term +func NewRandomPoly(ballot [][]byte, numVoters int, pinput *fr.Element) *Poly { + coeffs := make([]*fr.Element, numVoters - 1) + + var err error + for i := range coeffs { + coeffs[i], err = new(fr.Element).SetRandom() + if err != nil { + panic(err) + } + } + + return &Poly{ballot, coeffs, pinput, nil} +} - // number of bits per coefficient - numBits := uint(math.Ceil(float64(entropy) / float64(degree))) +func (p *Poly) setupCache() { + p.cache = &polyCache{} - // number of bytes per coefficient - numBytes := numBits / 8 - if numBits%8 > 0 { - numBytes += 1 + // lay out ballot bits slice and compute constant term + n := len(p.Ballot) + ballotBits := make([]*fr.Element, n*n) + ballot1DBytes := make([]byte, 0, n*n) + for i, row := range p.Ballot { + ballot1DBytes = append(ballot1DBytes, row...) + for j, bool := range row { + if bool == 0 { + ballotBits[i * n + j] = new(fr.Element).SetZero() + } else if bool == 1 { + ballotBits[i * n + j] = new(fr.Element).SetOne() + } else { + panic(fmt.Sprintf("invalid ballot byte value: %d", bool)) + } + } } + p.cache.ballotBits = ballotBits + p.cache.constant = new(fr.Element).SetBytes(ballot1DBytes) - var err error - for i := range coefficients { - coefficients[i], err = RandomBigInt(numBytes, false) + // malloc outputs map + p.cache.outputs = make(map[*fr.Element]outputCache) + + // calculate verifier's output + poutput := new(fr.Element).Set(p.cache.constant) + term := new(fr.Element) + for i, coeff := range p.Coeffs { + term.Set(coeff) + // <= because we want there to be at least one multiplication + for j := 0; j <= i; j++ { + term.Mul(term, p.PInput) + } + poutput.Add(poutput, term) + } + p.cache.poutput = poutput + +} + +func (p *Poly) EvalAndProve(input *fr.Element, provingKey groth16.ProvingKey, hashSeed string) (*fr.Element, []byte, groth16.Proof) { + if p.cache == nil { + p.setupCache() + } else if outputCache, exists := p.cache.outputs[input]; exists { + return outputCache.output, outputCache.poutputHash, outputCache.proof + } + + // compile R1CS + var r1cs r1cs.R1CS + { + var circuit EvalCircuit + circuit.Coeffs = make([]frontend.Variable, len(p.Coeffs)) + circuit.BallotBits = make([]frontend.Variable, len(p.Ballot)*len(p.Ballot[0])) + circuit.HashSeed = hashSeed + var err error + r1cs, err = frontend.Compile(gurvy.BLS381, &circuit) if err != nil { panic(err) } } - return NewPoly(coefficients, ballot) -} + output := p.eval(input) -func (p *Poly) Eval(input *big.Int) *big.Int { - res := new(big.Int).Set(p.Constant) + var witness EvalCircuit + // public + witness.Input.Assign(input) + witness.Output.Assign(output) + witness.PInput.Assign(p.PInput) + var poutputHash []byte + { + poutputBytes := p.cache.poutput.Bytes() + var err error + poutputHash, err = bls381.Sum(hashSeed, poutputBytes[:]) + if err != nil { + panic(err) + } + witness.POutputHash.Assign(poutputHash) + } + witness.HashSeed = hashSeed + // private + witness.BallotBits = make([]frontend.Variable, len(p.Ballot)*len(p.Ballot[0])) + for i, bit := range p.cache.ballotBits { + witness.BallotBits[i].Assign(bit) + } + witness.Coeffs = make([]frontend.Variable, len(p.Coeffs)) + for i, coeff := range p.Coeffs { + witness.Coeffs[i].Assign(coeff) + } + witness.Constant.Assign(p.cache.constant) + witness.POutput.Assign(p.cache.poutput) - for i, coef := range p.Coefs { - degree := big.NewInt(int64(i + 1)) - term := new(big.Int).Exp(input, degree, nil) - term.Mul(term, coef) - res.Add(res, term) + proof, err := groth16.Prove(r1cs, provingKey, &witness) + if err != nil { + panic(err) } - return res + p.cache.outputs[input] = outputCache{output, poutputHash, proof} + return output, poutputHash, proof +} + +func (p *Poly) eval(input *fr.Element) *fr.Element { + if p.cache == nil { + p.setupCache() + } + output := new(fr.Element).Set(p.cache.constant) + term := new(fr.Element) + for i, coeff := range p.Coeffs { + term.Set(coeff) + // <= because we want there to be at least one multiplication + for j := 0; j <= i; j++ { + term.Mul(term, input) + } + output.Add(output, term) + } + return output } diff --git a/math/zk.go b/math/zk.go new file mode 100644 index 0000000..a612040 --- /dev/null +++ b/math/zk.go @@ -0,0 +1,117 @@ +package math + +import ( + "errors" + + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/hash/mimc" + "github.com/consensys/gurvy" +) + +type EvalCircuit struct { + Input frontend.Variable `gnark:",public"` + Output frontend.Variable `gnark:",public"` + // prover's input + PInput frontend.Variable `gnark:",public"` + POutputHash frontend.Variable `gnark:",public"` + HashSeed string + + // must be created with length numCandidates^2 + BallotBits []frontend.Variable `gnark:",private"` + // must be created with length numVoters - 1 + Coeffs []frontend.Variable `gnark:",private"` + Constant frontend.Variable `gnark:",private"` + // prover's hidden output for their own polynomial + POutput frontend.Variable `gnark:",private"` +} + +func (circuit *EvalCircuit) Define(curveID gurvy.ID, cs *frontend.ConstraintSystem) error { + // prove the input yields the given output + output := circuit.Constant + poutput := circuit.Constant + for i, coeff := range circuit.Coeffs { + term := coeff + vterm := coeff + // <= because we want there to be at least one multiplication + for j := 0; j <= i; j++ { + term = cs.Mul(term, circuit.Input) + vterm = cs.Mul(vterm, circuit.PInput) + } + output = cs.Add(output, term) + poutput = cs.Add(poutput, vterm) + } + cs.AssertIsEqual(output, circuit.Output) + cs.AssertIsEqual(poutput, circuit.POutput) + + // prove the constant is valid (i.e. a string of bits separated by 7 + // zeros) + // TODO: support more than 255 voters (limit from usage of byte) + zero := cs.Constant(0) + shift := cs.Constant(1 << 8) + slot := cs.Constant(1) + constructedConstant := zero + for i := len(circuit.BallotBits)-1; i >= 0; i-- { + bit := circuit.BallotBits[i] + cs.AssertIsBoolean(bit) + constructedConstant = cs.Add(constructedConstant, cs.Select(bit, slot, zero)) + slot = cs.Mul(slot, shift) + } + cs.AssertIsEqual(constructedConstant, circuit.Constant) + + mimc, err := mimc.NewMiMC(circuit.HashSeed, gurvy.BLS381) + if err != nil { + return err + } + + cs.AssertIsEqual(mimc.Hash(cs, circuit.POutput), circuit.POutputHash) + + return nil +} + +type SumCircuit struct { + HashSeeds []string + HashSelects []frontend.Variable `gnark:",public"` + OutputHashes []frontend.Variable `gnark:",public"` + Sum frontend.Variable `gnark:",public"` + + Outputs []frontend.Variable `gnark:",private"` +} + +func (circuit *SumCircuit) Define(curveID gurvy.ID, cs *frontend.ConstraintSystem) error { + if len(circuit.HashSeeds) != len(circuit.HashSelects) { + return errors.New("len(circuit.HashSeeds) != len(circuit.HashSelects)") + } + if len(circuit.HashSelects) != len(circuit.OutputHashes) { + return errors.New("len(circuit.HashSelects) != len(circuit.OutputHashes)") + } + if len(circuit.OutputHashes) != len(circuit.Outputs) { + return errors.New("len(circuit.OutputHashes) != len(circuit.Outputs)") + } + + // prove the secret outputs hash to public hashes + zero := cs.Constant(0) + // TODO: this seems verify inefficient + for i, hashSeed := range circuit.HashSeeds { + mimc, err := mimc.NewMiMC(hashSeed, gurvy.BLS381) + if err != nil { + return err + } + bit := circuit.HashSelects[i] + for j, output := range circuit.Outputs { + cs.AssertIsEqual( + cs.Select(bit, mimc.Hash(cs, output), zero), + cs.Select(bit, circuit.OutputHashes[j], zero), + ) + } + } + + // prove that sum of outputs yields Sum + sum := zero + for _, output := range circuit.Outputs { + sum = cs.Add(sum, output) + } + + cs.AssertIsEqual(sum, circuit.Sum) + + return nil +} diff --git a/ui/tui.go b/ui/tui.go index e526310..a7596ab 100644 --- a/ui/tui.go +++ b/ui/tui.go @@ -232,8 +232,10 @@ func CreateElectionTUI() (title string, candidates []election.Candidate) { n := 2 app := newTallyardApplication() plus := func() { - form.AddInputField(fmt.Sprintf("%d.", n+1), "", 50, nil, nil) - n++ + if n < 5 { + form.AddInputField(fmt.Sprintf("%d.", n+1), "", 50, nil, nil) + n++ + } } minus := func() { // TODO: ensure from joiner that there are at least two @@ -274,7 +276,7 @@ func CreateElectionTUI() (title string, candidates []election.Candidate) { return title, candidates } -func ElectionWaitTUI(client *mautrix.Client, el *election.Election, eventStore *election.EventStore) error { +func ElectionWaitTUI(client *mautrix.Client, el *election.Election, eventStore *election.EventStore) (err error) { votersTextView := tview.NewTextView() frame := tview.NewFrame(votersTextView) frame.SetTitle(el.Title).SetBorder(true) @@ -286,20 +288,21 @@ func ElectionWaitTUI(client *mautrix.Client, el *election.Election, eventStore * if event.Key() == tcell.KeyEnter { frame.Clear() frame.AddText("Starting election...", false, tview.AlignCenter, tcell.ColorWhite) - err := el.StartElection(client, eventStore) - if err != nil { - panic(err) - } + err = el.StartElection(client, eventStore) } return event }) } else { frame.AddText("Waiting for election to start...", false, tview.AlignCenter, tcell.ColorWhite) } + if err != nil { + return + } el.RUnlock() update := func() { el.RLock() // TODO: handle duplicate joins from one UserID + // TODO: handle users who joined after cutoff voters := make([]string, 0, len(el.Joins)) for _, voter := range el.Joins { voters = append(voters, voter.JoinEvt.Sender.String()) @@ -311,28 +314,59 @@ func ElectionWaitTUI(client *mautrix.Client, el *election.Election, eventStore * votersTextView.SetText(text) } go func() { + var electionStarted bool for app.alive { - app.QueueUpdateDraw(update) - - // has the election started? el.RLock() - started := el.StartID != nil - el.RUnlock() - if started { - app.Stop() - break + if electionStarted { + // have we received everyone's keys? + receivedAllKeys := true + for _, joinID := range *el.FinalJoinIDs { + if el.Joins[joinID].KeysID == nil { + receivedAllKeys = false + break + } + } + el.RUnlock() + if receivedAllKeys { + app.Stop() + return + } + } else if el.FinalJoinIDs != nil { + electionStarted = true + el.RUnlock() + if el.LocalVoter.KeysID == nil { + err = el.SendProvingKeys(client, eventStore) + if err != nil { + app.Stop() + return + } + } + app.QueueUpdateDraw(func() { + frame.Clear() + frame.AddText("Waiting for everyone's keys...", false, tview.AlignCenter, tcell.ColorWhite) + update() + }) + } else { + el.RUnlock() + app.QueueUpdateDraw(update) } time.Sleep(1 * time.Second) } }() app.SetRoot(frame, true) - err := app.Run() - return err + err2 := app.Run() + if err != nil { + return err + } + if err2 != nil { + return err2 + } + return nil } // displays a voting UI to the user and returns the encoded ballot -func VoteTUI(candidates []election.Candidate) (ballot *[]byte, err error) { +func VoteTUI(candidates []election.Candidate) (ballot *[][]byte, err error) { app := newTallyardApplication() form := tview.NewForm() form.SetTitle("Vote").SetBorder(true) @@ -398,17 +432,15 @@ func (t *tallyardApplication) Run() error { return err } -func getBallotFromRankings(ranks []int) []byte { +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) + sort.Sort(&candidateRanking{candidates, ranks}) // TODO: support more than 255 voters (limit from usage of byte) prefMatrix := make([][]byte, n) @@ -431,13 +463,7 @@ func getBallotFromRankings(ranks []int) []byte { 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 + return prefMatrix } type candidateRanking struct { -- 2.38.4