diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 1c2c6cba7d28..eab5ea2d7a47 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -689,16 +689,25 @@ func (x *XDPoS_v2) ProposedBlockHandler(chain consensus.ChainReader, blockHeader */ // To be used by different message verification. Verify local DB block info against the received block information(i.e hash, blockNum, round) -func (x *XDPoS_v2) VerifyBlockInfo(blockChainReader consensus.ChainReader, blockInfo *utils.BlockInfo) error { +func (x *XDPoS_v2) VerifyBlockInfo(blockChainReader consensus.ChainReader, blockInfo *utils.BlockInfo, blockHeader *types.Header) error { /* 1. Check if is able to get header by hash from the chain 2. Check the header from step 1 matches what's in the blockInfo. This includes the block number and the round */ - blockHeader := blockChainReader.GetHeaderByHash(blockInfo.Hash) if blockHeader == nil { - log.Warn("[VerifyBlockInfo] No such header in the chain", "BlockInfoHash", blockInfo.Hash.Hex(), "BlockInfoNum", blockInfo.Number, "BlockInfoRound", blockInfo.Round, "currentHeaderNum", blockChainReader.CurrentHeader().Number) - return fmt.Errorf("[VerifyBlockInfo] header doesn't exist for the received blockInfo at hash: %v", blockInfo.Hash.Hex()) + blockHeader = blockChainReader.GetHeaderByHash(blockInfo.Hash) + if blockHeader == nil { + log.Warn("[VerifyBlockInfo] No such header in the chain", "BlockInfoHash", blockInfo.Hash.Hex(), "BlockInfoNum", blockInfo.Number, "BlockInfoRound", blockInfo.Round, "currentHeaderNum", blockChainReader.CurrentHeader().Number) + return fmt.Errorf("[VerifyBlockInfo] header doesn't exist for the received blockInfo at hash: %v", blockInfo.Hash.Hex()) + } + } else { + // If blockHeader present, then its value shall consistent with what's provided in the blockInfo + if blockHeader.Hash() != blockInfo.Hash { + log.Warn("[VerifyBlockInfo] BlockHeader and blockInfo mismatch", "BlockInfoHash", blockInfo.Hash.Hex(), "BlockHeaderHash", blockHeader.Hash()) + return fmt.Errorf("[VerifyBlockInfo] Provided blockheader does not match what's in the blockInfo") + } } + if blockHeader.Number.Cmp(blockInfo.Number) != 0 { log.Warn("[VerifyBlockInfo] Block Number mismatch", "BlockInfoHash", blockInfo.Hash.Hex(), "BlockInfoNum", blockInfo.Number, "BlockInfoRound", blockInfo.Round, "blockHeaderNum", blockHeader.Number) return fmt.Errorf("[VerifyBlockInfo] chain header number does not match for the received blockInfo at hash: %v", blockInfo.Hash.Hex()) @@ -792,7 +801,8 @@ func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert * log.Error("[verifyQC] gap number mismatch", "BlockInfoHash", quorumCert.ProposedBlockInfo.Hash, "Gap", quorumCert.GapNumber, "GapShouldBe", gapNumber) return fmt.Errorf("gap number mismatch %v", quorumCert) } - return x.VerifyBlockInfo(blockChainReader, quorumCert.ProposedBlockInfo) + + return x.VerifyBlockInfo(blockChainReader, quorumCert.ProposedBlockInfo, parentHeader) } // Update local QC variables including highestQC & lockQuorumCert, as well as commit the blocks that satisfy the algorithm requirements diff --git a/consensus/XDPoS/engines/engine_v2/vote.go b/consensus/XDPoS/engines/engine_v2/vote.go index cfce42ad33d5..dc6f3c94ad54 100644 --- a/consensus/XDPoS/engines/engine_v2/vote.go +++ b/consensus/XDPoS/engines/engine_v2/vote.go @@ -77,7 +77,7 @@ func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *utils.Vote) return nil } - err := x.VerifyBlockInfo(chain, voteMsg.ProposedBlockInfo) + err := x.VerifyBlockInfo(chain, voteMsg.ProposedBlockInfo, nil) if err != nil { return err } diff --git a/consensus/tests/engine_v2_tests/verify_blockinfo_test.go b/consensus/tests/engine_v2_tests/verify_blockinfo_test.go index e3f72854403b..57155bd0e83d 100644 --- a/consensus/tests/engine_v2_tests/verify_blockinfo_test.go +++ b/consensus/tests/engine_v2_tests/verify_blockinfo_test.go @@ -20,7 +20,7 @@ func TestShouldVerifyBlockInfo(t *testing.T) { Round: utils.Round(1), Number: currentBlock.Number(), } - err := engineV2.VerifyBlockInfo(blockchain, blockInfo) + err := engineV2.VerifyBlockInfo(blockchain, blockInfo, nil) assert.Nil(t, err) // Insert another Block, but it won't trigger commit @@ -35,7 +35,7 @@ func TestShouldVerifyBlockInfo(t *testing.T) { Round: utils.Round(2), Number: block902.Number(), } - err = engineV2.VerifyBlockInfo(blockchain, blockInfo) + err = engineV2.VerifyBlockInfo(blockchain, blockInfo, nil) assert.Nil(t, err) blockInfo = &utils.BlockInfo{ @@ -43,7 +43,7 @@ func TestShouldVerifyBlockInfo(t *testing.T) { Round: utils.Round(2), Number: currentBlock.Number(), } - err = engineV2.VerifyBlockInfo(blockchain, blockInfo) + err = engineV2.VerifyBlockInfo(blockchain, blockInfo, nil) assert.NotNil(t, err) blockInfo = &utils.BlockInfo{ @@ -51,7 +51,7 @@ func TestShouldVerifyBlockInfo(t *testing.T) { Round: utils.Round(3), Number: block902.Number(), } - err = engineV2.VerifyBlockInfo(blockchain, blockInfo) + err = engineV2.VerifyBlockInfo(blockchain, blockInfo, nil) assert.NotNil(t, err) blockInfo = &utils.BlockInfo{ @@ -59,6 +59,6 @@ func TestShouldVerifyBlockInfo(t *testing.T) { Round: utils.Round(2), Number: currentBlock.Number(), } - err = engineV2.VerifyBlockInfo(blockchain, blockInfo) + err = engineV2.VerifyBlockInfo(blockchain, blockInfo, nil) assert.NotNil(t, err) } diff --git a/consensus/tests/engine_v2_tests/verify_header_test.go b/consensus/tests/engine_v2_tests/verify_header_test.go index 2c0665ca6cbc..a380b1c69377 100644 --- a/consensus/tests/engine_v2_tests/verify_header_test.go +++ b/consensus/tests/engine_v2_tests/verify_header_test.go @@ -235,9 +235,6 @@ func TestShouldVerifyHeaders(t *testing.T) { blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, 0) adaptor := blockchain.Engine().(*XDPoS.XDPoS) - // var results <-chan error - // var abort <-chan struct{} - // Happy path var happyPathHeaders []*types.Header happyPathHeaders = append(happyPathHeaders, blockchain.GetBlockByNumber(899).Header(), blockchain.GetBlockByNumber(900).Header(), blockchain.GetBlockByNumber(901).Header(), blockchain.GetBlockByNumber(902).Header()) @@ -245,10 +242,72 @@ func TestShouldVerifyHeaders(t *testing.T) { var fullVerifies []bool fullVerifies = append(fullVerifies, false, true, true, false) _, results := adaptor.VerifyHeaders(blockchain, happyPathHeaders, fullVerifies) - select { - case result := <-results: - assert.Nil(t, result) - case <-time.After(time.Duration(2) * time.Second): // It should be very fast to verify headers - t.Fatalf("Taking too long to verify headers") + var verified []bool + for { + select { + case result := <-results: + if result != nil { + panic("Error received while verifying headers") + } + verified = append(verified, true) + case <-time.After(time.Duration(5) * time.Second): // It should be very fast to verify headers + if len(verified) == len(happyPathHeaders) { + return + } else { + panic("Suppose to have verified 3 block headers") + } + } + } +} + +func TestShouldVerifyHeadersEvenIfParentsNotYetWrittenIntoDB(t *testing.T) { + b, err := json.Marshal(params.TestXDPoSMockChainConfig) + assert.Nil(t, err) + configString := string(b) + + var config params.ChainConfig + err = json.Unmarshal([]byte(configString), &config) + assert.Nil(t, err) + // Enable verify + config.XDPoS.V2.SkipV2Validation = false + // Skip the mining time validation by set mine time to 0 + config.XDPoS.V2.MinePeriod = 0 + // Block 901 is the first v2 block with round of 1 + blockchain, _, block910, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, 0) + adaptor := blockchain.Engine().(*XDPoS.XDPoS) + + var headersTobeVerified []*types.Header + + // Create block 911 but don't write into DB + blockNumber := 911 + roundNumber := int64(blockNumber) - config.XDPoS.V2.SwitchBlock.Int64() + block911 := CreateBlock(blockchain, &config, block910, blockNumber, roundNumber, signer.Hex(), signer, signFn, nil) + + // Create block 912 and not write into DB as well + blockNumber = 912 + roundNumber = int64(blockNumber) - config.XDPoS.V2.SwitchBlock.Int64() + block912 := CreateBlock(blockchain, &config, block911, blockNumber, roundNumber, signer.Hex(), signer, signFn, nil) + + headersTobeVerified = append(headersTobeVerified, block910.Header(), block911.Header(), block912.Header()) + // Randomly set full verify + var fullVerifies []bool + fullVerifies = append(fullVerifies, true, true, true) + _, results := adaptor.VerifyHeaders(blockchain, headersTobeVerified, fullVerifies) + + var verified []bool + for { + select { + case result := <-results: + if result != nil { + panic("Error received while verifying headers") + } + verified = append(verified, true) + case <-time.After(time.Duration(5) * time.Second): // It should be very fast to verify headers + if len(verified) == len(headersTobeVerified) { + return + } else { + panic("Suppose to have verified 3 block headers") + } + } } }