diff --git a/consensus/XDPoS/engines/engine_v2/epochSwitch.go b/consensus/XDPoS/engines/engine_v2/epochSwitch.go index e6f75a3158..61739e4a7f 100644 --- a/consensus/XDPoS/engines/engine_v2/epochSwitch.go +++ b/consensus/XDPoS/engines/engine_v2/epochSwitch.go @@ -78,6 +78,10 @@ func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types log.Error("[getEpochSwitchInfo] get extra field", "err", err, "number", h.Number.Uint64()) return nil, err } + if len(masternodes) == 0 { + return nil, fmt.Errorf("masternodes list is empty at epoch switch block %v", h.Number.Uint64()) + } + snap, err := x.getSnapshot(chain, h.Number.Uint64(), false) if err != nil { log.Error("[getEpochSwitchInfo] Adaptor v2 getSnapshot has error", "err", err) @@ -110,11 +114,13 @@ func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types x.epochSwitches.Add(hash, epochSwitchInfo) return epochSwitchInfo, nil } + epochSwitchInfo, err = x.getEpochSwitchInfo(chain, nil, h.ParentHash) if err != nil { log.Error("[getEpochSwitchInfo] recursive error", "err", err, "hash", hash.Hex(), "number", h.Number.Uint64()) return nil, err } + log.Debug("[getEpochSwitchInfo] get epoch switch info recursively", "hash", hash.Hex(), "number", h.Number.Uint64()) x.epochSwitches.Add(hash, epochSwitchInfo) return epochSwitchInfo, nil diff --git a/consensus/XDPoS/engines/engine_v2/syncInfo.go b/consensus/XDPoS/engines/engine_v2/syncInfo.go index 7c6b34d001..38838e3704 100644 --- a/consensus/XDPoS/engines/engine_v2/syncInfo.go +++ b/consensus/XDPoS/engines/engine_v2/syncInfo.go @@ -31,9 +31,9 @@ func (x *XDPoS_v2) VerifySyncInfoMessage(chain consensus.ChainReader, syncInfo * return false, nil } - snapshot, err := x.getSnapshot(chain, qc.GapNumber, true) + epochInfo, err := x.getEpochSwitchInfo(chain, nil, qc.ProposedBlockInfo.Hash) if err != nil { - log.Error("[VerifySyncInfoMessage] fail to get snapshot for a syncInfo message", "blockNum", qc.ProposedBlockInfo.Number, "blockHash", qc.ProposedBlockInfo.Hash, "error", err) + log.Error("[VerifySyncInfoMessage] fail to get epochInfo for qc syncInfo message", "blockNum", qc.ProposedBlockInfo.Number, "blockHash", qc.ProposedBlockInfo.Hash, "error", err) return false, err } @@ -42,16 +42,16 @@ func (x *XDPoS_v2) VerifySyncInfoMessage(chain consensus.ChainReader, syncInfo * GapNumber: qc.GapNumber, }) - if err := x.verifySignatures(voteSigHash, qc.Signatures, snapshot.NextEpochCandidates); err != nil { + if err := x.verifySignatures(voteSigHash, qc.Signatures, epochInfo.Masternodes); err != nil { log.Warn("[VerifySyncInfoMessage] SyncInfo message verification failed due to QC", "blockNum", qc.ProposedBlockInfo.Number, "gapNum", qc.GapNumber, "round", qc.ProposedBlockInfo.Round, "error", err) return false, err } if tc != nil { // tc is optional, when the node is starting up there is no TC at the memory - snapshot, err = x.getSnapshot(chain, tc.GapNumber, true) + epochInfo, err := x.getTCEpochInfo(chain, tc.Round) if err != nil { - log.Error("[VerifySyncInfoMessage] Fail to get snapshot when verifying TC!", "tcGapNumber", tc.GapNumber) - return false, fmt.Errorf("[VerifySyncInfoMessage] Unable to get snapshot, %s", err) + log.Error("[VerifySyncInfoMessage] Fail to get epochInfo for tc syncInfo message", "tcRound", tc.Round, "error", err) + return false, err } signedTimeoutObj := types.TimeoutSigHash(&types.TimeoutForSign{ @@ -59,7 +59,7 @@ func (x *XDPoS_v2) VerifySyncInfoMessage(chain consensus.ChainReader, syncInfo * GapNumber: tc.GapNumber, }) - if err := x.verifySignatures(signedTimeoutObj, tc.Signatures, snapshot.NextEpochCandidates); err != nil { + if err := x.verifySignatures(signedTimeoutObj, tc.Signatures, epochInfo.Masternodes); err != nil { log.Warn("[VerifySyncInfoMessage] SyncInfo message verification failed due to TC", "gapNum", tc.GapNumber, "round", tc.Round, "error", err) return false, err } diff --git a/consensus/XDPoS/engines/engine_v2/timeout.go b/consensus/XDPoS/engines/engine_v2/timeout.go index 15f030cdd9..15f3028d1d 100644 --- a/consensus/XDPoS/engines/engine_v2/timeout.go +++ b/consensus/XDPoS/engines/engine_v2/timeout.go @@ -22,20 +22,17 @@ func (x *XDPoS_v2) VerifyTimeoutMessage(chain consensus.ChainReader, timeoutMsg log.Debug("[VerifyTimeoutMessage] Disqualified timeout message as the proposed round does not match currentRound", "timeoutHash", timeoutMsg.Hash(), "timeoutRound", timeoutMsg.Round, "currentRound", x.currentRound) return false, nil } - snap, err := x.getSnapshot(chain, timeoutMsg.GapNumber, true) - if err != nil || snap == nil { - log.Error("[VerifyTimeoutMessage] Fail to get snapshot when verifying timeout message!", "messageGapNumber", timeoutMsg.GapNumber, "err", err) + + epochInfo, err := x.getTCEpochInfo(chain, timeoutMsg.Round) + if err != nil { + log.Error("[VerifyTimeoutMessage] Fail to get epochInfo for timeout message", "tcGapNumber", timeoutMsg.GapNumber, "tcRound", timeoutMsg.Round, "error", err) return false, err } - if len(snap.NextEpochCandidates) == 0 { - log.Error("[VerifyTimeoutMessage] cannot find NextEpochCandidates from snapshot", "messageGapNumber", timeoutMsg.GapNumber) - return false, errors.New("empty master node lists from snapshot") - } verified, signer, err := x.verifyMsgSignature(types.TimeoutSigHash(&types.TimeoutForSign{ Round: timeoutMsg.Round, GapNumber: timeoutMsg.GapNumber, - }), timeoutMsg.Signature, snap.NextEpochCandidates) + }), timeoutMsg.Signature, epochInfo.Masternodes) if err != nil { log.Warn("[VerifyTimeoutMessage] cannot verify timeout signature", "err", err) @@ -116,7 +113,7 @@ func (x *XDPoS_v2) onTimeoutPoolThresholdReached(blockChainReader consensus.Chai return nil } -func (x *XDPoS_v2) getTCEpochInfo(chain consensus.ChainReader, timeoutCert *types.TimeoutCert) (*types.EpochSwitchInfo, error) { +func (x *XDPoS_v2) getTCEpochInfo(chain consensus.ChainReader, timeoutRound types.Round) (*types.EpochSwitchInfo, error) { epochSwitchInfo, err := x.getEpochSwitchInfo(chain, (chain.CurrentHeader()), (chain.CurrentHeader()).Hash()) if err != nil { log.Error("[getTCEpochInfo] Error when getting epoch switch info", "error", err) @@ -131,18 +128,18 @@ func (x *XDPoS_v2) getTCEpochInfo(chain consensus.ChainReader, timeoutCert *type Round: epochRound, Number: epochSwitchInfo.EpochSwitchBlockInfo.Number, } - log.Info("[getTCEpochInfo] Init epochInfo", "number", epochBlockInfo.Number, "round", epochRound, "tcRound", timeoutCert.Round, "tcEpoch", tempTCEpoch) - for epochBlockInfo.Round > timeoutCert.Round { + log.Info("[getTCEpochInfo] Init epochInfo", "number", epochBlockInfo.Number, "round", epochRound, "tcRound", timeoutRound, "tcEpoch", tempTCEpoch) + for epochBlockInfo.Round > timeoutRound { tempTCEpoch-- epochBlockInfo, err = x.GetBlockByEpochNumber(chain, tempTCEpoch) if err != nil { log.Error("[getTCEpochInfo] Error when getting epoch block info by tc round", "error", err) return nil, fmt.Errorf("fail on getTCEpochInfo due to failure in getting epoch block info tc round, %s", err) } - log.Debug("[getTCEpochInfo] Loop to get right epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutCert.Round, "tcEpoch", tempTCEpoch) + log.Debug("[getTCEpochInfo] Loop to get right epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutRound, "tcEpoch", tempTCEpoch) } tcEpoch := tempTCEpoch - log.Info("[getTCEpochInfo] Final TC epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutCert.Round, "tcEpoch", tcEpoch) + log.Info("[getTCEpochInfo] Final TC epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutRound, "tcEpoch", tcEpoch) epochInfo, err := x.getEpochSwitchInfo(chain, nil, epochBlockInfo.Hash) if err != nil { @@ -175,7 +172,7 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time } } - epochInfo, err := x.getTCEpochInfo(chain, timeoutCert) + epochInfo, err := x.getTCEpochInfo(chain, timeoutCert.Round) if err != nil { return err } diff --git a/consensus/XDPoS/engines/engine_v2/vote.go b/consensus/XDPoS/engines/engine_v2/vote.go index 8f9aa95874..6d9a8b41ed 100644 --- a/consensus/XDPoS/engines/engine_v2/vote.go +++ b/consensus/XDPoS/engines/engine_v2/vote.go @@ -22,17 +22,18 @@ func (x *XDPoS_v2) VerifyVoteMessage(chain consensus.ChainReader, vote *types.Vo return false, nil } - snapshot, err := x.getSnapshot(chain, vote.GapNumber, true) + epochInfo, err := x.getEpochSwitchInfo(chain, chain.CurrentHeader(), chain.CurrentHeader().Hash()) if err != nil { - log.Error("[VerifyVoteMessage] fail to get snapshot for a vote message", "blockNum", vote.ProposedBlockInfo.Number, "blockHash", vote.ProposedBlockInfo.Hash, "voteHash", vote.Hash(), "error", err.Error()) + log.Error("[VerifyVoteMessage] Fail to get epochInfo when verifying vote message!", "blockNum", vote.ProposedBlockInfo.Number, "blockHash", vote.ProposedBlockInfo.Hash, "voteHash", vote.Hash(), "voteGapNumber", vote.GapNumber, "err", err) return false, err } + verified, signer, err := x.verifyMsgSignature(types.VoteSigHash(&types.VoteForSign{ ProposedBlockInfo: vote.ProposedBlockInfo, GapNumber: vote.GapNumber, - }), vote.Signature, snapshot.NextEpochCandidates) + }), vote.Signature, epochInfo.Masternodes) if err != nil { - for i, mn := range snapshot.NextEpochCandidates { + for i, mn := range epochInfo.Masternodes { log.Warn("[VerifyVoteMessage] Master node list item", "index", i, "Master node", mn.Hex()) } log.Warn("[VerifyVoteMessage] Error while verifying vote message", "votedBlockNum", vote.ProposedBlockInfo.Number.Uint64(), "votedBlockHash", vote.ProposedBlockInfo.Hash.Hex(), "voteHash", vote.Hash(), "error", err.Error()) diff --git a/consensus/tests/engine_v2_tests/api_test.go b/consensus/tests/engine_v2_tests/api_test.go index b70627d7f8..2ca83c1b84 100644 --- a/consensus/tests/engine_v2_tests/api_test.go +++ b/consensus/tests/engine_v2_tests/api_test.go @@ -172,11 +172,11 @@ func TestGetBlockByEpochNumber(t *testing.T) { blockchain, _, currentBlock, signer, signFn := PrepareXDCTestBlockChainWithPenaltyForV2Engine(t, 1802, params.TestXDPoSMockChainConfig) blockCoinBase := "0x111000000000000000000000000000000123" - largeRound := int64(1802) + largeRound := int64(1800) newBlock := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, int(currentBlock.NumberU64())+1, largeRound, blockCoinBase, signer, signFn, nil, nil, currentBlock.Header().Root.Hex()) err := blockchain.InsertBlock(newBlock) assert.Nil(t, err) - largeRound2 := int64(3603) + largeRound2 := int64(3600) newBlock2 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, newBlock, int(newBlock.NumberU64())+1, largeRound2, blockCoinBase, signer, signFn, nil, nil, newBlock.Header().Root.Hex()) err = blockchain.InsertBlock(newBlock2) assert.Nil(t, err) @@ -189,9 +189,9 @@ func TestGetBlockByEpochNumber(t *testing.T) { // 1800,900,2 (2nd epoch switch block) // 1801,901,2 // 1802,902,2 - // 1803,1802,3 (epoch switch) + // 1803,1800,3 (epoch switch) // epoch 4 has no block - // 1804,3603,5 (epoch switch) + // 1804,3600,5 (epoch switch) engine := blockchain.Engine().(*XDPoS.XDPoS) // init the snapshot, otherwise getEpochSwitchInfo would return error diff --git a/consensus/tests/engine_v2_tests/vote_test.go b/consensus/tests/engine_v2_tests/vote_test.go index 7febbd8cc7..777efc11d7 100644 --- a/consensus/tests/engine_v2_tests/vote_test.go +++ b/consensus/tests/engine_v2_tests/vote_test.go @@ -537,32 +537,6 @@ func TestVerifyVoteMsg(t *testing.T) { assert.Nil(t, err) } -func TestVoteMsgMissingSnapshot(t *testing.T) { - blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 915, params.TestXDPoSMockChainConfig, nil) - engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 - - blockInfo := &types.BlockInfo{ - Hash: currentBlock.Hash(), - Round: types.Round(14), - Number: big.NewInt(915), - } - voteForSign := &types.VoteForSign{ - ProposedBlockInfo: blockInfo, - GapNumber: 450, - } - - signHash, _ := signFn(accounts.Account{Address: signer}, types.VoteSigHash(voteForSign).Bytes()) - voteMsg := &types.Vote{ - ProposedBlockInfo: blockInfo, - Signature: signHash, - GapNumber: 1350, // missing 1350 snapshot - } - engineV2.SetNewRoundFaker(blockchain, types.Round(14), false) - verified, err := engineV2.VerifyVoteMessage(blockchain, voteMsg) - assert.False(t, verified) - assert.NotNil(t, err) -} - func TestVoteMessageHandlerWrongGapNumber(t *testing.T) { blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2