diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go index 70032fd7ab38..95df621c79c9 100644 --- a/consensus/XDPoS/XDPoS.go +++ b/consensus/XDPoS/XDPoS.go @@ -63,6 +63,8 @@ type XDPoS struct { // The exact consensus engine with different versions EngineV1 *engine_v1.XDPoS_v1 EngineV2 *engine_v2.XDPoS_v2 + + isV2Initilised bool } // New creates a XDPoS delegated-proof-of-stake consensus engine with the initial @@ -90,6 +92,7 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS { signingTxsCache: signingTxsCache, EngineV1: engine_v1.New(config, db), EngineV2: engine_v2.New(config, db, waitPeriodCh), + isV2Initilised: false, } } @@ -321,9 +324,18 @@ func (x *XDPoS) YourTurn(chain consensus.ChainReader, parent *types.Header, sign log.Error("[YourTurn] Error while initilising first v2 block from the last v1 block", "ParentBlockHash", parent.Hash(), "Error", err) return false, err } - } else if parent.Number.Cmp(x.config.V2.SwitchBlock) == 1 { // TODO: XIN-147 + x.isV2Initilised = true + } else if parent.Number.Cmp(x.config.V2.SwitchBlock) == 1 && !x.isV2Initilised { // TODO: XIN-147, temporary solution for now log.Info("[YourTurn] Initilising v2 after sync or restarted", "currentBlockNum", chain.CurrentHeader().Number, "currentBlockHash", chain.CurrentHeader().Hash()) + lastv1BlockHeader := chain.GetHeaderByNumber(x.config.V2.SwitchBlock.Uint64()) + err := x.initialV2FromLastV1(chain, lastv1BlockHeader) + if err != nil { + log.Error("[YourTurn] Temporary solution! Error when initialise v2", "lastv1BlockHeader", lastv1BlockHeader.Hash(), "Error", err) + return false, err + } + x.isV2Initilised = true } + } switch x.config.BlockConsensusVersion(big.NewInt(parent.Number.Int64() + 1)) { case params.ConsensusEngineVersion2: diff --git a/consensus/tests/test_helper.go b/consensus/tests/test_helper.go index 316db30ec30c..4dd8567a63ce 100644 --- a/consensus/tests/test_helper.go +++ b/consensus/tests/test_helper.go @@ -368,8 +368,10 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon blockchain := backend.GetBlockChain() blockchain.Client = backend + engine := blockchain.Engine().(*XDPoS.XDPoS) + // Authorise - blockchain.Engine().(*XDPoS.XDPoS).Authorize(signer, signFn) + engine.Authorize(signer, signFn) currentBlock := blockchain.Genesis() @@ -410,6 +412,19 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon blockchain.InsertBlock(forkedBlock) currentForkBlock = forkedBlock } + + // First v2 block + if (int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64()) == 1 { + lastv1BlockNumber := block.Header().Number.Uint64() - 1 + checkpointBlockNumber := lastv1BlockNumber - lastv1BlockNumber%chainConfig.XDPoS.Epoch + checkpointHeader := blockchain.GetHeaderByNumber(checkpointBlockNumber) + masternodes := engine.EngineV1.GetMasternodesFromCheckpointHeader(checkpointHeader) + err := engine.EngineV2.Initial(blockchain, block.Header(), masternodes) + if err != nil { + panic(err) + } + } + currentBlock = block } diff --git a/consensus/tests/vote_test.go b/consensus/tests/vote_test.go index b9ff4cf666c2..bdd3e0a2bcb8 100644 --- a/consensus/tests/vote_test.go +++ b/consensus/tests/vote_test.go @@ -374,3 +374,35 @@ func TestVoteMessageShallNotThrowErrorIfBlockNotYetExist(t *testing.T) { assert.Equal(t, utils.Round(4), highestCommitBlock.Round) assert.Equal(t, big.NewInt(904), highestCommitBlock.Number) } + +func TestVerifyVoteMsg(t *testing.T) { + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 915, params.TestXDPoSMockChainConfig, 0) + engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 + + blockInfo := &utils.BlockInfo{ + Hash: currentBlock.Hash(), + Round: utils.Round(15), + Number: big.NewInt(915), + } + + // Invalid vote msg + voteMsg := &utils.Vote{ + ProposedBlockInfo: blockInfo, + Signature: []byte{1}, + } + + verified, err := engineV2.VerifyVoteMessage(blockchain, voteMsg) + assert.False(t, verified) + assert.NotNil(t, err) + + // Valid vote message from a master node + signHash, _ := signFn(accounts.Account{Address: signer}, utils.VoteSigHash(blockInfo).Bytes()) + voteMsg = &utils.Vote{ + ProposedBlockInfo: blockInfo, + Signature: signHash, + } + + verified, err = engineV2.VerifyVoteMessage(blockchain, voteMsg) + assert.True(t, verified) + assert.Nil(t, err) +} diff --git a/eth/bft/bft_hander_test.go b/eth/bft/bft_hander_test.go index e22bbf80610c..935b87b820bf 100644 --- a/eth/bft/bft_hander_test.go +++ b/eth/bft/bft_hander_test.go @@ -54,9 +54,9 @@ func TestSequentialVotes(t *testing.T) { broadcastCounter := uint32(0) targetVotes := 10 - tester.bfter.consensus.verifyVote = func(vote *utils.Vote) error { + tester.bfter.consensus.verifyVote = func(chain consensus.ChainReader, vote *utils.Vote) (bool, error) { atomic.AddUint32(&verifyCounter, 1) - return nil + return true, nil } tester.bfter.consensus.voteHandler = func(chain consensus.ChainReader, vote *utils.Vote) error { @@ -91,9 +91,9 @@ func TestDuplicateVotes(t *testing.T) { broadcastCounter := uint32(0) targetVotes := 1 - tester.bfter.consensus.verifyVote = func(vote *utils.Vote) error { + tester.bfter.consensus.verifyVote = func(chain consensus.ChainReader, vote *utils.Vote) (bool, error) { atomic.AddUint32(&verifyCounter, 1) - return nil + return true, nil } tester.bfter.consensus.voteHandler = func(chain consensus.ChainReader, vote *utils.Vote) error { @@ -124,8 +124,8 @@ func TestNotBoardcastInvalidVote(t *testing.T) { broadcastCounter := uint32(0) targetVotes := 0 - tester.bfter.consensus.verifyVote = func(vote *utils.Vote) error { - return fmt.Errorf("This is invalid vote") + tester.bfter.consensus.verifyVote = func(chain consensus.ChainReader, vote *utils.Vote) (bool, error) { + return false, fmt.Errorf("This is invalid vote") } tester.bfter.consensus.voteHandler = func(chain consensus.ChainReader, vote *utils.Vote) error { diff --git a/eth/bft/bft_handler.go b/eth/bft/bft_handler.go index 25f8eba293de..b911ff75de33 100644 --- a/eth/bft/bft_handler.go +++ b/eth/bft/bft_handler.go @@ -1,6 +1,8 @@ package bft import ( + "fmt" + "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" @@ -32,7 +34,7 @@ type Bfter struct { } type ConsensusFns struct { - verifyVote func(*utils.Vote) error + verifyVote func(chain consensus.ChainReader, vote *utils.Vote) (bool, error) voteHandler func(consensus.ChainReader, *utils.Vote) error verifyTimeout func(*utils.Timeout) error @@ -68,7 +70,7 @@ func (b *Bfter) SetConsensusFuns(engine consensus.Engine) { b.broadcastCh = e.EngineV2.BroadcastCh b.consensus = ConsensusFns{ verifySyncInfo: e.VerifySyncInfo, - verifyVote: e.VerifyVote, + verifyVote: e.EngineV2.VerifyVoteMessage, verifyTimeout: e.VerifyTimeout, voteHandler: e.EngineV2.VoteHandler, @@ -85,11 +87,16 @@ func (b *Bfter) Vote(vote *utils.Vote) error { return nil } - err := b.consensus.verifyVote(vote) - if err != nil { - log.Error("Verify BFT Vote", "error", err) + verified, err := b.consensus.verifyVote(b.blockChainReader, vote) + + if err != nil || !verified { + log.Error("Verify BFT Vote", "error", err, "verified", verified) + if !verified { + return fmt.Errorf("Fail to verify vote") + } return err } + b.broadcastCh <- vote err = b.consensus.voteHandler(b.blockChainReader, vote)