From ac9b1662cbd9d1dcc6954b166ab37009cf2fa6f4 Mon Sep 17 00:00:00 2001 From: Linus Gasser Date: Wed, 28 Feb 2024 13:22:52 +0100 Subject: [PATCH] Correctly store and retrieve DKGs --- integration/dvotingdela.go | 2 +- services/dkg/pedersen/controller/action.go | 55 +------------- .../dkg/pedersen/controller/action_test.go | 3 +- services/dkg/pedersen/controller/mod.go | 30 +------- services/dkg/pedersen/handler.go | 48 ++++++++---- services/dkg/pedersen/handler_test.go | 4 +- services/dkg/pedersen/mod.go | 73 ++++++++++++++++++- services/dkg/pedersen/mod_test.go | 14 ++-- 8 files changed, 121 insertions(+), 108 deletions(-) diff --git a/integration/dvotingdela.go b/integration/dvotingdela.go index 34ba8c1b4..ff455a7f2 100644 --- a/integration/dvotingdela.go +++ b/integration/dvotingdela.go @@ -241,7 +241,7 @@ func newDVotingNode(t require.TestingT, path string, randSource rand.Source) dVo formFac := etypes.NewFormFactory(etypes.CiphervoteFactory{}, rosterFac) - dkg := pedersen.NewPedersen(onet, srvc, pool, formFac, signer) + dkg := pedersen.NewPedersen(onet, srvc, db, pool, formFac, signer) evoting.RegisterContract(exec, evoting.NewContract(accessService, dkg, rosterFac)) diff --git a/services/dkg/pedersen/controller/action.go b/services/dkg/pedersen/controller/action.go index 95dbe3f81..f63fa702d 100644 --- a/services/dkg/pedersen/controller/action.go +++ b/services/dkg/pedersen/controller/action.go @@ -67,30 +67,11 @@ func (a *initAction) Execute(ctx node.Context) error { return xerrors.Errorf("failed to make client: %v", err) } - actor, err := dkg.Listen(formIDBuf, signed.NewManager(signer, &client)) + _, err = dkg.Listen(formIDBuf, signed.NewManager(signer, &client)) if err != nil { return xerrors.Errorf("failed to start the RPC: %v", err) } - dela.Logger.Info().Msg("DKG has been initialized successfully") - - err = updateDKGStore(ctx.Injector, func(tx kv.WritableTx) error { - bucket, err := tx.GetBucketOrCreate([]byte(BucketName)) - if err != nil { - return err - } - - actorBuf, err := actor.MarshalJSON() - if err != nil { - return err - } - - return bucket.Set(formIDBuf, actorBuf) - }) - if err != nil { - return xerrors.Errorf("failed to update DKG store: %v", err) - } - dela.Logger.Info().Msgf("DKG was successfully linked to form %v", formIDBuf) return nil @@ -137,23 +118,6 @@ func (a *setupAction) Execute(ctx node.Context) error { Hex("DKG public key", pubkeyBuf). Msg("DKG public key") - err = updateDKGStore(ctx.Injector, func(tx kv.WritableTx) error { - bucket, err := tx.GetBucketOrCreate([]byte(BucketName)) - if err != nil { - return err - } - - actorBuf, err := actor.MarshalJSON() - if err != nil { - return err - } - - return bucket.Set(formIDBuf, actorBuf) - }) - if err != nil { - return xerrors.Errorf("failed to update DKG store: %v", err) - } - return nil } @@ -190,7 +154,7 @@ func (a *exportInfoAction) Execute(ctx node.Context) error { } err = db.View(func(tx kv.ReadableTx) error { - bucket := tx.GetBucket([]byte(BucketName)) + bucket := tx.GetBucket([]byte(pedersen.BucketName)) if bucket == nil { return nil } @@ -333,21 +297,6 @@ func (a *RegisterHandlersAction) Execute(ctx node.Context) error { return nil } -func updateDKGStore(inj node.Injector, fn func(kv.WritableTx) error) error { - var db kv.DB - err := inj.Resolve(&db) - if err != nil { - return xerrors.Errorf("failed to resolve db: %v", err) - } - - err = db.Update(fn) - if err != nil { - return xerrors.Errorf("failed to update: %v", err) - } - - return nil -} - func makeClient(inj node.Injector) (client, error) { var service ordering.Service err := inj.Resolve(&service) diff --git a/services/dkg/pedersen/controller/action_test.go b/services/dkg/pedersen/controller/action_test.go index c6440ece4..dd8fff035 100644 --- a/services/dkg/pedersen/controller/action_test.go +++ b/services/dkg/pedersen/controller/action_test.go @@ -5,6 +5,7 @@ import ( "io" "testing" + "github.com/c4dt/d-voting/services/dkg/pedersen" "golang.org/x/xerrors" "github.com/c4dt/d-voting/internal/testing/fake" @@ -130,7 +131,7 @@ func TestSetupAction_Execute(t *testing.T) { // Check that the map contains the actor err = db.View(func(tx kv.ReadableTx) error { - bucket := tx.GetBucket([]byte(BucketName)) + bucket := tx.GetBucket([]byte(pedersen.BucketName)) require.NotNil(t, bucket) pubKeyBuf := bucket.Get(formIDBuf) diff --git a/services/dkg/pedersen/controller/mod.go b/services/dkg/pedersen/controller/mod.go index bf4c7c756..6a06fe91c 100644 --- a/services/dkg/pedersen/controller/mod.go +++ b/services/dkg/pedersen/controller/mod.go @@ -2,16 +2,15 @@ package controller import ( "encoding" - "encoding/json" "path/filepath" + "github.com/c4dt/d-voting/contracts/evoting" "go.dedis.ch/dela/core/txn/pool" "go.dedis.ch/dela/core/txn/signed" "go.dedis.ch/dela/crypto" "go.dedis.ch/dela/crypto/bls" "go.dedis.ch/dela/crypto/loader" - "github.com/c4dt/d-voting/contracts/evoting" "github.com/c4dt/d-voting/services/dkg/pedersen" "go.dedis.ch/dela/cli" "go.dedis.ch/dela/cli/node" @@ -27,8 +26,6 @@ import ( etypes "github.com/c4dt/d-voting/contracts/evoting/types" ) -// BucketName is the name of the bucket in the database. -const BucketName = "dkgmap" const privateKeyFile = "private.key" // NewController returns a new controller initializer @@ -150,31 +147,10 @@ func (m controller) OnStart(ctx cli.Flags, inj node.Injector) error { formFac := etypes.NewFormFactory(etypes.CiphervoteFactory{}, rosterFac) - dkg := pedersen.NewPedersen(no, srvc, p, formFac, signer) + dkg := pedersen.NewPedersen(no, srvc, db, p, formFac, signer) // Use dkgMap to fill the actors map - err = db.View(func(tx kv.ReadableTx) error { - bucket := tx.GetBucket([]byte(BucketName)) - if bucket == nil { - return nil - } - - return bucket.ForEach(func(formIDBuf, handlerDataBuf []byte) error { - - handlerData := pedersen.HandlerData{} - err = json.Unmarshal(handlerDataBuf, &handlerData) - if err != nil { - return err - } - - _, err = dkg.NewActor(formIDBuf, p, signed.NewManager(signer, &client), handlerData) - if err != nil { - return err - } - - return nil - }) - }) + err = dkg.ReadActors(signed.NewManager(signer, &client)) if err != nil { return xerrors.Errorf("database read failed: %v", err) } diff --git a/services/dkg/pedersen/handler.go b/services/dkg/pedersen/handler.go index abafa39bd..e43527d88 100644 --- a/services/dkg/pedersen/handler.go +++ b/services/dkg/pedersen/handler.go @@ -5,7 +5,6 @@ import ( "container/list" "context" "crypto/sha256" - "encoding/hex" "encoding/json" "errors" "io" @@ -19,10 +18,10 @@ import ( "go.dedis.ch/dela/core/txn" "go.dedis.ch/dela/core/txn/pool" "go.dedis.ch/dela/crypto" + "go.dedis.ch/dela/mino/minogrpc/session" jsondela "go.dedis.ch/dela/serde/json" etypes "github.com/c4dt/d-voting/contracts/evoting/types" - "github.com/c4dt/d-voting/internal/testing/fake" "github.com/c4dt/d-voting/services/dkg" "github.com/c4dt/d-voting/services/dkg/pedersen/types" "go.dedis.ch/dela" @@ -72,13 +71,16 @@ type Handler struct { log zerolog.Logger running bool + saveState func(*Handler) + status *dkg.Status } // NewHandler creates a new handler func NewHandler(me mino.Address, service ordering.Service, pool pool.Pool, txnmngr txn.Manager, pubSharesSigner crypto.Signer, handlerData HandlerData, - context serde.Context, formFac serde.Factory, status *dkg.Status) *Handler { + context serde.Context, formFac serde.Factory, status *dkg.Status, + saveState func(*Handler)) *Handler { privKey := handlerData.PrivKey pubKey := handlerData.PubKey @@ -105,7 +107,8 @@ func NewHandler(me mino.Address, service ordering.Service, pool pool.Pool, log: log, running: false, - status: status, + saveState: saveState, + status: status, } } @@ -299,6 +302,7 @@ func (h *Handler) doDKG(deals, resps *list.List, out mino.Sender, from mino.Addr dela.Logger.Error().Msgf("got an error while sending pub key: %v", err) return } + h.saveState(h) } func (h *Handler) deal(out mino.Sender) error { @@ -612,7 +616,7 @@ func (hd *HandlerData) MarshalJSON() ([]byte, error) { return nil, err } - return json.Marshal(&struct { + ret, err := json.Marshal(&struct { StartRes []byte `json:",omitempty"` PrivShare []byte `json:",omitempty"` PubKey []byte @@ -623,6 +627,8 @@ func (hd *HandlerData) MarshalJSON() ([]byte, error) { PubKey: pubKeyBuf, PrivKey: privKeyBuf, }) + + return ret, err } // UnmarshalJSON fills a HandlerData with previously marshalled data. @@ -640,7 +646,10 @@ func (hd *HandlerData) UnmarshalJSON(data []byte) error { // Unmarshal StartRes hd.StartRes = &state{} - hd.StartRes.UnmarshalJSON(aux.StartRes) + err = hd.StartRes.UnmarshalJSON(aux.StartRes) + if err != nil { + return err + } // Unmarshal PrivShare if aux.PrivShare == nil { @@ -655,7 +664,10 @@ func (hd *HandlerData) UnmarshalJSON(data []byte) error { return err } privShareV := suite.Scalar() - privShareV.UnmarshalBinary(privShareBuf.V) + err = privShareV.UnmarshalBinary(privShareBuf.V) + if err != nil { + return err + } privShare := &share.PriShare{ I: privShareBuf.I, V: privShareV, @@ -665,12 +677,18 @@ func (hd *HandlerData) UnmarshalJSON(data []byte) error { // Unmarshal PubKey pubKey := suite.Point() - pubKey.UnmarshalBinary(aux.PubKey) + err = pubKey.UnmarshalBinary(aux.PubKey) + if err != nil { + return err + } hd.PubKey = pubKey // Unmarshal PrivKey privKey := suite.Scalar() - privKey.UnmarshalBinary(aux.PrivKey) + err = privKey.UnmarshalBinary(aux.PrivKey) + if err != nil { + return err + } hd.PrivKey = privKey return nil @@ -739,13 +757,15 @@ func (s *state) MarshalJSON() ([]byte, error) { } } - return json.Marshal(&struct { + ret, err := json.Marshal(&struct { DistKey []byte `json:",omitempty"` Participants [][]byte `json:",omitempty"` }{ DistKey: distKeyBuf, Participants: participantsBuf, }) + + return ret, err } func (s *state) UnmarshalJSON(data []byte) error { @@ -770,11 +790,11 @@ func (s *state) UnmarshalJSON(data []byte) error { } if aux.Participants != nil { - // TODO: Is using a fake implementation a problem? - f := fake.NewBadMino().GetAddressFactory() + // TODO: use addressFactory here + f := session.AddressFactory{} var participants = make([]mino.Address, len(aux.Participants)) - for i := 0; i < len(aux.Participants); i++ { - participants[i] = f.FromText(aux.Participants[i]) + for i, partStr := range aux.Participants { + participants[i] = f.FromText(partStr) } s.SetParticipants(participants) } else { diff --git a/services/dkg/pedersen/handler_test.go b/services/dkg/pedersen/handler_test.go index 52627d0ff..0e3982446 100644 --- a/services/dkg/pedersen/handler_test.go +++ b/services/dkg/pedersen/handler_test.go @@ -10,6 +10,7 @@ import ( formTypes "github.com/c4dt/d-voting/contracts/evoting/types" "go.dedis.ch/dela/core/access" "go.dedis.ch/dela/core/txn/signed" + "go.dedis.ch/dela/mino/minogrpc/session" "go.dedis.ch/dela/serde/json" "github.com/c4dt/d-voting/internal/testing/fake" @@ -264,7 +265,8 @@ func TestState_MarshalJSON(t *testing.T) { // Try with some data distKey := suite.Point().Pick(suite.RandomStream()) - participants := []mino.Address{fake.NewAddress(0), fake.NewAddress(1)} + // TODO: use AddressFactory here + participants := []mino.Address{session.NewAddress("grpcs://localhost:12345"), session.NewAddress("grpcs://localhost:1234")} s1.SetDistKey(distKey) s1.SetParticipants(participants) diff --git a/services/dkg/pedersen/mod.go b/services/dkg/pedersen/mod.go index 032a876bc..7f0c54782 100644 --- a/services/dkg/pedersen/mod.go +++ b/services/dkg/pedersen/mod.go @@ -2,9 +2,11 @@ package pedersen import ( "encoding/hex" + "encoding/json" "sync" "time" + "go.dedis.ch/dela/core/store/kv" "go.dedis.ch/dela/core/txn" "go.dedis.ch/dela/core/txn/pool" "go.dedis.ch/dela/crypto" @@ -35,6 +37,9 @@ import ( _ "github.com/c4dt/d-voting/contracts/evoting/json" ) +// BucketName is the name of the bucket in the database. +const BucketName = "dkgmap" + // suite is the Kyber suite for Pedersen. var suite = suites.MustFind("Ed25519") @@ -68,10 +73,12 @@ type Pedersen struct { pool pool.Pool signer crypto.Signer actors map[string]dkg.Actor + db kv.DB } // NewPedersen returns a new DKG Pedersen factory -func NewPedersen(m mino.Mino, service ordering.Service, pool pool.Pool, +func NewPedersen(m mino.Mino, service ordering.Service, + db kv.DB, pool pool.Pool, formFac serde.Factory, signer crypto.Signer) *Pedersen { factory := types.NewMessageFactory(m.GetAddressFactory()) @@ -85,6 +92,7 @@ func NewPedersen(m mino.Mino, service ordering.Service, pool pool.Pool, actors: actors, signer: signer, formFac: formFac, + db: db, } } @@ -125,7 +133,12 @@ func (s *Pedersen) NewActor(formIDBuf []byte, pool pool.Pool, txmngr txn.Manager // link the actor to an RPC by the form ID h := NewHandler(s.mino.GetAddress(), s.service, pool, txmngr, s.signer, - handlerData, ctx, s.formFac, status) + handlerData, ctx, s.formFac, status, func(h *Handler) { + err := storeHandler(formID, s.db, h) + if err != nil { + dela.Logger.Err(err).Msg("While storing the dkg handler") + } + }) no := s.mino.WithSegment(formID) rpc := mino.MustCreateRPC(no, RPC, h, s.factory) @@ -142,6 +155,7 @@ func (s *Pedersen) NewActor(formIDBuf []byte, pool pool.Pool, txmngr txn.Manager formID: formID, status: status, log: log, + db: s.db, } evoting.PromFormDkgStatus.WithLabelValues(formID).Set(float64(dkg.Initialized)) @@ -150,7 +164,7 @@ func (s *Pedersen) NewActor(formIDBuf []byte, pool pool.Pool, txmngr txn.Manager defer s.Unlock() s.actors[formID] = a - return a, nil + return a, a.store() } // GetActor implements dkg.DKG @@ -161,6 +175,32 @@ func (s *Pedersen) GetActor(formIDBuf []byte) (dkg.Actor, bool) { return actor, exists } +func (s *Pedersen) ReadActors(txmngr txn.Manager) error { + // Use dkgMap to fill the actors map + return s.db.View(func(tx kv.ReadableTx) error { + bucket := tx.GetBucket([]byte(BucketName)) + if bucket == nil { + return nil + } + + return bucket.ForEach(func(formIDBuf, handlerDataBuf []byte) error { + + handlerData := HandlerData{} + err := json.Unmarshal(handlerDataBuf, &handlerData) + if err != nil { + return err + } + + _, err = s.NewActor(formIDBuf, s.pool, txmngr, handlerData) + if err != nil { + return err + } + + return nil + }) + }) +} + // Actor allows one to perform DKG operations like encrypt/decrypt a message // // - implements dkg.Actor @@ -174,6 +214,7 @@ type Actor struct { formID string status *dkg.Status log zerolog.Logger + db kv.DB } func (a *Actor) setErr(err error, args map[string]interface{}) { @@ -327,7 +368,31 @@ func (a *Actor) Setup() (kyber.Point, error) { *a.status = dkg.Status{Status: dkg.Setup} evoting.PromFormDkgStatus.WithLabelValues(a.formID).Set(float64(dkg.Setup)) - return dkgPubKeys[0], nil + return dkgPubKeys[0], a.store() +} + +func (a *Actor) store() error { + return storeHandler(a.formID, a.db, a.handler) +} +func storeHandler(formID string, db kv.DB, h *Handler) error { + return db.Update(func(tx kv.WritableTx) error { + formIDBuf, err := hex.DecodeString(formID) + if err != nil { + return err + } + + bucket, err := tx.GetBucketOrCreate([]byte(BucketName)) + if err != nil { + return err + } + + actorBuf, err := h.MarshalJSON() + if err != nil { + return err + } + + return bucket.Set(formIDBuf, actorBuf) + }) } // GetPublicKey implements dkg.Actor diff --git a/services/dkg/pedersen/mod_test.go b/services/dkg/pedersen/mod_test.go index c89d2a1dc..f7c4c1af6 100644 --- a/services/dkg/pedersen/mod_test.go +++ b/services/dkg/pedersen/mod_test.go @@ -54,7 +54,7 @@ func init() { func TestActor_MarshalJSON(t *testing.T) { initMetrics() - p := NewPedersen(fake.Mino{}, &fake.Service{}, &fake.Pool{}, fake.Factory{}, fake.Signer{}) + p := NewPedersen(fake.Mino{}, &fake.Service{}, fake.NewInMemoryDB(), &fake.Pool{}, fake.Factory{}, fake.Signer{}) // Create new actor actor1, err := p.NewActor([]byte("deadbeef"), &fake.Pool{}, @@ -139,7 +139,7 @@ func TestPedersen_InitNonEmptyMap(t *testing.T) { require.NoError(t, err) // Initialize a Pedersen - p := NewPedersen(fake.Mino{}, &fake.Service{}, &fake.Pool{}, fake.Factory{}, fake.Signer{}) + p := NewPedersen(fake.Mino{}, &fake.Service{}, fake.NewInMemoryDB(), &fake.Pool{}, fake.Factory{}, fake.Signer{}) err = dkgMap.View(func(tx kv.ReadableTx) error { bucket := tx.GetBucket([]byte("dkgmap")) @@ -210,7 +210,7 @@ func TestPedersen_SyncDB(t *testing.T) { manager := fake.Manager{} // Initialize a Pedersen - p := NewPedersen(fake.Mino{}, &service, &pool, fake.Factory{}, fake.Signer{}) + p := NewPedersen(fake.Mino{}, &service, fake.NewInMemoryDB(), &pool, fake.Factory{}, fake.Signer{}) // Create actors formID1buf, err := hex.DecodeString(formID1) @@ -259,7 +259,7 @@ func TestPedersen_SyncDB(t *testing.T) { require.NoError(t, err) // Recover them from the map - q := NewPedersen(fake.Mino{}, &service, &pool, fake.Factory{}, fake.Signer{}) + q := NewPedersen(fake.Mino{}, &service, fake.NewInMemoryDB(), &pool, fake.Factory{}, fake.Signer{}) err = dkgMap.View(func(tx kv.ReadableTx) error { bucket := tx.GetBucket([]byte("dkgmap")) @@ -304,7 +304,7 @@ func TestPedersen_Listen(t *testing.T) { service := fake.NewService(formID, etypes.Form{Roster: fake.Authority{}}, serdecontext) - p := NewPedersen(fake.Mino{}, &service, &fake.Pool{}, + p := NewPedersen(fake.Mino{}, &service, fake.NewInMemoryDB(), &fake.Pool{}, fake.Factory{}, fake.Signer{}) actor, err := p.Listen(formIDBuf, fake.Manager{}) @@ -322,7 +322,7 @@ func TestPedersen_TwoListens(t *testing.T) { service := fake.NewService(formID, etypes.Form{Roster: fake.Authority{}}, serdecontext) - p := NewPedersen(fake.Mino{}, &service, &fake.Pool{}, fake.Factory{}, fake.Signer{}) + p := NewPedersen(fake.Mino{}, &service, fake.NewInMemoryDB(), &fake.Pool{}, fake.Factory{}, fake.Signer{}) actor1, err := p.Listen(formIDBuf, fake.Manager{}) require.NoError(t, err) @@ -511,7 +511,7 @@ func TestPedersen_Scenario(t *testing.T) { for i, mino := range minos { fac := etypes.NewFormFactory(etypes.CiphervoteFactory{}, fake.NewRosterFac(roster)) - dkg := NewPedersen(mino, &service, &fake.Pool{}, fac, fake.Signer{}) + dkg := NewPedersen(mino, &service, fake.NewInMemoryDB(), &fake.Pool{}, fac, fake.Signer{}) actor, err := dkg.Listen(formIDBuf, signed.NewManager(fake.Signer{}, &client{ srvc: &fake.Service{},