diff --git a/snow/engine/snowman/issuer.go b/snow/engine/snowman/issuer.go index ca69064105e1..697bc302e730 100644 --- a/snow/engine/snowman/issuer.go +++ b/snow/engine/snowman/issuer.go @@ -14,6 +14,7 @@ import ( // issuer issues [blk] into to consensus after its dependencies are met. type issuer struct { t *Transitive + nodeID ids.NodeID // nodeID of the peer that provided this block blk snowman.Block abandoned bool deps set.Set[ids.ID] @@ -51,5 +52,5 @@ func (i *issuer) Update(ctx context.Context) { return } // Issue the block into consensus - i.t.errs.Add(i.t.deliver(ctx, i.blk, i.push)) + i.t.errs.Add(i.t.deliver(ctx, i.nodeID, i.blk, i.push)) } diff --git a/snow/engine/snowman/metrics.go b/snow/engine/snowman/metrics.go index ae7cc66cfbfb..a0e5e5b565fe 100644 --- a/snow/engine/snowman/metrics.go +++ b/snow/engine/snowman/metrics.go @@ -27,6 +27,7 @@ type metrics struct { numProcessingAncestorFetchesUnneeded prometheus.Counter getAncestorsBlks metric.Averager selectedVoteIndex metric.Averager + issuerStake metric.Averager } func (m *metrics) Initialize(namespace string, reg prometheus.Registerer) error { @@ -115,6 +116,13 @@ func (m *metrics) Initialize(namespace string, reg prometheus.Registerer) error reg, &errs, ) + m.issuerStake = metric.NewAveragerWithErrs( + namespace, + "issuer_stake", + "stake weight of the peer who provided a block that was issued into consensus", + reg, + &errs, + ) errs.Add( reg.Register(m.bootstrapFinished), diff --git a/snow/engine/snowman/transitive.go b/snow/engine/snowman/transitive.go index 76a9d218ca65..fd2ac3a86b75 100644 --- a/snow/engine/snowman/transitive.go +++ b/snow/engine/snowman/transitive.go @@ -444,7 +444,7 @@ func (t *Transitive) Start(ctx context.Context, startReqID uint32) error { default: for _, blk := range options { // note that deliver will set the VM's preference - if err := t.deliver(ctx, blk, false); err != nil { + if err := t.deliver(ctx, t.Ctx.NodeID, blk, false); err != nil { return err } } @@ -650,7 +650,7 @@ func (t *Transitive) issueFrom(ctx context.Context, nodeID ids.NodeID, blk snowm // issue [blk] and its ancestors to consensus. blkID := blk.ID() for !t.wasIssued(blk) { - if err := t.issue(ctx, blk, false); err != nil { + if err := t.issue(ctx, nodeID, blk, false); err != nil { return false, err } @@ -690,7 +690,7 @@ func (t *Transitive) issueWithAncestors(ctx context.Context, blk snowman.Block) // issue [blk] and its ancestors into consensus status := blk.Status() for status.Fetched() && !t.wasIssued(blk) { - err := t.issue(ctx, blk, true) + err := t.issue(ctx, t.Ctx.NodeID, blk, true) if err != nil { return false, err } @@ -732,7 +732,7 @@ func (t *Transitive) wasIssued(blk snowman.Block) bool { // Issue [blk] to consensus once its ancestors have been issued. // If [push] is true, a push query will be used. Otherwise, a pull query will be // used. -func (t *Transitive) issue(ctx context.Context, blk snowman.Block, push bool) error { +func (t *Transitive) issue(ctx context.Context, nodeID ids.NodeID, blk snowman.Block, push bool) error { blkID := blk.ID() // mark that the block is queued to be added to consensus once its ancestors have been @@ -743,9 +743,10 @@ func (t *Transitive) issue(ctx context.Context, blk snowman.Block, push bool) er // Will add [blk] to consensus once its ancestors have been i := &issuer{ - t: t, - blk: blk, - push: push, + t: t, + nodeID: nodeID, + blk: blk, + push: push, } // block on the parent if needed @@ -849,7 +850,7 @@ func (t *Transitive) sendQuery( // issue [blk] to consensus // If [push] is true, a push query will be used. Otherwise, a pull query will be // used. -func (t *Transitive) deliver(ctx context.Context, blk snowman.Block, push bool) error { +func (t *Transitive) deliver(ctx context.Context, nodeID ids.NodeID, blk snowman.Block, push bool) error { blkID := blk.ID() if t.Consensus.Decided(blk) || t.Consensus.Processing(blkID) { return nil @@ -875,7 +876,7 @@ func (t *Transitive) deliver(ctx context.Context, blk snowman.Block, push bool) // By ensuring that the parent is either processing or accepted, it is // guaranteed that the parent was successfully verified. This means that // calling Verify on this block is allowed. - blkAdded, err := t.addUnverifiedBlockToConsensus(ctx, blk) + blkAdded, err := t.addUnverifiedBlockToConsensus(ctx, nodeID, blk) if err != nil { return err } @@ -899,7 +900,7 @@ func (t *Transitive) deliver(ctx context.Context, blk snowman.Block, push bool) } for _, blk := range options { - blkAdded, err := t.addUnverifiedBlockToConsensus(ctx, blk) + blkAdded, err := t.addUnverifiedBlockToConsensus(ctx, nodeID, blk) if err != nil { return err } @@ -979,12 +980,13 @@ func (t *Transitive) addToNonVerifieds(blk snowman.Block) { // addUnverifiedBlockToConsensus returns whether the block was added and an // error if one occurred while adding it to consensus. -func (t *Transitive) addUnverifiedBlockToConsensus(ctx context.Context, blk snowman.Block) (bool, error) { +func (t *Transitive) addUnverifiedBlockToConsensus(ctx context.Context, nodeID ids.NodeID, blk snowman.Block) (bool, error) { blkID := blk.ID() // make sure this block is valid if err := blk.Verify(ctx); err != nil { t.Ctx.Log.Debug("block verification failed", + zap.Stringer("nodeID", nodeID), zap.Stringer("blkID", blkID), zap.Error(err), ) @@ -997,7 +999,9 @@ func (t *Transitive) addUnverifiedBlockToConsensus(ctx context.Context, blk snow t.nonVerifieds.Remove(blkID) t.nonVerifiedCache.Evict(blkID) t.metrics.numNonVerifieds.Set(float64(t.nonVerifieds.Len())) + t.metrics.issuerStake.Observe(float64(t.Validators.GetWeight(t.Ctx.SubnetID, nodeID))) t.Ctx.Log.Verbo("adding block to consensus", + zap.Stringer("nodeID", nodeID), zap.Stringer("blkID", blkID), ) return true, t.Consensus.Add(ctx, &memoryBlock{ diff --git a/snow/engine/snowman/transitive_test.go b/snow/engine/snowman/transitive_test.go index 405f1abd3666..62662ed66077 100644 --- a/snow/engine/snowman/transitive_test.go +++ b/snow/engine/snowman/transitive_test.go @@ -412,7 +412,7 @@ func TestEngineMultipleQuery(t *testing.T) { } } - require.NoError(te.issue(context.Background(), blk0, false)) + require.NoError(te.issue(context.Background(), te.Ctx.NodeID, blk0, false)) blk1 := &snowman.TestBlock{ TestDecidable: choices.TestDecidable{ @@ -522,10 +522,10 @@ func TestEngineBlockedIssue(t *testing.T) { } } - require.NoError(te.issue(context.Background(), blk1, false)) + require.NoError(te.issue(context.Background(), te.Ctx.NodeID, blk1, false)) blk0.StatusV = choices.Processing - require.NoError(te.issue(context.Background(), blk0, false)) + require.NoError(te.issue(context.Background(), te.Ctx.NodeID, blk0, false)) require.Equal(blk1.ID(), te.Consensus.Preference()) } @@ -558,7 +558,7 @@ func TestEngineAbandonResponse(t *testing.T) { return nil, errUnknownBlock } - require.NoError(te.issue(context.Background(), blk, false)) + require.NoError(te.issue(context.Background(), te.Ctx.NodeID, blk, false)) require.NoError(te.QueryFailed(context.Background(), vdr, 1)) require.Empty(te.blocked) @@ -797,7 +797,7 @@ func TestVoteCanceling(t *testing.T) { require.Equal(uint64(1), requestedHeight) } - require.NoError(te.issue(context.Background(), blk, true)) + require.NoError(te.issue(context.Background(), te.Ctx.NodeID, blk, true)) require.Equal(1, te.polls.Len()) @@ -858,7 +858,7 @@ func TestEngineNoQuery(t *testing.T) { BytesV: []byte{1}, } - require.NoError(te.issue(context.Background(), blk, false)) + require.NoError(te.issue(context.Background(), te.Ctx.NodeID, blk, false)) } func TestEngineNoRepollQuery(t *testing.T) { @@ -961,7 +961,7 @@ func TestEngineAbandonChit(t *testing.T) { reqID = requestID } - require.NoError(te.issue(context.Background(), blk, false)) + require.NoError(te.issue(context.Background(), te.Ctx.NodeID, blk, false)) fakeBlkID := ids.GenerateTestID() vm.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { @@ -1016,7 +1016,7 @@ func TestEngineAbandonChitWithUnexpectedPutBlock(t *testing.T) { reqID = requestID } - require.NoError(te.issue(context.Background(), blk, true)) + require.NoError(te.issue(context.Background(), te.Ctx.NodeID, blk, true)) fakeBlkID := ids.GenerateTestID() vm.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { @@ -1099,7 +1099,7 @@ func TestEngineBlockingChitRequest(t *testing.T) { return blockingBlk, nil } - require.NoError(te.issue(context.Background(), parentBlk, false)) + require.NoError(te.issue(context.Background(), te.Ctx.NodeID, parentBlk, false)) sender.CantSendChits = false @@ -1110,7 +1110,7 @@ func TestEngineBlockingChitRequest(t *testing.T) { sender.CantSendPullQuery = false missingBlk.StatusV = choices.Processing - require.NoError(te.issue(context.Background(), missingBlk, false)) + require.NoError(te.issue(context.Background(), te.Ctx.NodeID, missingBlk, false)) require.Empty(te.blocked) } @@ -1163,7 +1163,7 @@ func TestEngineBlockingChitResponse(t *testing.T) { } } - require.NoError(te.issue(context.Background(), blockingBlk, false)) + require.NoError(te.issue(context.Background(), te.Ctx.NodeID, blockingBlk, false)) queryRequestID := new(uint32) sender.SendPullQueryF = func(_ context.Context, inVdrs set.Set[ids.NodeID], requestID uint32, blkID ids.ID, requestedHeight uint64) { @@ -1174,7 +1174,7 @@ func TestEngineBlockingChitResponse(t *testing.T) { require.Equal(uint64(1), requestedHeight) } - require.NoError(te.issue(context.Background(), issuedBlk, false)) + require.NoError(te.issue(context.Background(), te.Ctx.NodeID, issuedBlk, false)) sender.SendPushQueryF = nil sender.CantSendPushQuery = false @@ -1185,7 +1185,7 @@ func TestEngineBlockingChitResponse(t *testing.T) { sender.CantSendPullQuery = false missingBlk.StatusV = choices.Processing - require.NoError(te.issue(context.Background(), missingBlk, false)) + require.NoError(te.issue(context.Background(), te.Ctx.NodeID, missingBlk, false)) } func TestEngineRetryFetch(t *testing.T) { @@ -1281,9 +1281,9 @@ func TestEngineUndeclaredDependencyDeadlock(t *testing.T) { return nil, errUnknownBlock } } - require.NoError(te.issue(context.Background(), validBlk, false)) + require.NoError(te.issue(context.Background(), te.Ctx.NodeID, validBlk, false)) sender.SendPushQueryF = nil - require.NoError(te.issue(context.Background(), invalidBlk, false)) + require.NoError(te.issue(context.Background(), te.Ctx.NodeID, invalidBlk, false)) require.NoError(te.Chits(context.Background(), vdr, *reqID, invalidBlkID, invalidBlkID, invalidBlkID)) require.Equal(choices.Accepted, validBlk.Status()) @@ -1666,7 +1666,7 @@ func TestEngineDoubleChit(t *testing.T) { require.Equal(blk.ID(), blkID) require.Equal(uint64(1), requestedHeight) } - require.NoError(te.issue(context.Background(), blk, false)) + require.NoError(te.issue(context.Background(), te.Ctx.NodeID, blk, false)) vm.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { switch id { @@ -2785,7 +2785,7 @@ func TestEngineApplyAcceptedFrontierInQueryFailed(t *testing.T) { require.Equal(uint64(1), requestedHeight) } - require.NoError(te.issue(context.Background(), blk, true)) + require.NoError(te.issue(context.Background(), te.Ctx.NodeID, blk, true)) vm.GetBlockF = func(_ context.Context, id ids.ID) (snowman.Block, error) { switch id {