Merge pull request #1620 from nspcc-dev/fix/consensus

consensus: validate timestamp in `verifyBlock()`
This commit is contained in:
Roman Khimov 2020-12-16 19:13:15 +03:00 committed by GitHub
commit f2365e2392
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 27 additions and 3 deletions

2
go.mod
View file

@ -11,7 +11,7 @@ require (
github.com/gorilla/websocket v1.4.2
github.com/hashicorp/golang-lru v0.5.4
github.com/mr-tron/base58 v1.1.2
github.com/nspcc-dev/dbft v0.0.0-20201109143252-cd27d76617ed
github.com/nspcc-dev/dbft v0.0.0-20201216144126-a4bc58feae87
github.com/nspcc-dev/rfc6979 v0.2.0
github.com/pierrec/lz4 v2.5.2+incompatible
github.com/prometheus/client_golang v1.2.1

4
go.sum
View file

@ -166,8 +166,8 @@ github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a h1:ajvxgEe9qY4vvoSm
github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk=
github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1 h1:yEx9WznS+rjE0jl0dLujCxuZSIb+UTjF+005TJu/nNI=
github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ=
github.com/nspcc-dev/dbft v0.0.0-20201109143252-cd27d76617ed h1:T4qjutPMqjnYDQFyrbCew3lGeJt6MIbNyNn7gRx0o/g=
github.com/nspcc-dev/dbft v0.0.0-20201109143252-cd27d76617ed/go.mod h1:I5D0W3tu3epdt2RMCTxS//HDr4S+OHRqajouQTOAHI8=
github.com/nspcc-dev/dbft v0.0.0-20201216144126-a4bc58feae87 h1:sKV7BKdp2iDW39AkGp0GPGHRDuR9vgQT7gDdMKMkuOI=
github.com/nspcc-dev/dbft v0.0.0-20201216144126-a4bc58feae87/go.mod h1:I5D0W3tu3epdt2RMCTxS//HDr4S+OHRqajouQTOAHI8=
github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg=
github.com/nspcc-dev/neofs-crypto v0.2.0 h1:ftN+59WqxSWz/RCgXYOfhmltOOqU+udsNQSvN6wkFck=
github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA=

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)