From c9687894af04d3c0d4d4ebd86942a7e566bd3242 Mon Sep 17 00:00:00 2001 From: David Florness Date: Sun, 13 Mar 2022 20:58:27 -0400 Subject: [PATCH] Speed up marshalling by storing keys in separate files --- election/map.go | 2 +- election/marshal.go | 115 +++++++++++++++++++++++++++------------ election/marshal_test.go | 16 +++--- election/msg.go | 45 ++++++--------- election/voter.go | 18 +++--- go.sum | 4 ++ 6 files changed, 119 insertions(+), 81 deletions(-) diff --git a/election/map.go b/election/map.go index 48f6699..3613135 100644 --- a/election/map.go +++ b/election/map.go @@ -29,7 +29,7 @@ type ElectionsMap struct { save func(*ElectionsMap) `json:"-"` } -const electionsMapVersion = 6 +const electionsMapVersion = 7 func NewElectionsMap(userID id.UserID, save func(*ElectionsMap)) *ElectionsMap { return &ElectionsMap{ diff --git a/election/marshal.go b/election/marshal.go index 08d3eb4..ea3acce 100644 --- a/election/marshal.go +++ b/election/marshal.go @@ -1,67 +1,110 @@ package election import ( - "bytes" - "encoding/base64" + "os" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/backend/groth16" + "github.com/kyoh86/xdg" + log "github.com/sirupsen/logrus" + "maunium.net/go/mautrix/appservice" ) -type MarshallableProvingKey struct { - Pk groth16.ProvingKey +func init() { + os.MkdirAll(xdg.DataHome() + "/tallyard/files", 0700) } -func (t *MarshallableProvingKey) UnmarshalJSON(b []byte) error { - var buf bytes.Buffer - _, err := buf.Write(bytes.Trim(b, "\"")) +func filePath(fileID string) string { + return xdg.DataHome() + "/tallyard/files/" + fileID +} + +type ProvingKeyFile struct { + FileID string `json:"file_id"` + pk groth16.ProvingKey `json:"-"` +} + +func NewProvingKeyFile(fileID string, pk groth16.ProvingKey) *ProvingKeyFile { + filePath := filePath(fileID) + file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600) if err != nil { - return err + log.Errorf("couldn't open file %s; this proving key will be discarded upon exit: %s", filePath, err) + return &ProvingKeyFile{"", pk} + } + _, err = pk.WriteTo(file) + if err != nil { + log.Errorf("couldn't write proving key to file %s; this proving will be discarded upon exit: %s", filePath, err) + return &ProvingKeyFile{"", pk} + } + return &ProvingKeyFile{ + FileID: fileID, + pk: pk, } - t.Pk = groth16.NewProvingKey(ecc.BLS12_381) - _, err = t.Pk.ReadFrom(base64.NewDecoder(base64.StdEncoding, &buf)) - return err } -func (t *MarshallableProvingKey) MarshalJSON() ([]byte, error) { - var buf bytes.Buffer - encoder := base64.NewEncoder(base64.StdEncoding, &buf) - _, err := t.Pk.WriteTo(encoder) +func (provingKey *ProvingKeyFile) Pk() groth16.ProvingKey { + if provingKey.pk != nil { + return provingKey.pk + } + + filePath := filePath(provingKey.FileID) + file, err := os.Open(filePath) if err != nil { - return nil, err + log.Errorf("couldn't open proving key file %s: %s", filePath, err) + return nil } - err = encoder.Close() + pk := groth16.NewProvingKey(ecc.BLS12_381) + _, err = pk.ReadFrom(file) if err != nil { - return nil, err + log.Errorf("couldn't read proving key file %s: %s", filePath, err) + return nil } - return append([]byte{'"'}, append(buf.Bytes(), '"')...), nil + + provingKey.pk = pk + return pk } -type MarshallableVerifyingKey struct { - Vk groth16.VerifyingKey +type VerifyingKeyFile struct { + FileID string `json:"file_id"` + vk groth16.VerifyingKey `json:"-"` } -func (t *MarshallableVerifyingKey) UnmarshalJSON(b []byte) error { - var buf bytes.Buffer - _, err := buf.Write(bytes.Trim(b, "\"")) +func NewVerifyingKeyFile(vk groth16.VerifyingKey) *VerifyingKeyFile { + fileID := appservice.RandomString(24) + filePath := filePath(fileID) + file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600) + if err != nil { + log.Errorf("couldn't open file %s; this verifying key will be discarded upon exit: %s", filePath, err) + return &VerifyingKeyFile{"", vk} + } + _, err = vk.WriteTo(file) if err != nil { - return err + log.Errorf("couldn't write verifying key to file %s; this proving will be discarded upon exit: %s", filePath, err) + return &VerifyingKeyFile{"", vk} + } + return &VerifyingKeyFile{ + FileID: fileID, + vk: vk, } - t.Vk = groth16.NewVerifyingKey(ecc.BLS12_381) - _, err = t.Vk.ReadFrom(base64.NewDecoder(base64.StdEncoding, &buf)) - return err } -func (t *MarshallableVerifyingKey) MarshalJSON() ([]byte, error) { - var buf bytes.Buffer - encoder := base64.NewEncoder(base64.StdEncoding, &buf) - _, err := t.Vk.WriteRawTo(encoder) +func (verifyingKey *VerifyingKeyFile) Vk() groth16.VerifyingKey { + if verifyingKey.vk != nil { + return verifyingKey.vk + } + + filePath := filePath(verifyingKey.FileID) + file, err := os.Open(filePath) if err != nil { - return nil, err + log.Errorf("couldn't open verifying key file %s: %s", filePath, err) + return nil } - err = encoder.Close() + vk := groth16.NewVerifyingKey(ecc.BLS12_381) + _, err = vk.ReadFrom(file) if err != nil { - return nil, err + log.Errorf("couldn't read verifying key file %s: %s", filePath, err) + return nil } - return append([]byte{'"'}, append(buf.Bytes(), '"')...), nil + + verifyingKey.vk = vk + return vk } diff --git a/election/marshal_test.go b/election/marshal_test.go index 5758850..9b18c46 100644 --- a/election/marshal_test.go +++ b/election/marshal_test.go @@ -122,9 +122,9 @@ func randomLocalVoter(t *testing.T) *LocalVoter { } // EvalProvingKey - localVoter.EvalProvingKey = &MarshallableProvingKey{evalProvingKey} + localVoter.EvalProvingKey = NewProvingKeyFile("a", evalProvingKey) // EvalVerifyingKey - localVoter.EvalVerifyingKey = &MarshallableVerifyingKey{evalVerifyingKey} + localVoter.EvalVerifyingKey = NewVerifyingKeyFile(evalVerifyingKey) } { @@ -137,9 +137,9 @@ func randomLocalVoter(t *testing.T) *LocalVoter { t.Fatal(err) } // SumProvingKey - localVoter.SumProvingKey = &MarshallableProvingKey{sumProvingKey} + localVoter.SumProvingKey = NewProvingKeyFile("b", sumProvingKey) // SumVerifyingKey - localVoter.SumVerifyingKey = &MarshallableVerifyingKey{sumVerifyingKey} + localVoter.SumVerifyingKey = NewVerifyingKeyFile(sumVerifyingKey) } ballot := make([][]byte, numCandidates) @@ -226,19 +226,19 @@ func ensureEqual(t *testing.T, lv1 *LocalVoter, lv2 *LocalVoter) { } // EvalProvingKey - if lv2.EvalProvingKey.Pk.IsDifferent(lv1.EvalProvingKey.Pk) { + if lv2.EvalProvingKey.Pk().IsDifferent(lv1.EvalProvingKey.Pk()) { t.Error("eval proving keys not equal") } // EvalVerifyingKey - if lv2.EvalVerifyingKey.Vk.IsDifferent(lv1.EvalVerifyingKey.Vk) { + if lv2.EvalVerifyingKey.Vk().IsDifferent(lv1.EvalVerifyingKey.Vk()) { t.Error("eval verifying keys not equal") } // SumProvingKey - if lv2.SumProvingKey.Pk.IsDifferent(lv1.SumProvingKey.Pk) { + if lv2.SumProvingKey.Pk().IsDifferent(lv1.SumProvingKey.Pk()) { t.Error("sum proving keys not equal") } // SumVerifyingKey - if lv2.SumVerifyingKey.Vk.IsDifferent(lv1.SumVerifyingKey.Vk) { + if lv2.SumVerifyingKey.Vk().IsDifferent(lv1.SumVerifyingKey.Vk()) { t.Error("sum verifying keys not equal") } } diff --git a/election/msg.go b/election/msg.go index d70f071..c7e4684 100644 --- a/election/msg.go +++ b/election/msg.go @@ -499,47 +499,38 @@ func (elections *ElectionsMap) onKeysMessage(evt *event.Event, client *mautrix.C return } - var evalProvingKey groth16.ProvingKey + var evalProvingKey *ProvingKeyFile { - // yes, I know we should be using Download here - byts, err := client.DownloadBytes(content.EvalProvingKeyURI) + readCloser, err := client.Download(content.EvalProvingKeyURI) if err != nil { errorf("couldn't download eval proving key: %s", err) return } - var buf bytes.Buffer - _, err = buf.Write(byts) - if err != nil { - errorf("couldn't write to eval bytes buffer: %s") - return - } - evalProvingKey = groth16.NewProvingKey(ecc.BLS12_381) - _, err = evalProvingKey.ReadFrom(&buf) + pk := groth16.NewProvingKey(ecc.BLS12_381) + _, err = pk.ReadFrom(readCloser) + readCloser.Close() if err != nil { - errorf("couldn't read eval key from downloaded eval proving keys file: %s", err) + errorf("couldn't read eval proving key: %s", err) return } + evalProvingKey = NewProvingKeyFile(content.EvalProvingKeyURI.FileID, pk) } - var sumProvingKey groth16.ProvingKey + var sumProvingKey *ProvingKeyFile { - byts, err := client.DownloadBytes(content.SumProvingKeyURI) + readCloser, err := client.Download(content.SumProvingKeyURI) if err != nil { errorf("couldn't download sum proving key: %s", err) return } - var buf bytes.Buffer - _, err = buf.Write(byts) - if err != nil { - errorf("couldn't write to sum bytes buffer: %s") - return - } - sumProvingKey = groth16.NewProvingKey(ecc.BLS12_381) - _, err = sumProvingKey.ReadFrom(&buf) + pk := groth16.NewProvingKey(ecc.BLS12_381) + _, err = pk.ReadFrom(readCloser) + readCloser.Close() if err != nil { - errorf("couldn't read sum key from downloaded sum keys file: %s", err) + errorf("couldn't read sum proving key: %s", err) return } + sumProvingKey = NewProvingKeyFile(content.SumProvingKeyURI.FileID, pk) } var voter *Voter @@ -565,8 +556,8 @@ func (elections *ElectionsMap) onKeysMessage(evt *event.Event, client *mautrix.C defer el.Save() defer el.Unlock() - voter.EvalProvingKey = &MarshallableProvingKey{evalProvingKey} - voter.SumProvingKey = &MarshallableProvingKey{sumProvingKey} + voter.EvalProvingKey = evalProvingKey + voter.SumProvingKey = sumProvingKey voter.KeysID = &evt.ID return true @@ -749,7 +740,7 @@ func (elections *ElectionsMap) onEvalsMessage(evt *event.Event) (success bool) { errorf("our evals verifying key is nil") return } - err := groth16.Verify(proof, el.LocalVoter.EvalVerifyingKey.Vk, &publicCircuit) + err := groth16.Verify(proof, el.LocalVoter.EvalVerifyingKey.Vk(), &publicCircuit) if err != nil { warnf("poly eval proof verification failed: %s", err) return @@ -946,7 +937,7 @@ func (elections *ElectionsMap) onSumMessage(evt *event.Event) (success bool) { errorf("our sum verifying key is nil") return } - err := groth16.Verify(proof, el.LocalVoter.SumVerifyingKey.Vk, &publicCircuit) + err := groth16.Verify(proof, el.LocalVoter.SumVerifyingKey.Vk(), &publicCircuit) if err != nil { warnf("sum proof verification failed: %s", err) return diff --git a/election/voter.go b/election/voter.go index 9dfa678..510c023 100644 --- a/election/voter.go +++ b/election/voter.go @@ -33,8 +33,8 @@ type Voter struct { Sum *fr.Element `json:"sum,omitempty"` SumID *id.EventID `json:"sum_id,omitempty"` - EvalProvingKey *MarshallableProvingKey `json:"eval_proving_key,omitempty"` - SumProvingKey *MarshallableProvingKey `json:"sum_proving_key,omitempty"` + EvalProvingKey *ProvingKeyFile `json:"eval_proving_key,omitempty"` + SumProvingKey *ProvingKeyFile `json:"sum_proving_key,omitempty"` } type LocalVoter struct { @@ -42,9 +42,9 @@ type LocalVoter struct { PrivKey [32]byte `json:"priv_key"` - EvalVerifyingKey *MarshallableVerifyingKey `json:"eval_verifying_key,omitempty"` - Poly *math.Poly `json:"poly,omitempty"` - SumVerifyingKey *MarshallableVerifyingKey `json:"sum_verifying_key,omitempty"` + EvalVerifyingKey *VerifyingKeyFile `json:"eval_verifying_key,omitempty"` + Poly *math.Poly `json:"poly,omitempty"` + SumVerifyingKey *VerifyingKeyFile `json:"sum_verifying_key,omitempty"` } func NewVoter(input *fr.Element, joinEvt *event.Event, pubKey *[32]byte, seedPart []byte) *Voter { @@ -286,8 +286,8 @@ func (el *Election) SendProvingKeys(client *mautrix.Client, eventStore *EventSto defer el.Save() defer el.Unlock() - el.LocalVoter.EvalVerifyingKey = &MarshallableVerifyingKey{evalVerifyingKey} - el.LocalVoter.SumVerifyingKey = &MarshallableVerifyingKey{sumVerifyingKey} + el.LocalVoter.EvalVerifyingKey = NewVerifyingKeyFile(evalVerifyingKey) + el.LocalVoter.SumVerifyingKey = NewVerifyingKeyFile(sumVerifyingKey) return nil } @@ -304,7 +304,7 @@ func (el *Election) SendEvals(client *mautrix.Client, eventStore *EventStore) er for i, joinID := range *el.FinalJoinIDs { voter := el.Joins[joinID] - output, outputHash, proof := el.LocalVoter.Poly.EvalAndProve(&voter.Input, voter.EvalProvingKey.Pk) + output, outputHash, proof := el.LocalVoter.Poly.EvalAndProve(&voter.Input, voter.EvalProvingKey.Pk()) // encrypt output var outputBytes [32]byte @@ -429,7 +429,7 @@ func (el *Election) SendSum(client *mautrix.Client, eventStore *EventStore) erro for i, joinID := range *el.FinalJoinIDs { voter := *el.Joins[joinID] - proof, err := groth16.Prove(r1cs, voter.SumProvingKey.Pk, &witness) + proof, err := groth16.Prove(r1cs, voter.SumProvingKey.Pk(), &witness) if err != nil { el.RUnlock() return fmt.Errorf("couldn't prove sum: %s", err) diff --git a/go.sum b/go.sum index c39a9f1..138926e 100644 --- a/go.sum +++ b/go.sum @@ -14,7 +14,9 @@ github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1/go.mod h1:Az6Jt+M5idSED2YPGtwnfJV0kXohgdCBPmHGSYc1r04= github.com/gdamore/tcell/v2 v2.4.1-0.20211227212015-3260e4ac4385 h1:O5oaOCRcXvNnsPikhB6xGd4a1bbfgcuFQCQgDB4tM7Y= github.com/gdamore/tcell/v2 v2.4.1-0.20211227212015-3260e4ac4385/go.mod h1:I8YJFI9gzgl4dHi9UlRDZosCW+jYkDA37AXmXvL51w4= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -98,10 +100,12 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +maunium.net/go/maulogger/v2 v2.3.2 h1:1XmIYmMd3PoQfp9J+PaHhpt80zpfmMqaShzUTC7FwY0= maunium.net/go/maulogger/v2 v2.3.2/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A= maunium.net/go/mautrix v0.10.11 h1:u3D5+Ko7Pk0ruVFUAgjfk5E6U5Ys9VVObEGrytr0Hk4= maunium.net/go/mautrix v0.10.11/go.mod h1:Ynac6y32yvdJC8YiYvWjWp6u1WjVTNq+JssC+07ZZWw= -- 2.38.4