diff --git a/pkg/consensus/consensus.go b/pkg/consensus/consensus.go index 03a7a8efb..ebf8d4d25 100644 --- a/pkg/consensus/consensus.go +++ b/pkg/consensus/consensus.go @@ -81,6 +81,11 @@ type service struct { started *atomic.Bool quit chan struct{} finished chan struct{} + // lastTimestamp contains timestamp for the last processed block. + // We can't rely on timestamp from dbft context because it is changed + // before block is accepted, so in case of change view it will contain + // updated value. + lastTimestamp uint64 } // Config is a configuration for consensus services. @@ -298,6 +303,9 @@ func (s *service) handleChainBlock(b *coreb.Block) { s.log.Debug("new block in the chain", zap.Uint32("dbft index", s.dbft.BlockIndex), zap.Uint32("chain index", s.Chain.BlockHeight())) + if s.lastTimestamp < b.Timestamp { + s.lastTimestamp = b.Timestamp + } s.dbft.InitializeConsensus(0) } } @@ -418,6 +426,12 @@ func (s *service) verifyBlock(b block.Block) bool { s.log.Warn("proposed block has already outdated") return false } + if s.lastTimestamp >= coreb.Timestamp { + s.log.Warn("proposed block has small timestamp", + zap.Uint64("ts", coreb.Timestamp), + zap.Uint64("last", s.lastTimestamp)) + return false + } maxBlockSize := int(s.Chain.GetPolicer().GetMaxBlockSize()) size := io.GetVarSize(coreb) if size > maxBlockSize { @@ -492,6 +506,9 @@ func (s *service) processBlock(b block.Block) { s.log.Warn("error on add block", zap.Error(err)) } } + if s.lastTimestamp < bb.Timestamp { + s.lastTimestamp = bb.Timestamp + } } func (s *service) getBlockWitness(b *coreb.Block) *transaction.Witness { diff --git a/pkg/consensus/consensus_test.go b/pkg/consensus/consensus_test.go index d3983f9bf..99ae07bb0 100644 --- a/pkg/consensus/consensus_test.go +++ b/pkg/consensus/consensus_test.go @@ -344,6 +344,8 @@ func TestService_OnPayload(t *testing.T) { func TestVerifyBlock(t *testing.T) { srv := newTestService(t) defer srv.Chain.Close() + + srv.lastTimestamp = 1 t.Run("good empty", func(t *testing.T) { b := testchain.NewBlock(t, srv.Chain, 1, 0) require.True(t, srv.verifyBlock(&neoBlock{Block: *b})) @@ -386,6 +388,11 @@ func TestVerifyBlock(t *testing.T) { b.Index = srv.Chain.BlockHeight() require.False(t, srv.verifyBlock(&neoBlock{Block: *b})) }) + t.Run("bad timestamp", func(t *testing.T) { + b := testchain.NewBlock(t, srv.Chain, 1, 0) + b.Timestamp = srv.lastTimestamp - 1 + require.False(t, srv.verifyBlock(&neoBlock{Block: *b})) + }) t.Run("bad big size", func(t *testing.T) { script := make([]byte, int(srv.Chain.GetPolicer().GetMaxBlockSize())) script[0] = byte(opcode.RET)