From 4809cdf0b05bb658cece18c38b5ff6544e0b010c Mon Sep 17 00:00:00 2001
From: Anna Shaleva <shaleva.ann@nspcc.ru>
Date: Thu, 18 Mar 2021 18:05:24 +0300
Subject: [PATCH] consensus: fix panic during verifyBlock

Issue: panic during mixed 4-nodes consensus setup:
```
2021-03-18T12:01:50.715Z	INFO	skip change view	{"nc": 0, "nf": 3}
2021-03-18T12:01:52.786Z	INFO	received ChangeView	{"validator": 0, "reason": "Timeout", "new view": 1}
2021-03-18T12:01:53.602Z	INFO	received ChangeView	{"validator": 2, "reason": "Timeout", "new view": 1}
2021-03-18T12:01:56.736Z	INFO	received ChangeView	{"validator": 1, "reason": "Timeout", "new view": 1}
2021-03-18T12:01:56.736Z	INFO	changing dbft view	{"height": 3, "view": 1, "index": 3, "role": "Backup"}
2021-03-18T12:02:01.758Z	INFO	received PrepareRequest	{"validator": 2, "tx": 0}
panic: interface conversion: block.Block is nil, not *consensus.neoBlock

goroutine 315 [running]:
github.com/nspcc-dev/neo-go/pkg/consensus.(*service).verifyBlock(0xc000419540, 0x0, 0x0, 0x4)
	github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:427 +0x1306
github.com/nspcc-dev/dbft.(*DBFT).createAndCheckBlock(0xc0001f8840, 0x13f0002)
	github.com/nspcc-dev/dbft@v0.0.0-20210302103605-cc75991b7cfb/dbft.go:373 +0x27e
github.com/nspcc-dev/dbft.(*DBFT).onPrepareRequest(0xc0001f8840, 0x13f4378, 0xc0003b8500)
	github.com/nspcc-dev/dbft@v0.0.0-20210302103605-cc75991b7cfb/dbft.go:329 +0xdf1
github.com/nspcc-dev/dbft.(*DBFT).OnReceive(0xc0001f8840, 0x13f4378, 0xc0003b8500)
	github.com/nspcc-dev/dbft@v0.0.0-20210302103605-cc75991b7cfb/dbft.go:247 +0xe25
github.com/nspcc-dev/neo-go/pkg/consensus.(*service).eventLoop(0xc000419540)
	github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:297 +0x79d
created by github.com/nspcc-dev/neo-go/pkg/consensus.(*service).Start
	github.com/nspcc-dev/neo-go/pkg/consensus/consensus.go:249 +0xa5
```

So (*service).verifyBlock is unable to work with nil block.
---
 pkg/consensus/consensus.go | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/pkg/consensus/consensus.go b/pkg/consensus/consensus.go
index a87b1fe4a..cff3c95da 100644
--- a/pkg/consensus/consensus.go
+++ b/pkg/consensus/consensus.go
@@ -648,9 +648,6 @@ func convertKeys(validators []crypto.PublicKey) (pubs []*keys.PublicKey) {
 
 func (s *service) newBlockFromContext(ctx *dbft.Context) block.Block {
 	block := new(neoBlock)
-	if ctx.TransactionHashes == nil {
-		return nil
-	}
 
 	block.Block.Network = s.ProtocolConfiguration.Magic
 	block.Block.Timestamp = ctx.Timestamp / nsInMs
@@ -658,7 +655,7 @@ func (s *service) newBlockFromContext(ctx *dbft.Context) block.Block {
 	if s.ProtocolConfiguration.StateRootInHeader {
 		sr, err := s.Chain.GetStateModule().GetStateRoot(ctx.BlockIndex - 1)
 		if err != nil {
-			return nil
+			s.log.Fatal(fmt.Sprintf("failed to get state root: %s", err.Error()))
 		}
 		block.StateRootEnabled = true
 		block.PrevStateRoot = sr.Root
@@ -672,11 +669,11 @@ func (s *service) newBlockFromContext(ctx *dbft.Context) block.Block {
 		validators, err = s.Chain.GetNextBlockValidators()
 	}
 	if err != nil {
-		return nil
+		s.log.Fatal(fmt.Sprintf("failed to get validators: %s", err.Error()))
 	}
 	script, err := smartcontract.CreateMultiSigRedeemScript(s.dbft.Context.M(), validators)
 	if err != nil {
-		return nil
+		s.log.Fatal(fmt.Sprintf("failed to create multisignature script: %s", err.Error()))
 	}
 	block.Block.NextConsensus = crypto.Hash160(script)
 	block.Block.PrevHash = ctx.PrevHash
@@ -685,6 +682,7 @@ func (s *service) newBlockFromContext(ctx *dbft.Context) block.Block {
 	primaryIndex := byte(ctx.PrimaryIndex)
 	block.Block.PrimaryIndex = primaryIndex
 
+	// it's OK to have ctx.TransactionsHashes == nil here
 	hashes := make([]util.Uint256, len(ctx.TransactionHashes))
 	copy(hashes, ctx.TransactionHashes)
 	block.Block.MerkleRoot = hash.CalcMerkleRoot(hashes)