consensus: validate timestamp in `verifyBlock()

Not doing this can possibly lead to the same node being validator
again and again.
This commit is contained in:
Evgenii Stratonikov 2020-12-16 13:24:49 +03:00
parent 4dcd06ef44
commit c5f9f6a3fd
2 changed files with 24 additions and 0 deletions

View file

@ -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 {

View file

@ -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)