From d70d8c74b33b13a715e75a192a5b40a23da7a69c Mon Sep 17 00:00:00 2001 From: Dmitry Shulyak Date: Fri, 6 Oct 2023 12:08:29 +0000 Subject: [PATCH] hare3: support multiple keys (#5126) closes: https://github.com/spacemeshos/go-spacemesh/issues/5085 this change refactors original hare oracle to accept key that will be used to determine eligibility. and extends hare3 with a collection of keys that can be used to participate from the same physical node. limitation is that ecvrf and ed signatures are computed sequentially and may cause messages to be delayed, if number of nodes are larger then 100. larger numbers are useful only if we expect it to be used with smallest possible atxs, which is by itself a problem. --- hare/eligibility/oracle.go | 21 +++-- hare3/hare.go | 165 ++++++++++++++++++++++++------------- hare3/hare_test.go | 101 +++++++++++++++++------ hare3/legacy_oracle.go | 13 +-- hare3/tracer.go | 4 +- node/node.go | 3 +- 6 files changed, 203 insertions(+), 104 deletions(-) diff --git a/hare/eligibility/oracle.go b/hare/eligibility/oracle.go index 00333ec24c..cc8f34c5ba 100644 --- a/hare/eligibility/oracle.go +++ b/hare/eligibility/oracle.go @@ -138,19 +138,13 @@ func (o *Oracle) resetCacheOnSynced(ctx context.Context) { } } -// buildVRFMessage builds the VRF message used as input for the BLS (msg=Beacon##Layer##Round). +// buildVRFMessage builds the VRF message used as input for hare eligibility validation. func (o *Oracle) buildVRFMessage(ctx context.Context, layer types.LayerID, round uint32) ([]byte, error) { beacon, err := o.beacons.GetBeacon(layer.GetEpoch()) if err != nil { return nil, fmt.Errorf("get beacon: %w", err) } - - msg := VrfMessage{Type: types.EligibilityHare, Beacon: beacon, Round: round, Layer: layer} - buf, err := codec.Encode(&msg) - if err != nil { - o.WithContext(ctx).With().Fatal("failed to encode", log.Err(err)) - } - return buf, nil + return codec.MustEncode(&VrfMessage{Type: types.EligibilityHare, Beacon: beacon, Round: round, Layer: layer}), nil } func (o *Oracle) totalWeight(ctx context.Context, layer types.LayerID) (uint64, error) { @@ -344,11 +338,16 @@ func (o *Oracle) CalcEligibility( // Proof returns the role proof for the current Layer & Round. func (o *Oracle) Proof(ctx context.Context, layer types.LayerID, round uint32) (types.VrfSignature, error) { - msg, err := o.buildVRFMessage(ctx, layer, round) + beacon, err := o.beacons.GetBeacon(layer.GetEpoch()) if err != nil { - return types.EmptyVrfSignature, err + return types.EmptyVrfSignature, fmt.Errorf("get beacon: %w", err) } - return o.vrfSigner.Sign(msg), nil + return o.GenVRF(ctx, o.vrfSigner, beacon, layer, round), nil +} + +// GenVRF generates vrf for hare eligibility. +func (o *Oracle) GenVRF(ctx context.Context, signer *signing.VRFSigner, beacon types.Beacon, layer types.LayerID, round uint32) types.VrfSignature { + return signer.Sign(codec.MustEncode(&VrfMessage{Type: types.EligibilityHare, Beacon: beacon, Round: round, Layer: layer})) } // Returns a map of all active node IDs in the specified layer id. diff --git a/hare3/hare.go b/hare3/hare.go index 2895cfa615..6f7f458bb3 100644 --- a/hare3/hare.go +++ b/hare3/hare.go @@ -11,6 +11,7 @@ import ( "github.com/jonboulle/clockwork" "go.uber.org/zap" "go.uber.org/zap/zapcore" + "golang.org/x/exp/maps" "golang.org/x/sync/errgroup" "github.com/spacemeshos/go-spacemesh/codec" @@ -137,7 +138,6 @@ func New( pubsub pubsub.PublishSubsciber, db *datastore.CachedDB, verifier *signing.EdVerifier, - signer *signing.EdSigner, oracle oracle, sync system.SyncStateProvider, patrol *layerpatrol.LayerPatrol, @@ -149,6 +149,7 @@ func New( cancel: cancel, results: make(chan ConsensusOutput, 32), coins: make(chan WeakCoinOutput, 32), + signers: map[string]*signing.EdSigner{}, sessions: map[types.LayerID]*protocol{}, config: DefaultConfig(), @@ -159,7 +160,6 @@ func New( pubsub: pubsub, db: db, verifier: verifier, - signer: signer, oracle: &legacyOracle{ log: zap.NewNop(), oracle: oracle, @@ -183,6 +183,7 @@ type Hare struct { results chan ConsensusOutput coins chan WeakCoinOutput mu sync.Mutex + signers map[string]*signing.EdSigner sessions map[types.LayerID]*protocol // options @@ -195,13 +196,19 @@ type Hare struct { pubsub pubsub.PublishSubsciber db *datastore.CachedDB verifier *signing.EdVerifier - signer *signing.EdSigner oracle *legacyOracle sync system.SyncStateProvider patrol *layerpatrol.LayerPatrol tracer Tracer } +func (h *Hare) Register(signer *signing.EdSigner) { + h.mu.Lock() + defer h.mu.Unlock() + h.log.Info("register signing key", zap.Stringer("node", signer.NodeID())) + h.signers[string(signer.NodeID().Bytes())] = signer +} + func (h *Hare) Results() <-chan ConsensusOutput { return h.results } @@ -323,15 +330,24 @@ func (h *Hare) onLayer(layer types.LayerID) { return } h.patrol.SetHareInCharge(layer) - proto := newProtocol(h.config.Committee/2 + 1) + h.mu.Lock() - h.sessions[layer] = proto + // signer can't join mid session + s := &session{ + lid: layer, + beacon: beacon, + signers: maps.Values(h.signers), + vrfs: make([]*types.HareEligibility, len(h.signers)), + proto: newProtocol(h.config.Committee/2 + 1), + } + h.sessions[layer] = s.proto h.mu.Unlock() + sessionStart.Inc() h.tracer.OnStart(layer) h.log.Debug("registered layer", zap.Uint32("lid", layer.Uint32())) h.eg.Go(func() error { - if err := h.run(layer, beacon, proto); err != nil { + if err := h.run(s); err != nil { h.log.Warn("failed", zap.Uint32("lid", layer.Uint32()), zap.Error(err), @@ -354,20 +370,25 @@ func (h *Hare) onLayer(layer types.LayerID) { }) } -func (h *Hare) run(layer types.LayerID, beacon types.Beacon, proto *protocol) error { +func (h *Hare) run(session *session) error { // oracle may load non-negligible amount of data from disk // we do it before preround starts, so that load can have some slack time // before it needs to be used in validation - current := IterRound{Round: preround} - - start := time.Now() - vrf := h.oracle.active(h.signer.NodeID(), layer, current) + var ( + current = IterRound{Round: preround} + start = time.Now() + active bool + ) + for i := range session.signers { + session.vrfs[i] = h.oracle.active(session.signers[i], session.beacon, session.lid, current) + active = active || session.vrfs[i] != nil + } + h.tracer.OnActive(session.vrfs) activeLatency.Observe(time.Since(start).Seconds()) - h.tracer.OnActive(vrf) - walltime := h.nodeclock.LayerToTime(layer).Add(h.config.PreroundDelay) - if vrf != nil { - h.log.Debug("active in preround", zap.Uint32("lid", layer.Uint32())) + walltime := h.nodeclock.LayerToTime(session.lid).Add(h.config.PreroundDelay) + if active { + h.log.Debug("active in preround", zap.Uint32("lid", session.lid.Uint32())) // initial set is not needed if node is not active in preround select { case <-h.wallclock.After(walltime.Sub(h.wallclock.Now())): @@ -375,35 +396,42 @@ func (h *Hare) run(layer types.LayerID, beacon types.Beacon, proto *protocol) er return h.ctx.Err() } start := time.Now() - proto.OnInitial(h.proposals(layer, beacon)) + session.proto.OnInitial(h.proposals(session)) proposalsLatency.Observe(time.Since(start).Seconds()) } - if err := h.onOutput(layer, current, proto.Next(vrf != nil), vrf); err != nil { + if err := h.onOutput(session, current, session.proto.Next(active)); err != nil { return err } result := false for { walltime = walltime.Add(h.config.RoundDuration) - current := proto.IterRound - var vrf *types.HareEligibility - if current.IsMessageRound() { - start := time.Now() - vrf = h.oracle.active(h.signer.NodeID(), layer, current) - activeLatency.Observe(time.Since(start).Seconds()) + active = false + current = session.proto.IterRound + start = time.Now() + + for i := range session.signers { + if current.IsMessageRound() { + session.vrfs[i] = h.oracle.active(session.signers[i], session.beacon, session.lid, current) + } else { + session.vrfs[i] = nil + } + active = active || session.vrfs[i] != nil } - h.tracer.OnActive(vrf) + h.tracer.OnActive(session.vrfs) + activeLatency.Observe(time.Since(start).Seconds()) + select { case <-h.wallclock.After(walltime.Sub(h.wallclock.Now())): h.log.Debug("execute round", - zap.Uint32("lid", layer.Uint32()), - zap.Uint8("iter", proto.Iter), zap.Stringer("round", proto.Round), - zap.Bool("active", vrf != nil), + zap.Uint32("lid", session.lid.Uint32()), + zap.Uint8("iter", session.proto.Iter), zap.Stringer("round", session.proto.Round), + zap.Bool("active", active), ) - out := proto.Next(vrf != nil) + out := session.proto.Next(active) if out.result != nil { result = true } - if err := h.onOutput(layer, current, out, vrf); err != nil { + if err := h.onOutput(session, current, out); err != nil { return err } if out.terminated { @@ -422,27 +450,31 @@ func (h *Hare) run(layer types.LayerID, beacon types.Beacon, proto *protocol) er } } -func (h *Hare) onOutput(layer types.LayerID, ir IterRound, out output, vrf *types.HareEligibility) error { - if out.message != nil { - out.message.Layer = layer - out.message.Eligibility = *vrf - out.message.Sender = h.signer.NodeID() - out.message.Signature = h.signer.Sign(signing.HARE, out.message.ToMetadata().ToBytes()) - if err := h.pubsub.Publish(h.ctx, h.config.ProtocolName, out.message.ToBytes()); err != nil { - h.log.Error("failed to publish", zap.Inline(out.message), zap.Error(err)) +func (h *Hare) onOutput(session *session, ir IterRound, out output) error { + for i, vrf := range session.vrfs { + if vrf == nil || out.message == nil { + continue + } + msg := *out.message // shallow copy + msg.Layer = session.lid + msg.Eligibility = *vrf + msg.Sender = session.signers[i].NodeID() + msg.Signature = session.signers[i].Sign(signing.HARE, msg.ToMetadata().ToBytes()) + if err := h.pubsub.Publish(h.ctx, h.config.ProtocolName, msg.ToBytes()); err != nil { + h.log.Error("failed to publish", zap.Inline(&msg), zap.Error(err)) } } + h.tracer.OnMessageSent(out.message) h.log.Debug("round output", - zap.Uint32("lid", layer.Uint32()), + zap.Uint32("lid", session.lid.Uint32()), zap.Uint8("iter", ir.Iter), zap.Stringer("round", ir.Round), zap.Inline(&out), ) - h.tracer.OnMessageSent(out.message) if out.coin != nil { select { case <-h.ctx.Done(): return h.ctx.Err() - case h.coins <- WeakCoinOutput{Layer: layer, Coin: *out.coin}: + case h.coins <- WeakCoinOutput{Layer: session.lid, Coin: *out.coin}: } sessionCoin.Inc() } @@ -450,38 +482,45 @@ func (h *Hare) onOutput(layer types.LayerID, ir IterRound, out output, vrf *type select { case <-h.ctx.Done(): return h.ctx.Err() - case h.results <- ConsensusOutput{Layer: layer, Proposals: out.result}: + case h.results <- ConsensusOutput{Layer: session.lid, Proposals: out.result}: } sessionResult.Inc() } return nil } -func (h *Hare) proposals(lid types.LayerID, epochBeacon types.Beacon) []types.ProposalID { +func (h *Hare) proposals(session *session) []types.ProposalID { h.log.Debug("requested proposals", - zap.Uint32("lid", lid.Uint32()), - zap.Stringer("beacon", epochBeacon), + zap.Uint32("lid", session.lid.Uint32()), + zap.Stringer("beacon", session.beacon), ) - props, err := proposals.GetByLayer(h.db, lid) + props, err := proposals.GetByLayer(h.db, session.lid) if err != nil { if errors.Is(err, sql.ErrNotFound) { h.log.Warn("no proposals found for hare, using empty set", - zap.Uint32("lid", lid.Uint32()), zap.Error(err)) + zap.Uint32("lid", session.lid.Uint32()), zap.Error(err)) } else { h.log.Error("failed to get proposals for hare", - zap.Uint32("lid", lid.Uint32()), zap.Error(err)) + zap.Uint32("lid", session.lid.Uint32()), zap.Error(err)) } return []types.ProposalID{} } var ( - beacon types.Beacon - result []types.ProposalID + beacon types.Beacon + result []types.ProposalID + min, own *types.ActivationTxHeader ) - own, err := h.db.GetEpochAtx(lid.GetEpoch()-1, h.signer.NodeID()) - if err != nil { - h.log.Warn("no atxs in the requested epoch", - zap.Uint32("epoch", lid.GetEpoch().Uint32()-1), - zap.Error(err)) + for _, signer := range session.signers { + own, err = h.db.GetEpochAtx(session.lid.GetEpoch()-1, signer.NodeID()) + if err != nil && !errors.Is(err, sql.ErrNotFound) { + return []types.ProposalID{} + } + if min == nil || (min != nil && own != nil && own.TickHeight() < min.TickHeight()) { + min = own + } + } + if min == nil { + h.log.Debug("no atxs in the requested epoch", zap.Uint32("epoch", session.lid.GetEpoch().Uint32()-1)) return []types.ProposalID{} } atxs := map[types.ATXID]int{} @@ -508,10 +547,10 @@ func (h *Hare) proposals(lid types.LayerID, epochBeacon types.Beacon) []types.Pr h.log.Error("atx is not loaded", zap.Error(err), zap.Stringer("atxid", p.AtxID)) return []types.ProposalID{} } - if hdr.BaseTickHeight >= own.TickHeight() { + if hdr.BaseTickHeight >= min.TickHeight() { // does not vote for future proposal h.log.Warn("proposal base tick height too high. skipping", - zap.Uint32("lid", lid.Uint32()), + zap.Uint32("lid", session.lid.Uint32()), zap.Uint64("proposal_height", hdr.BaseTickHeight), zap.Uint64("own_height", own.TickHeight()), ) @@ -532,14 +571,14 @@ func (h *Hare) proposals(lid types.LayerID, epochBeacon types.Beacon) []types.Pr } else { beacon = refBallot.EpochData.Beacon } - if beacon == epochBeacon { + if beacon == session.beacon { result = append(result, p.ID()) } else { h.log.Warn("proposal has different beacon value", - zap.Uint32("lid", lid.Uint32()), + zap.Uint32("lid", session.lid.Uint32()), zap.Stringer("id", p.ID()), zap.String("proposal_beacon", beacon.ShortString()), - zap.String("epoch_beacon", epochBeacon.ShortString()), + zap.String("epoch_beacon", session.beacon.ShortString()), ) } } @@ -553,3 +592,11 @@ func (h *Hare) Stop() { close(h.coins) h.log.Info("stopped") } + +type session struct { + proto *protocol + lid types.LayerID + beacon types.Beacon + signers []*signing.EdSigner + vrfs []*types.HareEligibility +} diff --git a/hare3/hare_test.go b/hare3/hare_test.go index e16000c8e8..e92f080d77 100644 --- a/hare3/hare_test.go +++ b/hare3/hare_test.go @@ -108,14 +108,15 @@ func (t *testNodeClock) AwaitLayer(lid types.LayerID) <-chan struct{} { type node struct { t *tester - i int - clock clockwork.FakeClock - nclock *testNodeClock - signer *signing.EdSigner - vrfsigner *signing.VRFSigner - atx *types.VerifiedActivationTx - oracle *eligibility.Oracle - db *datastore.CachedDB + i int + clock clockwork.FakeClock + nclock *testNodeClock + signer *signing.EdSigner + registered []*signing.EdSigner + vrfsigner *signing.VRFSigner + atx *types.VerifiedActivationTx + oracle *eligibility.Oracle + db *datastore.CachedDB ctrl *gomock.Controller mpublisher *pmocks.MockPublishSubsciber @@ -213,15 +214,29 @@ func (n *node) withHare() *node { tracer := newTestTracer(n.t) n.tracer = tracer n.patrol = layerpatrol.New() - n.hare = New(n.nclock, n.mpublisher, n.db, verifier, n.signer, n.oracle, n.msyncer, n.patrol, + n.hare = New(n.nclock, n.mpublisher, n.db, verifier, n.oracle, n.msyncer, n.patrol, WithConfig(n.t.cfg), WithLogger(logger.Zap()), WithWallclock(n.clock), WithTracer(tracer), ) + n.register(n.signer) return n } +func (n *node) waitEligibility() { + n.tracer.waitEligibility() +} + +func (n *node) waitSent() { + n.tracer.waitSent() +} + +func (n *node) register(signer *signing.EdSigner) { + n.hare.Register(signer) + n.registered = append(n.registered, signer) +} + type clusterOpt func(*lockstepCluster) func withUnits(min, max int) clusterOpt { @@ -238,6 +253,14 @@ func withProposals(fraction float64) clusterOpt { } } +// withSigners creates N signers in addition to regular active nodes. +// this signeres will be partitioned in fair fashion across regular active nodes. +func withSigners(n int) clusterOpt { + return func(cluster *lockstepCluster) { + cluster.signersCount = n + } +} + func newLockstepCluster(t *tester, opts ...clusterOpt) *lockstepCluster { cluster := &lockstepCluster{t: t} cluster.units.min = 10 @@ -253,8 +276,9 @@ func newLockstepCluster(t *tester, opts ...clusterOpt) *lockstepCluster { // lockstepCluster allows to run rounds in lockstep // as no peer will be able to start around until test allows it. type lockstepCluster struct { - t *tester - nodes []*node + t *tester + nodes []*node + signers []*node // nodes that active on consensus but don't run hare instance units struct { min, max int @@ -263,6 +287,7 @@ type lockstepCluster struct { fraction float64 shuffle bool } + signersCount int timestamp time.Time } @@ -275,6 +300,21 @@ func (cl *lockstepCluster) addNode(n *node) { cl.nodes = append(cl.nodes, n) } +func (cl *lockstepCluster) partitionSigners() { + for i, signer := range cl.signers { + cl.nodes[i%len(cl.nodes)].register(signer.signer) + } +} + +func (cl *lockstepCluster) addSigner(n int) *lockstepCluster { + last := len(cl.signers) + for i := last; i < last+n; i++ { + n := (&node{t: cl.t, i: i}).withSigner().withAtx(cl.units.min, cl.units.max) + cl.signers = append(cl.signers, n) + } + return cl +} + func (cl *lockstepCluster) addActive(n int) *lockstepCluster { last := len(cl.nodes) for i := last; i < last+n; i++ { @@ -320,7 +360,7 @@ func (cl *lockstepCluster) nogossip() { func (cl *lockstepCluster) activeSet() types.ATXIDList { var ids []types.ATXID unique := map[types.ATXID]struct{}{} - for _, n := range cl.nodes { + for _, n := range append(cl.nodes, cl.signers...) { if n.atx == nil { continue } @@ -336,7 +376,7 @@ func (cl *lockstepCluster) activeSet() types.ATXIDList { func (cl *lockstepCluster) genProposals(lid types.LayerID) { active := cl.activeSet() all := []*types.Proposal{} - for _, n := range cl.nodes { + for _, n := range append(cl.nodes, cl.signers...) { if n.atx == nil { continue } @@ -375,7 +415,7 @@ func (cl *lockstepCluster) setup() { active := cl.activeSet() for _, n := range cl.nodes { require.NoError(cl.t, beacons.Add(n.db, cl.t.genesis.GetEpoch()+1, cl.t.beacon)) - for _, other := range cl.nodes { + for _, other := range append(cl.nodes, cl.signers...) { if other.atx == nil { continue } @@ -400,10 +440,10 @@ func (cl *lockstepCluster) movePreround(layer types.LayerID) { n.clock.Advance(cl.timestamp.Sub(n.clock.Now())) } for _, n := range cl.nodes { - n.tracer.waitEligibility() + n.waitEligibility() } for _, n := range cl.nodes { - n.tracer.waitSent() + n.waitSent() } } @@ -413,10 +453,10 @@ func (cl *lockstepCluster) moveRound() { n.clock.Advance(cl.timestamp.Sub(n.clock.Now())) } for _, n := range cl.nodes { - n.tracer.waitEligibility() + n.waitEligibility() } for _, n := range cl.nodes { - n.tracer.waitSent() + n.waitSent() } } @@ -430,7 +470,7 @@ func newTestTracer(tb testing.TB) *testTracer { return &testTracer{ TB: tb, stopped: make(chan types.LayerID, 100), - eligibility: make(chan *types.HareEligibility), + eligibility: make(chan []*types.HareEligibility), sent: make(chan *Message), } } @@ -438,7 +478,7 @@ func newTestTracer(tb testing.TB) *testTracer { type testTracer struct { testing.TB stopped chan types.LayerID - eligibility chan *types.HareEligibility + eligibility chan []*types.HareEligibility sent chan *Message } @@ -453,7 +493,7 @@ func (t *testTracer) waitStopped() types.LayerID { return 0 } -func (t *testTracer) waitEligibility() *types.HareEligibility { +func (t *testTracer) waitEligibility() []*types.HareEligibility { wait := time.Second select { case <-time.After(wait): @@ -484,7 +524,7 @@ func (t *testTracer) OnStop(lid types.LayerID) { } } -func (t *testTracer) OnActive(el *types.HareEligibility) { +func (t *testTracer) OnActive(el []*types.HareEligibility) { wait := time.Second select { case <-time.After(wait): @@ -519,6 +559,10 @@ func testHare(t *testing.T, active, inactive, equivocators int, opts ...clusterO addActive(active). addInactive(inactive). addEquivocators(equivocators) + if cluster.signersCount > 0 { + cluster = cluster.addSigner(cluster.signersCount) + cluster.partitionSigners() + } layer := tst.genesis + 1 cluster.setup() @@ -561,6 +605,8 @@ func TestHare(t *testing.T) { t.Run("with units", func(t *testing.T) { testHare(t, 5, 0, 0, withUnits(10, 50)) }) t.Run("with inactive", func(t *testing.T) { testHare(t, 3, 2, 0) }) t.Run("equivocators", func(t *testing.T) { testHare(t, 4, 0, 1, withProposals(0.75)) }) + t.Run("one active multi signers", func(t *testing.T) { testHare(t, 1, 0, 0, withSigners(2)) }) + t.Run("three active multi signers", func(t *testing.T) { testHare(t, 3, 0, 0, withSigners(10)) }) } func TestIterationLimit(t *testing.T) { @@ -618,7 +664,8 @@ func TestHandler(t *testing.T) { n.clock.Advance((tst.start. Add(tst.layerDuration * time.Duration(layer)). Add(tst.cfg.PreroundDelay)).Sub(n.clock.Now())) - elig := n.tracer.waitEligibility() + elig := n.tracer.waitEligibility()[0] + n.tracer.waitSent() n.tracer.waitEligibility() @@ -899,7 +946,7 @@ func TestProposals(t *testing.T) { } { t.Run(tc.desc, func(t *testing.T) { db := datastore.NewCachedDB(sql.InMemory(), log.NewNop()) - hare := New(nil, nil, db, nil, signer, nil, nil, layerpatrol.New(), WithLogger(logtest.New(t).Zap())) + hare := New(nil, nil, db, nil, nil, nil, layerpatrol.New(), WithLogger(logtest.New(t).Zap())) for _, atx := range tc.atxs { require.NoError(t, atxs.Add(db, &atx)) } @@ -910,7 +957,11 @@ func TestProposals(t *testing.T) { for _, id := range tc.malicious { require.NoError(t, identities.SetMalicious(db, id, []byte("non empty"), time.Time{})) } - require.Equal(t, tc.expect, hare.proposals(tc.layer, tc.beacon)) + require.Equal(t, tc.expect, hare.proposals(&session{ + lid: tc.layer, + beacon: tc.beacon, + signers: []*signing.EdSigner{signer}, + })) }) } } diff --git a/hare3/legacy_oracle.go b/hare3/legacy_oracle.go index ce5ef793ac..3a3f35e848 100644 --- a/hare3/legacy_oracle.go +++ b/hare3/legacy_oracle.go @@ -8,12 +8,13 @@ import ( "github.com/spacemeshos/go-spacemesh/common/types" "github.com/spacemeshos/go-spacemesh/hare/eligibility" + "github.com/spacemeshos/go-spacemesh/signing" ) type oracle interface { Validate(context.Context, types.LayerID, uint32, int, types.NodeID, types.VrfSignature, uint16) (bool, error) CalcEligibility(context.Context, types.LayerID, uint32, int, types.NodeID, types.VrfSignature) (uint16, error) - Proof(context.Context, types.LayerID, uint32) (types.VrfSignature, error) + GenVRF(context.Context, *signing.VRFSigner, types.Beacon, types.LayerID, uint32) types.VrfSignature } type legacyOracle struct { @@ -43,17 +44,17 @@ func (lg *legacyOracle) validate(msg *Message) grade { return grade5 } -func (lg *legacyOracle) active(smesher types.NodeID, layer types.LayerID, ir IterRound) *types.HareEligibility { - vrf, err := lg.oracle.Proof(context.Background(), layer, ir.Absolute()) +func (lg *legacyOracle) active(signer *signing.EdSigner, beacon types.Beacon, layer types.LayerID, ir IterRound) *types.HareEligibility { + vrfs, err := signer.VRFSigner() if err != nil { - lg.log.Error("failed to compute vrf", zap.Error(err)) - return nil + panic("can't cast ed signer to vrf signer") } + vrf := lg.oracle.GenVRF(context.Background(), vrfs, beacon, layer, ir.Absolute()) committee := int(lg.config.Committee) if ir.Round == propose { committee = int(lg.config.Leaders) } - count, err := lg.oracle.CalcEligibility(context.Background(), layer, ir.Absolute(), committee, smesher, vrf) + count, err := lg.oracle.CalcEligibility(context.Background(), layer, ir.Absolute(), committee, signer.NodeID(), vrf) if err != nil { if !errors.Is(err, eligibility.ErrNotActive) { lg.log.Error("failed to compute eligibilities", zap.Error(err)) diff --git a/hare3/tracer.go b/hare3/tracer.go index 2ab82d7357..861c5f9e9a 100644 --- a/hare3/tracer.go +++ b/hare3/tracer.go @@ -5,7 +5,7 @@ import "github.com/spacemeshos/go-spacemesh/common/types" type Tracer interface { OnStart(types.LayerID) OnStop(types.LayerID) - OnActive(*types.HareEligibility) + OnActive([]*types.HareEligibility) OnMessageSent(*Message) OnMessageReceived(*Message) } @@ -18,7 +18,7 @@ func (noopTracer) OnStart(types.LayerID) {} func (noopTracer) OnStop(types.LayerID) {} -func (noopTracer) OnActive(*types.HareEligibility) {} +func (noopTracer) OnActive([]*types.HareEligibility) {} func (noopTracer) OnMessageSent(*Message) {} diff --git a/node/node.go b/node/node.go index b3541f83f5..330828c377 100644 --- a/node/node.go +++ b/node/node.go @@ -780,10 +780,11 @@ func (app *App) initServices(ctx context.Context) error { } logger := app.addLogger(HareLogger, lg).Zap() app.hare3 = hare3.New( - app.clock, app.host, app.cachedDB, app.edVerifier, app.edSgn, app.hOracle, newSyncer, patrol, + app.clock, app.host, app.cachedDB, app.edVerifier, app.hOracle, newSyncer, patrol, hare3.WithLogger(logger), hare3.WithConfig(app.Config.HARE3), ) + app.hare3.Register(app.edSgn) app.hare3.Start() app.eg.Go(func() error { compat.ReportWeakcoin(ctx, logger, app.hare3.Coins(), tortoiseWeakCoin{db: app.cachedDB, tortoise: trtl})