From 163ed0fab3c6ae382141f6a59883e4c32bcc2114 Mon Sep 17 00:00:00 2001 From: Jianrong Date: Fri, 26 Nov 2021 20:26:39 +1100 Subject: [PATCH] add ProcessQC tests --- consensus/XDPoS/engines/engine_v2/engine.go | 88 +++++++--- consensus/XDPoS/utils/pool.go | 23 +-- consensus/XDPoS/utils/pool_test.go | 151 ++++-------------- eth/bfter/bft.go | 55 ++++--- eth/bfter/bft_test.go | 16 +- tests/consensus/adaptor_test.go | 2 +- tests/consensus/block_signer_test.go | 16 +- .../blockchain_race_condition_test.go | 4 +- tests/consensus/test_helper.go | 88 ++++++++-- tests/consensus/v2_test.go | 140 ++++++++++------ 10 files changed, 321 insertions(+), 262 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index b1a7712f1b..4764da379a 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -59,9 +59,6 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 { } // Add callback to the timer timer.OnTimeoutFn = engine.onCountdownTimeout - // Attach vote & timeout pool callback function when it reached threshold - votePool.SetOnThresholdFn(engine.onVotePoolThresholdReached) - timeoutPool.SetOnThresholdFn(engine.onTimeoutPoolThresholdReached) return engine } @@ -70,6 +67,8 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 { Testing tools */ func (x *XDPoS_v2) SetNewRoundFaker(newRound utils.Round, resetTimer bool) { + x.lock.Lock() + defer x.lock.Unlock() // Reset a bunch of things if resetTimer { x.timeoutWorker.Reset() @@ -78,10 +77,10 @@ func (x *XDPoS_v2) SetNewRoundFaker(newRound utils.Round, resetTimer bool) { } // Utils for test to check currentRound value -func (x *XDPoS_v2) GetCurrentRound() utils.Round { +func (x *XDPoS_v2) GetProperties() (utils.Round, *utils.QuorumCert, *utils.QuorumCert) { x.lock.Lock() defer x.lock.Unlock() - return x.currentRound + return x.currentRound, x.lockQuorumCert, x.highestQuorumCert } // Authorize injects a private key into the consensus engine to mint new blocks with. @@ -149,7 +148,7 @@ func (x *XDPoS_v2) VerifyVoteMessage(vote *utils.Vote) (bool, error) { } // Consensus entry point for processing vote message to produce QC -func (x *XDPoS_v2) VoteHandler(voteMsg *utils.Vote) error { +func (x *XDPoS_v2) VoteHandler(chain consensus.ChainReader, voteMsg *utils.Vote) error { x.lock.Lock() defer x.lock.Unlock() @@ -159,13 +158,15 @@ func (x *XDPoS_v2) VoteHandler(voteMsg *utils.Vote) error { } // Collect vote - thresholdReached, numberOfVotesInPool, hookError := x.votePool.Add(voteMsg) - if hookError != nil { - log.Error("Error while adding vote message to the pool, ", hookError) - return hookError + thresholdReached, numberOfVotesInPool, pooledVotes := x.votePool.Add(voteMsg) + if thresholdReached { + log.Debug("Vote pool threashold reached: %v, number of items in the pool: %v", thresholdReached, numberOfVotesInPool) + err := x.onVotePoolThresholdReached(chain, pooledVotes, voteMsg) + if err != nil { + return nil + } } - log.Debug("Vote pool threashold reached: %v, number of items in the pool: %v", thresholdReached, numberOfVotesInPool) return nil } @@ -173,7 +174,7 @@ func (x *XDPoS_v2) VoteHandler(voteMsg *utils.Vote) error { Function that will be called by votePool when it reached threshold. In the engine v2, we will need to generate and process QC */ -func (x *XDPoS_v2) onVotePoolThresholdReached(pooledVotes map[common.Hash]utils.PoolObj, currentVoteMsg utils.PoolObj) error { +func (x *XDPoS_v2) onVotePoolThresholdReached(chain consensus.ChainReader, pooledVotes map[common.Hash]utils.PoolObj, currentVoteMsg utils.PoolObj) error { signatures := []utils.Signature{} for _, v := range pooledVotes { signatures = append(signatures, v.(*utils.Vote).Signature) @@ -183,7 +184,7 @@ func (x *XDPoS_v2) onVotePoolThresholdReached(pooledVotes map[common.Hash]utils. ProposedBlockInfo: currentVoteMsg.(*utils.Vote).ProposedBlockInfo, Signatures: signatures, } - err := x.processQC(quorumCert) + err := x.processQC(chain, quorumCert) if err != nil { log.Error("Error while processing QC in the Vote handler after reaching pool threshold, ", err) return err @@ -211,7 +212,7 @@ func (x *XDPoS_v2) VerifyTimeoutMessage(timeoutMsg *utils.Timeout) (bool, error) Entry point for handling timeout message to process below: 1. checkRoundNumber() 2. Collect timeout - Once timeout pool reached threshold, it will trigger the call to the hook function "onTimeoutPoolThresholdReached" + 3. Once timeout pool reached threshold, it will trigger the call to the function "onTimeoutPoolThresholdReached" */ func (x *XDPoS_v2) TimeoutHandler(timeout *utils.Timeout) error { x.lock.Lock() @@ -222,12 +223,15 @@ func (x *XDPoS_v2) TimeoutHandler(timeout *utils.Timeout) error { return fmt.Errorf("Timeout message round number: %v does not match currentRound: %v", timeout.Round, x.currentRound) } // Collect timeout, generate TC - isThresholdReached, numberOfTimeoutsInPool, hookError := x.timeoutPool.Add(timeout) - if hookError != nil { - log.Error("Error adding timeout to the pool, ", hookError.Error()) - return hookError + isThresholdReached, numberOfTimeoutsInPool, pooledTimeouts := x.timeoutPool.Add(timeout) + // Threshold reached + if isThresholdReached { + log.Debug("Timeout pool threashold reached: %v, number of items in the pool: %v", isThresholdReached, numberOfTimeoutsInPool) + err := x.onTimeoutPoolThresholdReached(pooledTimeouts, timeout) + if err != nil { + return err + } } - log.Debug("Timeout pool threashold reached: %v, number of items in the pool: %v", isThresholdReached, numberOfTimeoutsInPool) return nil } @@ -309,22 +313,28 @@ func (x *XDPoS_v2) verifyTC(timeoutCert *utils.TimeoutCert) error { return nil } -// Update local QC variables including highestQC & lockQuorumCert, as well as update commit blockInfo before call +// Update local QC variables including highestQC & lockQuorumCert, as well as commit the blocks that satisfy the algorithm requirements func (x *XDPoS_v2) processQC(blockCahinReader consensus.ChainReader, quorumCert *utils.QuorumCert) error { // 1. Update HighestQC - if x.highestQuorumCert == nil || quorumCert.ProposedBlockInfo.Round > x.highestQuorumCert.ProposedBlockInfo.Round { + if x.highestQuorumCert == nil || (quorumCert.ProposedBlockInfo.Round > x.highestQuorumCert.ProposedBlockInfo.Round) { //TODO: do I need a clone? x.highestQuorumCert = quorumCert } - // 2. Get QC from header and update lockQuorumCert + // 2. Get QC from header and update lockQuorumCert(lockQuorumCert is the parent of highestQC) proposedBlockHeader := blockCahinReader.GetHeaderByHash(quorumCert.ProposedBlockInfo.Hash) var decodedExtraField utils.ExtraFields_v2 - utils.DecodeBytesExtraFields(proposedBlockHeader.Extra, &decodedExtraField) + err := utils.DecodeBytesExtraFields(proposedBlockHeader.Extra, &decodedExtraField) + if err != nil { + return err + } x.lockQuorumCert = &decodedExtraField.QuorumCert + proposedBlockRound := &decodedExtraField.Round // 3. Update commit block info - //TODO: find parent and grandparent and grandgrandparent block, check round number, if so, commit grandgrandparent - + _, err = x.commitBlocks(blockCahinReader, proposedBlockHeader, proposedBlockRound) + if err != nil { + return err + } if quorumCert.ProposedBlockInfo.Round >= x.currentRound { err := x.setNewRound(quorumCert.ProposedBlockInfo.Round + 1) if err != nil { @@ -481,3 +491,31 @@ func (x *XDPoS_v2) getSyncInfo() utils.SyncInfo { HighestTimeoutCert: x.highestTimeoutCert, } } + +//TODO: find parent and grandparent and grandgrandparent block, check round number, if so, commit grandgrandparent +func (x *XDPoS_v2) commitBlocks(blockCahinReader consensus.ChainReader, proposedBlockHeader *types.Header, proposedBlockRound *utils.Round) (bool, error) { + // Find the last two parent block and check their rounds are the continous + parentBlock := blockCahinReader.GetHeaderByHash(proposedBlockHeader.ParentHash) + + var decodedExtraField utils.ExtraFields_v2 + err := utils.DecodeBytesExtraFields(parentBlock.Extra, &decodedExtraField) + if err != nil { + return false, err + } + if *proposedBlockRound-1 != decodedExtraField.Round { + return false, nil + } + + // If parent round is continous, we check grandparent + grandParentBlock := blockCahinReader.GetHeaderByHash(parentBlock.ParentHash) + err = utils.DecodeBytesExtraFields(grandParentBlock.Extra, &decodedExtraField) + if err != nil { + return false, err + } + if *proposedBlockRound-2 != decodedExtraField.Round { + return false, nil + } + // TODO: Commit the grandParent block + + return true, nil +} diff --git a/consensus/XDPoS/utils/pool.go b/consensus/XDPoS/utils/pool.go index 5393612342..a5ca1af3fb 100644 --- a/consensus/XDPoS/utils/pool.go +++ b/consensus/XDPoS/utils/pool.go @@ -1,8 +1,6 @@ package utils import ( - "fmt" - "github.com/XinFinOrg/XDPoSChain/common" ) @@ -11,9 +9,8 @@ type PoolObj interface { PoolKey() string } type Pool struct { - objList map[string]map[common.Hash]PoolObj - threshold int - onThresholdFn func(objsInPool map[common.Hash]PoolObj, currentObj PoolObj) error + objList map[string]map[common.Hash]PoolObj + threshold int } func NewPool(threshold int) *Pool { @@ -23,8 +20,8 @@ func NewPool(threshold int) *Pool { } } -// call the hook function onThresholdFn if reached threshold and return boolean to indicate whether pool has reached threshold -func (p *Pool) Add(obj PoolObj) (bool, int, error) { +// return true if it has reached threshold +func (p *Pool) Add(obj PoolObj) (bool, int, map[common.Hash]PoolObj) { poolKey := obj.PoolKey() objListKeyed, ok := p.objList[poolKey] if !ok { @@ -35,13 +32,9 @@ func (p *Pool) Add(obj PoolObj) (bool, int, error) { numOfItems := len(objListKeyed) if numOfItems >= p.threshold { delete(p.objList, poolKey) - if p.onThresholdFn != nil { - return true, numOfItems, p.onThresholdFn(objListKeyed, obj) - } else { - return true, numOfItems, fmt.Errorf("no call back function for pool") - } + return true, numOfItems, objListKeyed } - return false, numOfItems, nil + return false, numOfItems, objListKeyed } func (p *Pool) Clear() { @@ -51,7 +44,3 @@ func (p *Pool) Clear() { func (p *Pool) SetThreshold(t int) { p.threshold = t } - -func (p *Pool) SetOnThresholdFn(f func(objsInPool map[common.Hash]PoolObj, currentObj PoolObj) error) { - p.onThresholdFn = f -} diff --git a/consensus/XDPoS/utils/pool_test.go b/consensus/XDPoS/utils/pool_test.go index 55ab8adde8..5914c760ed 100644 --- a/consensus/XDPoS/utils/pool_test.go +++ b/consensus/XDPoS/utils/pool_test.go @@ -1,144 +1,55 @@ package utils import ( - "math/big" "testing" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/stretchr/testify/assert" ) -func TestPoolWithTimeout(t *testing.T) { +func TestPoolAdd(t *testing.T) { assert := assert.New(t) - var ret int - onThresholdFn := func(po map[common.Hash]PoolObj, currentPoolObj PoolObj) error { - for _, m := range po { - if _, ok := m.(*Timeout); ok { - ret += 1 - } else { - t.Fatalf("wrong type passed into pool: %v", m) - } - } - return nil - } pool := NewPool(2) // 2 is the cert threshold - ret = 0 - pool.SetOnThresholdFn(onThresholdFn) timeout1 := Timeout{Round: 1, Signature: []byte{1}} timeout2 := Timeout{Round: 1, Signature: []byte{2}} timeout3 := Timeout{Round: 1, Signature: []byte{3}} - _, numOfItems, err := pool.Add(&timeout1) - assert.Nil(err) + thresholdReached, numOfItems, pooledTimeouts := pool.Add(&timeout1) + assert.NotNil(pooledTimeouts) assert.Equal(1, numOfItems) - _, numOfItems, err = pool.Add(&timeout1) - assert.Nil(err) + assert.False(thresholdReached) + thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout1) + assert.NotNil(pooledTimeouts) + assert.False(thresholdReached) // Duplicates should not be added assert.Equal(1, numOfItems) - assert.Equal(0, ret) - _, numOfItems, err = pool.Add(&timeout2) - assert.Nil(err) - assert.Equal(2, ret) - _, numOfItems, err = pool.Add(&timeout3) - assert.Nil(err) - assert.Equal(2, ret) + // Should add the one that is not a duplicates + thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout2) + assert.True(thresholdReached) + assert.NotNil(pooledTimeouts) + assert.Equal(2, numOfItems) + + // Try to add one more to the same round, but that round threshold has already been reached, hence deleted + thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout3) + assert.False(thresholdReached) + assert.NotNil(pooledTimeouts) + assert.Equal(1, numOfItems) + pool = NewPool(3) // 3 is the cert size - ret = 0 - pool.SetOnThresholdFn(onThresholdFn) - _, numOfItems, err = pool.Add(&timeout1) - assert.Nil(err) + thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout1) + assert.False(thresholdReached) + assert.NotNil(pooledTimeouts) assert.Equal(1, numOfItems) - _, numOfItems, err = pool.Add(&timeout2) - assert.Nil(err) + + thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout2) + assert.False(thresholdReached) assert.Equal(2, numOfItems) - assert.Equal(ret, 0) + assert.NotNil(pooledTimeouts) pool.Clear() - _, numOfItems, err = pool.Add(&timeout3) - assert.Nil(err) + + // Pool has been cleared. Start from 0 again + thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout3) + assert.False(thresholdReached) assert.Equal(1, numOfItems) - assert.Equal(0, ret) -} - -func TestPoolWithVote(t *testing.T) { - assert := assert.New(t) - var ret int - onThresholdFn := func(po map[common.Hash]PoolObj, currentPoolObj PoolObj) error { - for _, m := range po { - if _, ok := m.(*Vote); ok { - ret += 1 - } else { - t.Fatalf("wrong type passed into pool: %v", m) - } - } - return nil - } - - pool := NewPool(2) // 2 is the cert threshold - ret = 0 - pool.SetOnThresholdFn(onThresholdFn) - blockInfo1 := BlockInfo{Hash: common.BigToHash(big.NewInt(2047)), Round: 1, Number: big.NewInt(1)} - blockInfo2 := BlockInfo{Hash: common.BigToHash(big.NewInt(4095)), Round: 1, Number: big.NewInt(1)} - vote1 := Vote{ProposedBlockInfo: blockInfo1, Signature: []byte{1}} - vote2 := Vote{ProposedBlockInfo: blockInfo2, Signature: []byte{2}} - vote3 := Vote{ProposedBlockInfo: blockInfo1, Signature: []byte{3}} - _, numOfItems, err := pool.Add(&vote1) - assert.Nil(err) - assert.Equal(1, numOfItems) - // Duplicates should not be added - _, numOfItems, err = pool.Add(&vote1) - assert.Nil(err) - assert.Equal(1, numOfItems) - assert.Equal(ret, 0) - - _, numOfItems, err = pool.Add(&vote2) - assert.Nil(err) - // vote2 is on a different blockInfo to vote1 - assert.Equal(1, numOfItems) - assert.Equal(0, ret) - - _, numOfItems, err = pool.Add(&vote3) - assert.Nil(err) - assert.Equal(2, numOfItems) - - assert.Equal(2, ret) - pool = NewPool(3) // 3 is the cert size - ret = 0 - pool.SetOnThresholdFn(onThresholdFn) - - _, numOfItems, err = pool.Add(&vote1) - assert.Nil(err) - assert.Equal(1, numOfItems) - - // vote2 is on a different blockInfo to vote1 - _, numOfItems, err = pool.Add(&vote2) - assert.Nil(err) - assert.Equal(1, numOfItems) - - _, numOfItems, err = pool.Add(&vote3) - assert.Nil(err) - assert.Equal(2, numOfItems) - - assert.Equal(0, ret) - pool.Clear() - assert.Empty(pool.objList) - pool = NewPool(2) // 2 is the cert size - ret = 0 - pool.SetOnThresholdFn(onThresholdFn) - - _, numOfItems, err = pool.Add(&vote1) - assert.Nil(err) - assert.Equal(1, numOfItems) - - // vote2 is on a different blockInfo to vote1 - _, numOfItems, err = pool.Add(&vote2) - assert.Nil(err) - assert.Equal(1, numOfItems) - - _, numOfItems, err = pool.Add(&vote3) - assert.Nil(err) - assert.Equal(2, numOfItems) - assert.Equal(1, len(pool.objList)) //vote for one hash is cleared, but another remains - pool.Clear() - assert.Empty(pool.objList) + assert.NotNil(pooledTimeouts) } diff --git a/eth/bfter/bft.go b/eth/bfter/bft.go index faeaa48cbe..7fb34c1fd3 100644 --- a/eth/bfter/bft.go +++ b/eth/bfter/bft.go @@ -19,7 +19,7 @@ type broadcastTimeoutFn func(*utils.Timeout) type broadcastSyncInfoFn func(*utils.SyncInfo) type Bfter struct { - blockCahinReader *core.BlockChain + blockCahinReader consensus.ChainReader broadcastCh chan interface{} quit chan struct{} consensus ConsensusFns @@ -33,7 +33,7 @@ type Bfter struct { type ConsensusFns struct { verifyVote func(*utils.Vote) error - voteHandler func(*utils.Vote) error + voteHandler func(consensus.ChainReader, *utils.Vote) error verifyTimeout func(*utils.Timeout) error timeoutHandler func(*utils.Timeout) error @@ -78,63 +78,70 @@ func (b *Bfter) SetConsensusFuns(engine consensus.Engine) { } // TODO: rename -func (b *Bfter) Vote(vote *utils.Vote) { +func (b *Bfter) Vote(vote *utils.Vote) error { log.Trace("Receive Vote", "vote", vote) if b.knownVotes.Contains(vote.Hash()) { log.Trace("Discarded vote, known vote", "Signature", vote.Signature, "hash", vote.Hash()) - return + return nil } err := b.consensus.verifyVote(vote) if err != nil { log.Error("Verify BFT Vote", "error", err) - return - } - err = b.consensus.voteHandler(vote) - if err != nil { - log.Error("handle BFT Vote", "error", err) - return + return err } b.knownVotes.Add(vote.Hash(), true) b.broadcastCh <- vote + + err = b.consensus.voteHandler(b.blockCahinReader, vote) + if err != nil { + log.Error("handle BFT Vote", "error", err) + return err + } + return nil } -func (b *Bfter) Timeout(timeout *utils.Timeout) { +func (b *Bfter) Timeout(timeout *utils.Timeout) error { log.Trace("Receive Timeout", "timeout", timeout) if b.knownVotes.Contains(timeout.Hash()) { log.Trace("Discarded Timeout, known Timeout", "Signature", timeout.Signature, "hash", timeout.Hash(), "round", timeout.Round) - return + return nil } err := b.consensus.verifyTimeout(timeout) if err != nil { log.Error("Verify BFT Timeout", "error", err) - return - } - err = b.consensus.timeoutHandler(timeout) - if err != nil { - log.Error("handle BFT Timeout", "error", err) - return + return err } b.knownTimeouts.Add(timeout.Hash(), true) b.broadcastCh <- timeout + + err = b.consensus.timeoutHandler(timeout) + if err != nil { + log.Error("handle BFT Timeout", "error", err) + return err + } + return nil } -func (b *Bfter) SyncInfo(syncInfo *utils.SyncInfo) { +func (b *Bfter) SyncInfo(syncInfo *utils.SyncInfo) error { log.Trace("Receive SyncInfo", "syncInfo", syncInfo) if b.knownVotes.Contains(syncInfo.Hash()) { log.Trace("Discarded SyncInfo, known SyncInfo", "hash", syncInfo.Hash()) - return + return nil } err := b.consensus.verifySyncInfo(syncInfo) if err != nil { log.Error("Verify BFT SyncInfo", "error", err) - return + return err } + + b.knownSyncInfos.Add(syncInfo.Hash(), true) + b.broadcastCh <- syncInfo + err = b.consensus.syncInfoHandler(syncInfo) if err != nil { log.Error("handle BFT SyncInfo", "error", err) - return + return err } - b.knownSyncInfos.Add(syncInfo.Hash(), true) - b.broadcastCh <- syncInfo + return nil } // Start Bft receiver diff --git a/eth/bfter/bft_test.go b/eth/bfter/bft_test.go index 7460c791d9..887613229b 100644 --- a/eth/bfter/bft_test.go +++ b/eth/bfter/bft_test.go @@ -6,9 +6,11 @@ import ( "testing" "time" + "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v2" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" + "github.com/XinFinOrg/XDPoSChain/core" ) // make different votes based on Signatures @@ -29,9 +31,10 @@ type bfterTester struct { func newTester() *bfterTester { testConsensus := &XDPoS.XDPoS{EngineV2: &engine_v2.XDPoS_v2{}} broadcasts := BroadcastFns{} + blockChain := &core.BlockChain{} tester := &bfterTester{} - tester.bfter = New(broadcasts) + tester.bfter = New(broadcasts, blockChain) tester.bfter.SetConsensusFuns(testConsensus) tester.bfter.broadcastCh = make(chan interface{}) tester.bfter.Start() @@ -52,7 +55,7 @@ func TestSequentialVotes(t *testing.T) { return nil } - tester.bfter.consensus.voteHandler = func(vote *utils.Vote) error { + tester.bfter.consensus.voteHandler = func(chain consensus.ChainReader, vote *utils.Vote) error { atomic.AddUint32(&handlerCounter, 1) return nil } @@ -63,7 +66,10 @@ func TestSequentialVotes(t *testing.T) { votes := makeVotes(targetVotes) for _, vote := range votes { - tester.bfter.Vote(&vote) + err := tester.bfter.Vote(&vote) + if err != nil { + t.Fatal(err) + } } time.Sleep(100 * time.Millisecond) @@ -86,7 +92,7 @@ func TestDuplicateVotes(t *testing.T) { return nil } - tester.bfter.consensus.voteHandler = func(vote *utils.Vote) error { + tester.bfter.consensus.voteHandler = func(chain consensus.ChainReader, vote *utils.Vote) error { atomic.AddUint32(&handlerCounter, 1) return nil } @@ -118,7 +124,7 @@ func TestNotBoardcastInvalidVote(t *testing.T) { return fmt.Errorf("This is invalid vote") } - tester.bfter.consensus.voteHandler = func(vote *utils.Vote) error { + tester.bfter.consensus.voteHandler = func(chain consensus.ChainReader, vote *utils.Vote) error { atomic.AddUint32(&handlerCounter, 1) return nil } diff --git a/tests/consensus/adaptor_test.go b/tests/consensus/adaptor_test.go index d85827b3ac..b96bf7d435 100644 --- a/tests/consensus/adaptor_test.go +++ b/tests/consensus/adaptor_test.go @@ -28,7 +28,7 @@ func TestAdaptorShouldGetAuthorForDifferentConsensusVersion(t *testing.T) { // Insert block 11 blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", 11) merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" - block11, err := insertBlock(blockchain, 11, blockCoinBase, currentBlock, merkleRoot, 1) + block11, err := insertBlock(blockchain, 11, blockCoinBase, currentBlock, merkleRoot, nil, 1) if err != nil { t.Fatal(err) } diff --git a/tests/consensus/block_signer_test.go b/tests/consensus/block_signer_test.go index ee6c70eba9..35c799996a 100644 --- a/tests/consensus/block_signer_test.go +++ b/tests/consensus/block_signer_test.go @@ -54,7 +54,7 @@ func TestNotChangeSingerListIfNothingProposedOrVoted(t *testing.T) { // Insert block 450 blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", 450) merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" - block, err := insertBlock(blockchain, 450, blockCoinBase, parentBlock, merkleRoot, 1) + block, err := insertBlock(blockchain, 450, blockCoinBase, parentBlock, merkleRoot, nil, 1) if err != nil { t.Fatal(err) } @@ -113,7 +113,7 @@ func TestUpdateSignerListIfVotedBeforeGap(t *testing.T) { // Now, let's mine another block to trigger the GAP block signerList update block450CoinbaseAddress := "0xaaa0000000000000000000000000000000000450" merkleRoot = "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772" - block450, err := insertBlock(blockchain, 450, block450CoinbaseAddress, parentBlock, merkleRoot, 1) + block450, err := insertBlock(blockchain, 450, block450CoinbaseAddress, parentBlock, merkleRoot, nil, 1) if err != nil { t.Fatal(err) } @@ -240,7 +240,7 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) { blockCoinBase451B := "0xbbb0000000000000000000000000000000000451" merkleRoot = "068dfa09d7b4093441c0cc4d9807a71bc586f6101c072d939b214c21cd136eb3" - block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, 1) + block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, nil, 1) if err != nil { t.Fatal(err) @@ -378,7 +378,7 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin blockCoinBase451B := "0xbbb0000000000000000000000000000000000451" merkleRoot = "184edaddeafc2404248f896ae46be503ae68949896c8eb6b6ad43695581e5022" - block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, 1) + block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, nil, 1) if err != nil { t.Fatal(err) @@ -440,7 +440,7 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) { // Insert normal blocks 450 A blockCoinBase450A := "0xaaa0000000000000000000000000000000000450" merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" - block450A, err := insertBlock(blockchain, 450, blockCoinBase450A, parentBlock, merkleRoot, 1) + block450A, err := insertBlock(blockchain, 450, blockCoinBase450A, parentBlock, merkleRoot, nil, 1) if err != nil { t.Fatal(err) } @@ -476,21 +476,21 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) { // Insert forked Block 450 B blockCoinBase450B := "0xbbb0000000000000000000000000000000000450" merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" - block450B, err := insertBlock(blockchain, 450, blockCoinBase450B, parentBlock, merkleRoot, 1) + block450B, err := insertBlock(blockchain, 450, blockCoinBase450B, parentBlock, merkleRoot, nil, 1) if err != nil { t.Fatal(err) } blockCoinBase451B := "0xbbb0000000000000000000000000000000000451" merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" - block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, 1) + block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, nil, 1) if err != nil { t.Fatal(err) } blockCoinBase452B := "0xbbb0000000000000000000000000000000000452" merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" - block452B, err := insertBlock(blockchain, 452, blockCoinBase452B, block451B, merkleRoot, 1) + block452B, err := insertBlock(blockchain, 452, blockCoinBase452B, block451B, merkleRoot, nil, 1) if err != nil { t.Fatal(err) } diff --git a/tests/consensus/blockchain_race_condition_test.go b/tests/consensus/blockchain_race_condition_test.go index d7c2b336d2..409dfef7c3 100644 --- a/tests/consensus/blockchain_race_condition_test.go +++ b/tests/consensus/blockchain_race_condition_test.go @@ -11,7 +11,7 @@ import ( // Snapshot try to read before blockchain is written func TestRaceConditionOnBlockchainReadAndWrite(t *testing.T) { - blockchain, backend, parentBlock := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig) + blockchain, backend, parentBlock, _ := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig) state, err := blockchain.State() if err != nil { @@ -104,7 +104,7 @@ func TestRaceConditionOnBlockchainReadAndWrite(t *testing.T) { blockCoinBase451B := "0xbbb0000000000000000000000000000000000451" merkleRoot = "184edaddeafc2404248f896ae46be503ae68949896c8eb6b6ad43695581e5022" - block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, 3) + block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, nil, 3) if err != nil { t.Fatal(err) diff --git a/tests/consensus/test_helper.go b/tests/consensus/test_helper.go index 9e457bd41a..2b867876bb 100644 --- a/tests/consensus/test_helper.go +++ b/tests/consensus/test_helper.go @@ -10,10 +10,12 @@ import ( "testing" "time" + "github.com/XinFinOrg/XDPoSChain/accounts" "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" + "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract" "github.com/XinFinOrg/XDPoSChain/core" . "github.com/XinFinOrg/XDPoSChain/core" @@ -243,7 +245,71 @@ func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params for i := 1; i <= numOfBlocks; i++ { blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i) merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" - block, err := insertBlock(blockchain, i, blockCoinBase, currentBlock, merkleRoot, 1) + + block, err := insertBlock(blockchain, i, blockCoinBase, currentBlock, merkleRoot, nil, 1) + if err != nil { + t.Fatal(err) + } + currentBlock = block + } + // Update Signer as there is no previous signer assigned + err = UpdateSigner(blockchain) + if err != nil { + t.Fatal(err) + } + + return blockchain, backend, currentBlock, signer +} + +func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address) { + // Preparation + var err error + backend := getCommonBackend(t, chainConfig) + blockchain := backend.GetBlockChain() + blockchain.Client = backend + + // Authorise + signer, signFn, err := backends.SimulateWalletAddressAndSignFn() + if err != nil { + panic(fmt.Errorf("Error while creating simulated wallet for generating singer address and signer fn: %v", err)) + } + blockchain.Engine().(*XDPoS.XDPoS).Authorize(signer, signFn) + + currentBlock := blockchain.Genesis() + + // Insert initial blocks + for i := 1; i <= numOfBlocks; i++ { + blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i) + merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" + + // Build engine v2 compatible extra data field + proposedBlockInfo := utils.BlockInfo{ + Hash: currentBlock.Hash(), + Round: utils.Round(i), + Number: big.NewInt(int64(i)), + } + // Genrate QC + signedHash, err := signFn(accounts.Account{Address: signer}, utils.VoteSigHash(&proposedBlockInfo).Bytes()) + if err != nil { + panic(fmt.Errorf("Error generate QC by creating signedHash: %v", err)) + } + var signatures []utils.Signature + signatures = append(signatures, signedHash) + quorumCert := utils.QuorumCert{ + ProposedBlockInfo: proposedBlockInfo, + Signatures: signatures, + } + + extra := utils.ExtraFields_v2{ + Round: utils.Round(i), + QuorumCert: quorumCert, + } + extraInBytes, err := extra.EncodeToBytes() + if err != nil { + panic(fmt.Errorf("Error encode extra into bytes: %v", err)) + } + + block, err := insertBlock(blockchain, i, blockCoinBase, currentBlock, merkleRoot, extraInBytes, 1) if err != nil { t.Fatal(err) } @@ -259,13 +325,14 @@ func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params } // insert Block without transcation attached -func insertBlock(blockchain *BlockChain, blockNum int, blockCoinBase string, parentBlock *types.Block, root string, difficulty int64) (*types.Block, error) { +func insertBlock(blockchain *BlockChain, blockNum int, blockCoinBase string, parentBlock *types.Block, root string, customExtra []byte, difficulty int64) (*types.Block, error) { block, err := createXDPoSTestBlock( blockchain, parentBlock.Hash().Hex(), blockCoinBase, blockNum, nil, "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", common.HexToHash(root), + customExtra, difficulty, ) if err != nil { @@ -287,6 +354,7 @@ func insertBlockTxs(blockchain *BlockChain, blockNum int, blockCoinBase string, blockCoinBase, blockNum, txs, "0x9319777b782ba2c83a33c995481ff894ac96d9a92a1963091346a3e1e386705c", common.HexToHash(root), + nil, difficulty, ) if err != nil { @@ -300,11 +368,14 @@ func insertBlockTxs(blockchain *BlockChain, blockNum int, blockCoinBase string, return block, nil } -func createXDPoSTestBlock(bc *BlockChain, parentHash, coinbase string, number int, txs []*types.Transaction, receiptHash string, root common.Hash, difficulty int64) (*types.Block, error) { - extraSubstring := "d7830100018358444388676f312e31342e31856c696e75780000000000000000b185dc0d0e917d18e5dbf0746be6597d3331dd27ea0554e6db433feb2e81730b20b2807d33a1527bf43cd3bc057aa7f641609c2551ebe2fd575f4db704fbf38101" // Grabbed from existing mainnet block, it does not have any meaning except for the length validation - //ReceiptHash = "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" - //Root := "0xc99c095e53ff1afe3b86750affd13c7550a2d24d51fb8e41b3c3ef2ea8274bcc" - extraByte, _ := hex.DecodeString(extraSubstring) +func createXDPoSTestBlock(bc *BlockChain, parentHash, coinbase string, number int, txs []*types.Transaction, receiptHash string, root common.Hash, customExtra []byte, difficulty int64) (*types.Block, error) { + if customExtra == nil { + extraSubstring := "d7830100018358444388676f312e31342e31856c696e75780000000000000000b185dc0d0e917d18e5dbf0746be6597d3331dd27ea0554e6db433feb2e81730b20b2807d33a1527bf43cd3bc057aa7f641609c2551ebe2fd575f4db704fbf38101" // Grabbed from existing mainnet block, it does not have any meaning except for the length validation + //ReceiptHash = "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" + //Root := "0xc99c095e53ff1afe3b86750affd13c7550a2d24d51fb8e41b3c3ef2ea8274bcc" + customExtra, _ = hex.DecodeString(extraSubstring) + } + header := types.Header{ ParentHash: common.HexToHash(parentHash), UncleHash: types.EmptyUncleHash, @@ -317,14 +388,13 @@ func createXDPoSTestBlock(bc *BlockChain, parentHash, coinbase string, number in Number: big.NewInt(int64(number)), GasLimit: 1200000000, Time: big.NewInt(int64(number * 10)), - Extra: extraByte, + Extra: customExtra, } var block *types.Block if len(txs) == 0 { block = types.NewBlockWithHeader(&header) } else { - // Prepare Receipt statedb, err := bc.StateAt(bc.GetBlockByNumber(uint64(number - 1)).Root()) //Get parent root if err != nil { diff --git a/tests/consensus/v2_test.go b/tests/consensus/v2_test.go index 53be94defd..4bac28454b 100644 --- a/tests/consensus/v2_test.go +++ b/tests/consensus/v2_test.go @@ -42,14 +42,16 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) { err := engineV2.TimeoutHandler(timeoutMsg) assert.Nil(t, err) - assert.Equal(t, utils.Round(1), engineV2.GetCurrentRound()) + currentRound, _, _ := engineV2.GetProperties() + assert.Equal(t, utils.Round(1), currentRound) timeoutMsg = &utils.Timeout{ Round: utils.Round(1), Signature: []byte{2}, } err = engineV2.TimeoutHandler(timeoutMsg) assert.Nil(t, err) - assert.Equal(t, utils.Round(1), engineV2.GetCurrentRound()) + currentRound, _, _ = engineV2.GetProperties() + assert.Equal(t, utils.Round(1), currentRound) // Create a timeout message that should trigger timeout pool hook timeoutMsg = &utils.Timeout{ Round: utils.Round(1), @@ -60,6 +62,9 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) { assert.Nil(t, err) syncInfoMsg := <-engineV2.BroadcastCh + + currentRound, _, _ = engineV2.GetProperties() + assert.NotNil(t, syncInfoMsg) // Should have QC, however, we did not inilise it, hence will show default empty value @@ -71,7 +76,7 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) { assert.Equal(t, tc.Round, utils.Round(1)) sigatures := []utils.Signature{[]byte{1}, []byte{2}, []byte{3}} assert.ElementsMatch(t, tc.Signatures, sigatures) - assert.Equal(t, utils.Round(2), engineV2.GetCurrentRound()) + assert.Equal(t, utils.Round(2), currentRound) } func TestThrowErrorIfTimeoutMsgRoundNotEqualToCurrentRound(t *testing.T) { @@ -99,106 +104,129 @@ func TestThrowErrorIfTimeoutMsgRoundNotEqualToCurrentRound(t *testing.T) { } // VoteHandler -func TestVoteMessageHandlerSuccessfullyGeneratedQC(t *testing.T) { - blockchain, _, _, _ := PrepareXDCTestBlockChain(t, 11, params.TestXDPoSMockChainConfigWithV2Engine) +func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) { + blockchain, _, currentBlock, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 + // parentBlock := blockchain.GetBlockByHash(currentBlock.ParentHash()) + // grandParentBlock := blockchain.GetBlockByHash(parentBlock.ParentHash()) + blockInfo := &utils.BlockInfo{ - Hash: common.HexToHash("0x1"), - Round: utils.Round(1), - Number: big.NewInt(999), + Hash: currentBlock.Hash(), + Round: utils.Round(11), + Number: big.NewInt(11), } - // Set round to 1 - engineV2.SetNewRoundFaker(utils.Round(1), false) + // Set round to 11 + engineV2.SetNewRoundFaker(utils.Round(11), false) // Create two timeout message which will not reach vote pool threshold voteMsg := &utils.Vote{ ProposedBlockInfo: *blockInfo, Signature: []byte{1}, } - err := engineV2.VoteHandler(voteMsg) + err := engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) - assert.Equal(t, utils.Round(1), engineV2.GetCurrentRound()) + currentRound, lockQuorumCert, highestQuorumCert := engineV2.GetProperties() + // Inilised with nil and 0 round + assert.Nil(t, lockQuorumCert) + assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round) + assert.Equal(t, utils.Round(11), currentRound) voteMsg = &utils.Vote{ ProposedBlockInfo: *blockInfo, Signature: []byte{2}, } - err = engineV2.VoteHandler(voteMsg) + err = engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) - assert.Equal(t, utils.Round(1), engineV2.GetCurrentRound()) + currentRound, lockQuorumCert, highestQuorumCert = engineV2.GetProperties() + // Still using the initlised value because we did not yet go to the next round + assert.Nil(t, lockQuorumCert) + assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round) - // Create a vote message that should trigger vote pool hook + assert.Equal(t, utils.Round(11), currentRound) + + // Create a vote message that should trigger vote pool hook and increment the round to 12 voteMsg = &utils.Vote{ ProposedBlockInfo: *blockInfo, Signature: []byte{3}, } - err = engineV2.VoteHandler(voteMsg) + err = engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) - // Check round has now changed from 1 to 2 - assert.Equal(t, utils.Round(2), engineV2.GetCurrentRound()) + currentRound, lockQuorumCert, highestQuorumCert = engineV2.GetProperties() + // The lockQC shall be the parent's QC round number + assert.Equal(t, utils.Round(11), lockQuorumCert.ProposedBlockInfo.Round) + // The highestQC proposedBlockInfo shall be the same as the one from its votes + assert.Equal(t, highestQuorumCert.ProposedBlockInfo, voteMsg.ProposedBlockInfo) + // Check round has now changed from 11 to 12 + assert.Equal(t, utils.Round(12), currentRound) } func TestThrowErrorIfVoteMsgRoundNotEqualToCurrentRound(t *testing.T) { - blockchain, _, _, _ := PrepareXDCTestBlockChain(t, 11, params.TestXDPoSMockChainConfigWithV2Engine) + blockchain, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 blockInfo := &utils.BlockInfo{ Hash: common.HexToHash("0x1"), - Round: utils.Round(2), + Round: utils.Round(12), Number: big.NewInt(999), } - // Set round to 3 - engineV2.SetNewRoundFaker(utils.Round(3), false) + // Set round to 13 + engineV2.SetNewRoundFaker(utils.Round(13), false) voteMsg := &utils.Vote{ ProposedBlockInfo: *blockInfo, Signature: []byte{1}, } // voteRound > currentRound - err := engineV2.VoteHandler(voteMsg) + err := engineV2.VoteHandler(blockchain, voteMsg) assert.NotNil(t, err) - assert.Equal(t, "Vote message round number: 2 does not match currentRound: 3", err.Error()) + assert.Equal(t, "Vote message round number: 12 does not match currentRound: 13", err.Error()) - // Set round to 1 - engineV2.SetNewRoundFaker(utils.Round(1), false) - err = engineV2.VoteHandler(voteMsg) + // Set round to 11 + engineV2.SetNewRoundFaker(utils.Round(11), false) + err = engineV2.VoteHandler(blockchain, voteMsg) assert.NotNil(t, err) // voteRound < currentRound - assert.Equal(t, "Vote message round number: 2 does not match currentRound: 1", err.Error()) + assert.Equal(t, "Vote message round number: 12 does not match currentRound: 11", err.Error()) } func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { - blockchain, _, _, _ := PrepareXDCTestBlockChain(t, 11, params.TestXDPoSMockChainConfigWithV2Engine) + blockchain, _, currentBlock, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set round to 1 - engineV2.SetNewRoundFaker(utils.Round(1), false) + engineV2.SetNewRoundFaker(utils.Round(11), false) // Start with vote messages blockInfo := &utils.BlockInfo{ - Hash: common.HexToHash("0x1"), - Round: utils.Round(1), - Number: big.NewInt(999), + Hash: currentBlock.Hash(), + Round: utils.Round(11), + Number: big.NewInt(11), } - // Create two timeout message which will not reach vote pool threshold + // Create two vote message which will not reach vote pool threshold voteMsg := &utils.Vote{ ProposedBlockInfo: *blockInfo, Signature: []byte{1}, } - err := engineV2.VoteHandler(voteMsg) + err := engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) - assert.Equal(t, utils.Round(1), engineV2.GetCurrentRound()) + currentRound, lockQuorumCert, highestQuorumCert := engineV2.GetProperties() + // Inilised with nil and 0 round + assert.Nil(t, lockQuorumCert) + assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round) + + assert.Equal(t, utils.Round(11), currentRound) voteMsg = &utils.Vote{ ProposedBlockInfo: *blockInfo, Signature: []byte{2}, } - err = engineV2.VoteHandler(voteMsg) + err = engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) - assert.Equal(t, utils.Round(1), engineV2.GetCurrentRound()) + currentRound, _, _ = engineV2.GetProperties() + assert.Equal(t, utils.Round(11), currentRound) // Create a vote message that should trigger vote pool hook voteMsg = &utils.Vote{ @@ -206,42 +234,51 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { Signature: []byte{3}, } - err = engineV2.VoteHandler(voteMsg) + err = engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) - // Check round has now changed from 1 to 2 - assert.Equal(t, utils.Round(2), engineV2.GetCurrentRound()) + // Check round has now changed from 11 to 12 + currentRound, lockQuorumCert, highestQuorumCert = engineV2.GetProperties() + // The lockQC shall be the parent's QC round number + assert.Equal(t, utils.Round(11), lockQuorumCert.ProposedBlockInfo.Round) + // The highestQC proposedBlockInfo shall be the same as the one from its votes + assert.Equal(t, highestQuorumCert.ProposedBlockInfo, voteMsg.ProposedBlockInfo) + + assert.Equal(t, utils.Round(12), currentRound) // We shall have highestQuorumCert in engine now, let's do timeout msg to see if we can broadcast SyncInfo which contains both highestQuorumCert and HighestTimeoutCert // First, all incoming old timeout msg shall not be processed timeoutMsg := &utils.Timeout{ - Round: utils.Round(1), + Round: utils.Round(11), Signature: []byte{1}, } err = engineV2.TimeoutHandler(timeoutMsg) assert.NotNil(t, err) - assert.Equal(t, "Timeout message round number: 1 does not match currentRound: 2", err.Error()) + assert.Equal(t, "Timeout message round number: 11 does not match currentRound: 12", err.Error()) // Ok, let's do the timeout msg which is on the same round as the current round by creating two timeout message which will not reach timeout pool threshold timeoutMsg = &utils.Timeout{ - Round: utils.Round(2), + Round: utils.Round(12), Signature: []byte{1}, } err = engineV2.TimeoutHandler(timeoutMsg) assert.Nil(t, err) - assert.Equal(t, utils.Round(2), engineV2.GetCurrentRound()) + currentRound, _, _ = engineV2.GetProperties() + assert.Equal(t, utils.Round(12), currentRound) timeoutMsg = &utils.Timeout{ - Round: utils.Round(2), + Round: utils.Round(12), Signature: []byte{2}, } err = engineV2.TimeoutHandler(timeoutMsg) assert.Nil(t, err) - assert.Equal(t, utils.Round(2), engineV2.GetCurrentRound()) + currentRound, _, _ = engineV2.GetProperties() + assert.Equal(t, utils.Round(12), currentRound) + // Create a timeout message that should trigger timeout pool hook timeoutMsg = &utils.Timeout{ - Round: utils.Round(2), + Round: utils.Round(12), Signature: []byte{3}, } @@ -254,13 +291,14 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { // Should have HighestQuorumCert from previous round votes qc := syncInfoMsg.(utils.SyncInfo).HighestQuorumCert assert.NotNil(t, qc) - assert.Equal(t, utils.Round(1), qc.ProposedBlockInfo.Round) + assert.Equal(t, utils.Round(11), qc.ProposedBlockInfo.Round) tc := syncInfoMsg.(utils.SyncInfo).HighestTimeoutCert assert.NotNil(t, tc) - assert.Equal(t, tc.Round, utils.Round(2)) + assert.Equal(t, utils.Round(12), tc.Round) sigatures := []utils.Signature{[]byte{1}, []byte{2}, []byte{3}} assert.ElementsMatch(t, tc.Signatures, sigatures) // Round shall be +1 now - assert.Equal(t, utils.Round(3), engineV2.GetCurrentRound()) + currentRound, _, _ = engineV2.GetProperties() + assert.Equal(t, utils.Round(13), currentRound) }