diff --git a/proposals/handler.go b/proposals/handler.go index 0ef1ccc371..e2fba7036a 100644 --- a/proposals/handler.go +++ b/proposals/handler.go @@ -259,12 +259,13 @@ func (h *Handler) handleProposal(ctx context.Context, expHash types.Hash32, peer } if p.Layer <= types.GetEffectiveGenesis() { preGenesis.Inc() - return fmt.Errorf("proposal before effective genesis: layer %v", p.Layer) - } - if p.Layer <= h.mesh.ProcessedLayer() { - // old proposals have no use for the node + return fmt.Errorf("proposal before effective genesis: %d/%s", p.Layer, p.ID().String()) + } else if p.Layer <= h.mesh.ProcessedLayer() { tooLate.Inc() - return fmt.Errorf("proposal too late: layer %v", p.Layer) + return fmt.Errorf("proposal too late: %d/%s", p.Layer, p.ID().String()) + } else if p.Layer >= h.clock.CurrentLayer()+1 { + tooFuture.Inc() + return fmt.Errorf("proposal from future: %d/%s", p.Layer, p.ID().String()) } latency := receivedTime.Sub(h.clock.LayerToTime(p.Layer)) diff --git a/proposals/handler_test.go b/proposals/handler_test.go index f491b6e87f..172696848c 100644 --- a/proposals/handler_test.go +++ b/proposals/handler_test.go @@ -184,6 +184,13 @@ func withTransactions(ids ...types.TransactionID) createProposalOpt { } } +func withProposalLayer(layer types.LayerID) createProposalOpt { + return func(p *types.Proposal) { + p.Layer = layer + p.Ballot.Layer = layer + } +} + func createProposal(t *testing.T, opts ...any) *types.Proposal { t.Helper() b := types.RandomBallot() @@ -844,7 +851,7 @@ func TestBallot_DecodedStoreFailure(t *testing.T) { func TestProposal_MalformedData(t *testing.T) { th := createTestHandlerNoopDecoder(t) - p := createProposal(t) + p := createProposal(t, withProposalLayer(th.clock.CurrentLayer())) data, err := codec.Encode(&p.InnerProposal) require.NoError(t, err) require.ErrorIs(t, th.HandleSyncedProposal(context.Background(), p.ID().AsHash32(), p2p.NoPeer, data), errMalformedData) @@ -854,7 +861,7 @@ func TestProposal_MalformedData(t *testing.T) { func TestProposal_BeforeEffectiveGenesis(t *testing.T) { th := createTestHandlerNoopDecoder(t) - p := createProposal(t) + p := createProposal(t, withProposalLayer(th.clock.CurrentLayer())) p.Layer = types.GetEffectiveGenesis() data := encodeProposal(t, p) got := th.HandleSyncedProposal(context.Background(), p.ID().AsHash32(), p2p.NoPeer, data) @@ -868,7 +875,7 @@ func TestProposal_TooOld(t *testing.T) { th := createTestHandlerNoopDecoder(t) lid := types.LayerID(11) th.mockSet.setCurrentLayer(lid) - p := createProposal(t) + p := createProposal(t, withProposalLayer(th.clock.CurrentLayer())) p.Layer = lid - 1 data := encodeProposal(t, p) got := th.HandleSyncedProposal(context.Background(), p.ID().AsHash32(), p2p.NoPeer, data) @@ -878,9 +885,17 @@ func TestProposal_TooOld(t *testing.T) { checkProposal(t, th.cdb, p, false) } +func TestProposal_TooFuture(t *testing.T) { + th := createTestHandlerNoopDecoder(t) + p := createProposal(t, withProposalLayer(th.clock.CurrentLayer()+10)) + data := encodeProposal(t, p) + got := th.HandleSyncedProposal(context.Background(), p.ID().AsHash32(), p2p.NoPeer, data) + require.ErrorContains(t, got, "proposal from future") +} + func TestProposal_BadSignature(t *testing.T) { th := createTestHandlerNoopDecoder(t) - p := createProposal(t) + p := createProposal(t, withProposalLayer(th.clock.CurrentLayer())) p.Signature = types.EmptyEdSignature data := encodeProposal(t, p) got := th.HandleSyncedProposal(context.Background(), p.ID().AsHash32(), p2p.NoPeer, data) @@ -918,7 +933,7 @@ func TestProposal_InconsistentSmeshers(t *testing.T) { func TestProposal_WrongHash(t *testing.T) { th := createTestHandlerNoopDecoder(t) - p := createProposal(t) + p := createProposal(t, withProposalLayer(th.clock.CurrentLayer())) data := encodeProposal(t, p) err := th.HandleSyncedProposal(context.Background(), types.RandomHash(), p2p.NoPeer, data) require.ErrorIs(t, err, errWrongHash) @@ -927,7 +942,7 @@ func TestProposal_WrongHash(t *testing.T) { func TestProposal_KnownProposal(t *testing.T) { th := createTestHandlerNoopDecoder(t) - p := createProposal(t) + p := createProposal(t, withProposalLayer(th.clock.CurrentLayer())) createAtx(t, th.cdb.Database, p.Layer.GetEpoch()-1, p.AtxID, p.SmesherID) require.NoError(t, ballots.Add(th.cdb, &p.Ballot)) require.NoError(t, proposals.Add(th.cdb, p)) @@ -1430,6 +1445,7 @@ func TestHandleSyncedProposalActiveSet(t *testing.T) { th := createTestHandler(t) pid := p2p.Peer("any") + th.mclock.EXPECT().CurrentLayer().Return(lid).AnyTimes() th.mm.EXPECT().ProcessedLayer().Return(lid - 2).AnyTimes() th.mclock.EXPECT().LayerToTime(gomock.Any()) th.mf.EXPECT().RegisterPeerHashes(pid, gomock.Any()).AnyTimes() diff --git a/proposals/metrics.go b/proposals/metrics.go index 006fee1977..17b994e120 100644 --- a/proposals/metrics.go +++ b/proposals/metrics.go @@ -94,6 +94,7 @@ var ( failedInit = processErrors.WithLabelValues("init") known = processErrors.WithLabelValues("known") tooLate = processErrors.WithLabelValues("late") + tooFuture = processErrors.WithLabelValues("future") preGenesis = processErrors.WithLabelValues("genesis") badSigProposal = processErrors.WithLabelValues("sigp") badSigBallot = processErrors.WithLabelValues("sigb")