add ProcessQC tests

This commit is contained in:
Jianrong 2021-11-26 20:26:39 +11:00
parent bd60e1b0cf
commit 163ed0fab3
10 changed files with 321 additions and 262 deletions

View file

@ -59,9 +59,6 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 {
} }
// Add callback to the timer // Add callback to the timer
timer.OnTimeoutFn = engine.onCountdownTimeout timer.OnTimeoutFn = engine.onCountdownTimeout
// Attach vote & timeout pool callback function when it reached threshold
votePool.SetOnThresholdFn(engine.onVotePoolThresholdReached)
timeoutPool.SetOnThresholdFn(engine.onTimeoutPoolThresholdReached)
return engine return engine
} }
@ -70,6 +67,8 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 {
Testing tools Testing tools
*/ */
func (x *XDPoS_v2) SetNewRoundFaker(newRound utils.Round, resetTimer bool) { func (x *XDPoS_v2) SetNewRoundFaker(newRound utils.Round, resetTimer bool) {
x.lock.Lock()
defer x.lock.Unlock()
// Reset a bunch of things // Reset a bunch of things
if resetTimer { if resetTimer {
x.timeoutWorker.Reset() x.timeoutWorker.Reset()
@ -78,10 +77,10 @@ func (x *XDPoS_v2) SetNewRoundFaker(newRound utils.Round, resetTimer bool) {
} }
// Utils for test to check currentRound value // 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() x.lock.Lock()
defer x.lock.Unlock() 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. // 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 // 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() x.lock.Lock()
defer x.lock.Unlock() defer x.lock.Unlock()
@ -159,13 +158,15 @@ func (x *XDPoS_v2) VoteHandler(voteMsg *utils.Vote) error {
} }
// Collect vote // Collect vote
thresholdReached, numberOfVotesInPool, hookError := x.votePool.Add(voteMsg) thresholdReached, numberOfVotesInPool, pooledVotes := x.votePool.Add(voteMsg)
if hookError != nil { if thresholdReached {
log.Error("Error while adding vote message to the pool, ", hookError) log.Debug("Vote pool threashold reached: %v, number of items in the pool: %v", thresholdReached, numberOfVotesInPool)
return hookError 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 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. Function that will be called by votePool when it reached threshold.
In the engine v2, we will need to generate and process QC 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{} signatures := []utils.Signature{}
for _, v := range pooledVotes { for _, v := range pooledVotes {
signatures = append(signatures, v.(*utils.Vote).Signature) 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, ProposedBlockInfo: currentVoteMsg.(*utils.Vote).ProposedBlockInfo,
Signatures: signatures, Signatures: signatures,
} }
err := x.processQC(quorumCert) err := x.processQC(chain, quorumCert)
if err != nil { if err != nil {
log.Error("Error while processing QC in the Vote handler after reaching pool threshold, ", err) log.Error("Error while processing QC in the Vote handler after reaching pool threshold, ", err)
return 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: Entry point for handling timeout message to process below:
1. checkRoundNumber() 1. checkRoundNumber()
2. Collect timeout 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 { func (x *XDPoS_v2) TimeoutHandler(timeout *utils.Timeout) error {
x.lock.Lock() 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) return fmt.Errorf("Timeout message round number: %v does not match currentRound: %v", timeout.Round, x.currentRound)
} }
// Collect timeout, generate TC // Collect timeout, generate TC
isThresholdReached, numberOfTimeoutsInPool, hookError := x.timeoutPool.Add(timeout) isThresholdReached, numberOfTimeoutsInPool, pooledTimeouts := x.timeoutPool.Add(timeout)
if hookError != nil { // Threshold reached
log.Error("Error adding timeout to the pool, ", hookError.Error()) if isThresholdReached {
return hookError 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 return nil
} }
@ -309,22 +313,28 @@ func (x *XDPoS_v2) verifyTC(timeoutCert *utils.TimeoutCert) error {
return nil 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 { func (x *XDPoS_v2) processQC(blockCahinReader consensus.ChainReader, quorumCert *utils.QuorumCert) error {
// 1. Update HighestQC // 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? //TODO: do I need a clone?
x.highestQuorumCert = quorumCert 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) proposedBlockHeader := blockCahinReader.GetHeaderByHash(quorumCert.ProposedBlockInfo.Hash)
var decodedExtraField utils.ExtraFields_v2 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 x.lockQuorumCert = &decodedExtraField.QuorumCert
proposedBlockRound := &decodedExtraField.Round
// 3. Update commit block info // 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 { if quorumCert.ProposedBlockInfo.Round >= x.currentRound {
err := x.setNewRound(quorumCert.ProposedBlockInfo.Round + 1) err := x.setNewRound(quorumCert.ProposedBlockInfo.Round + 1)
if err != nil { if err != nil {
@ -481,3 +491,31 @@ func (x *XDPoS_v2) getSyncInfo() utils.SyncInfo {
HighestTimeoutCert: x.highestTimeoutCert, 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
}

View file

@ -1,8 +1,6 @@
package utils package utils
import ( import (
"fmt"
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
) )
@ -11,9 +9,8 @@ type PoolObj interface {
PoolKey() string PoolKey() string
} }
type Pool struct { type Pool struct {
objList map[string]map[common.Hash]PoolObj objList map[string]map[common.Hash]PoolObj
threshold int threshold int
onThresholdFn func(objsInPool map[common.Hash]PoolObj, currentObj PoolObj) error
} }
func NewPool(threshold int) *Pool { 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 // return true if it has reached threshold
func (p *Pool) Add(obj PoolObj) (bool, int, error) { func (p *Pool) Add(obj PoolObj) (bool, int, map[common.Hash]PoolObj) {
poolKey := obj.PoolKey() poolKey := obj.PoolKey()
objListKeyed, ok := p.objList[poolKey] objListKeyed, ok := p.objList[poolKey]
if !ok { if !ok {
@ -35,13 +32,9 @@ func (p *Pool) Add(obj PoolObj) (bool, int, error) {
numOfItems := len(objListKeyed) numOfItems := len(objListKeyed)
if numOfItems >= p.threshold { if numOfItems >= p.threshold {
delete(p.objList, poolKey) delete(p.objList, poolKey)
if p.onThresholdFn != nil { return true, numOfItems, objListKeyed
return true, numOfItems, p.onThresholdFn(objListKeyed, obj)
} else {
return true, numOfItems, fmt.Errorf("no call back function for pool")
}
} }
return false, numOfItems, nil return false, numOfItems, objListKeyed
} }
func (p *Pool) Clear() { func (p *Pool) Clear() {
@ -51,7 +44,3 @@ func (p *Pool) Clear() {
func (p *Pool) SetThreshold(t int) { func (p *Pool) SetThreshold(t int) {
p.threshold = t p.threshold = t
} }
func (p *Pool) SetOnThresholdFn(f func(objsInPool map[common.Hash]PoolObj, currentObj PoolObj) error) {
p.onThresholdFn = f
}

View file

@ -1,144 +1,55 @@
package utils package utils
import ( import (
"math/big"
"testing" "testing"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestPoolWithTimeout(t *testing.T) { func TestPoolAdd(t *testing.T) {
assert := assert.New(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 pool := NewPool(2) // 2 is the cert threshold
ret = 0
pool.SetOnThresholdFn(onThresholdFn)
timeout1 := Timeout{Round: 1, Signature: []byte{1}} timeout1 := Timeout{Round: 1, Signature: []byte{1}}
timeout2 := Timeout{Round: 1, Signature: []byte{2}} timeout2 := Timeout{Round: 1, Signature: []byte{2}}
timeout3 := Timeout{Round: 1, Signature: []byte{3}} timeout3 := Timeout{Round: 1, Signature: []byte{3}}
_, numOfItems, err := pool.Add(&timeout1) thresholdReached, numOfItems, pooledTimeouts := pool.Add(&timeout1)
assert.Nil(err) assert.NotNil(pooledTimeouts)
assert.Equal(1, numOfItems) assert.Equal(1, numOfItems)
_, numOfItems, err = pool.Add(&timeout1) assert.False(thresholdReached)
assert.Nil(err) thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout1)
assert.NotNil(pooledTimeouts)
assert.False(thresholdReached)
// Duplicates should not be added // Duplicates should not be added
assert.Equal(1, numOfItems) 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) // Should add the one that is not a duplicates
assert.Nil(err) thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout2)
assert.Equal(2, ret) 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 pool = NewPool(3) // 3 is the cert size
ret = 0 thresholdReached, numOfItems, pooledTimeouts = pool.Add(&timeout1)
pool.SetOnThresholdFn(onThresholdFn) assert.False(thresholdReached)
_, numOfItems, err = pool.Add(&timeout1) assert.NotNil(pooledTimeouts)
assert.Nil(err)
assert.Equal(1, numOfItems) 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(2, numOfItems)
assert.Equal(ret, 0) assert.NotNil(pooledTimeouts)
pool.Clear() 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(1, numOfItems)
assert.Equal(0, ret) assert.NotNil(pooledTimeouts)
}
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)
} }

View file

@ -19,7 +19,7 @@ type broadcastTimeoutFn func(*utils.Timeout)
type broadcastSyncInfoFn func(*utils.SyncInfo) type broadcastSyncInfoFn func(*utils.SyncInfo)
type Bfter struct { type Bfter struct {
blockCahinReader *core.BlockChain blockCahinReader consensus.ChainReader
broadcastCh chan interface{} broadcastCh chan interface{}
quit chan struct{} quit chan struct{}
consensus ConsensusFns consensus ConsensusFns
@ -33,7 +33,7 @@ type Bfter struct {
type ConsensusFns struct { type ConsensusFns struct {
verifyVote func(*utils.Vote) error verifyVote func(*utils.Vote) error
voteHandler func(*utils.Vote) error voteHandler func(consensus.ChainReader, *utils.Vote) error
verifyTimeout func(*utils.Timeout) error verifyTimeout func(*utils.Timeout) error
timeoutHandler func(*utils.Timeout) error timeoutHandler func(*utils.Timeout) error
@ -78,63 +78,70 @@ func (b *Bfter) SetConsensusFuns(engine consensus.Engine) {
} }
// TODO: rename // TODO: rename
func (b *Bfter) Vote(vote *utils.Vote) { func (b *Bfter) Vote(vote *utils.Vote) error {
log.Trace("Receive Vote", "vote", vote) log.Trace("Receive Vote", "vote", vote)
if b.knownVotes.Contains(vote.Hash()) { if b.knownVotes.Contains(vote.Hash()) {
log.Trace("Discarded vote, known vote", "Signature", vote.Signature, "hash", vote.Hash()) log.Trace("Discarded vote, known vote", "Signature", vote.Signature, "hash", vote.Hash())
return return nil
} }
err := b.consensus.verifyVote(vote) err := b.consensus.verifyVote(vote)
if err != nil { if err != nil {
log.Error("Verify BFT Vote", "error", err) log.Error("Verify BFT Vote", "error", err)
return return err
}
err = b.consensus.voteHandler(vote)
if err != nil {
log.Error("handle BFT Vote", "error", err)
return
} }
b.knownVotes.Add(vote.Hash(), true) b.knownVotes.Add(vote.Hash(), true)
b.broadcastCh <- vote 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) log.Trace("Receive Timeout", "timeout", timeout)
if b.knownVotes.Contains(timeout.Hash()) { if b.knownVotes.Contains(timeout.Hash()) {
log.Trace("Discarded Timeout, known Timeout", "Signature", timeout.Signature, "hash", timeout.Hash(), "round", timeout.Round) log.Trace("Discarded Timeout, known Timeout", "Signature", timeout.Signature, "hash", timeout.Hash(), "round", timeout.Round)
return return nil
} }
err := b.consensus.verifyTimeout(timeout) err := b.consensus.verifyTimeout(timeout)
if err != nil { if err != nil {
log.Error("Verify BFT Timeout", "error", err) log.Error("Verify BFT Timeout", "error", err)
return return err
}
err = b.consensus.timeoutHandler(timeout)
if err != nil {
log.Error("handle BFT Timeout", "error", err)
return
} }
b.knownTimeouts.Add(timeout.Hash(), true) b.knownTimeouts.Add(timeout.Hash(), true)
b.broadcastCh <- timeout 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) log.Trace("Receive SyncInfo", "syncInfo", syncInfo)
if b.knownVotes.Contains(syncInfo.Hash()) { if b.knownVotes.Contains(syncInfo.Hash()) {
log.Trace("Discarded SyncInfo, known SyncInfo", "hash", syncInfo.Hash()) log.Trace("Discarded SyncInfo, known SyncInfo", "hash", syncInfo.Hash())
return return nil
} }
err := b.consensus.verifySyncInfo(syncInfo) err := b.consensus.verifySyncInfo(syncInfo)
if err != nil { if err != nil {
log.Error("Verify BFT SyncInfo", "error", err) log.Error("Verify BFT SyncInfo", "error", err)
return return err
} }
b.knownSyncInfos.Add(syncInfo.Hash(), true)
b.broadcastCh <- syncInfo
err = b.consensus.syncInfoHandler(syncInfo) err = b.consensus.syncInfoHandler(syncInfo)
if err != nil { if err != nil {
log.Error("handle BFT SyncInfo", "error", err) log.Error("handle BFT SyncInfo", "error", err)
return return err
} }
b.knownSyncInfos.Add(syncInfo.Hash(), true) return nil
b.broadcastCh <- syncInfo
} }
// Start Bft receiver // Start Bft receiver

View file

@ -6,9 +6,11 @@ import (
"testing" "testing"
"time" "time"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v2" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v2"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/core"
) )
// make different votes based on Signatures // make different votes based on Signatures
@ -29,9 +31,10 @@ type bfterTester struct {
func newTester() *bfterTester { func newTester() *bfterTester {
testConsensus := &XDPoS.XDPoS{EngineV2: &engine_v2.XDPoS_v2{}} testConsensus := &XDPoS.XDPoS{EngineV2: &engine_v2.XDPoS_v2{}}
broadcasts := BroadcastFns{} broadcasts := BroadcastFns{}
blockChain := &core.BlockChain{}
tester := &bfterTester{} tester := &bfterTester{}
tester.bfter = New(broadcasts) tester.bfter = New(broadcasts, blockChain)
tester.bfter.SetConsensusFuns(testConsensus) tester.bfter.SetConsensusFuns(testConsensus)
tester.bfter.broadcastCh = make(chan interface{}) tester.bfter.broadcastCh = make(chan interface{})
tester.bfter.Start() tester.bfter.Start()
@ -52,7 +55,7 @@ func TestSequentialVotes(t *testing.T) {
return nil 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) atomic.AddUint32(&handlerCounter, 1)
return nil return nil
} }
@ -63,7 +66,10 @@ func TestSequentialVotes(t *testing.T) {
votes := makeVotes(targetVotes) votes := makeVotes(targetVotes)
for _, vote := range votes { for _, vote := range votes {
tester.bfter.Vote(&vote) err := tester.bfter.Vote(&vote)
if err != nil {
t.Fatal(err)
}
} }
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
@ -86,7 +92,7 @@ func TestDuplicateVotes(t *testing.T) {
return nil 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) atomic.AddUint32(&handlerCounter, 1)
return nil return nil
} }
@ -118,7 +124,7 @@ func TestNotBoardcastInvalidVote(t *testing.T) {
return fmt.Errorf("This is invalid vote") 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) atomic.AddUint32(&handlerCounter, 1)
return nil return nil
} }

View file

@ -28,7 +28,7 @@ func TestAdaptorShouldGetAuthorForDifferentConsensusVersion(t *testing.T) {
// Insert block 11 // Insert block 11
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", 11) blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", 11)
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
block11, err := insertBlock(blockchain, 11, blockCoinBase, currentBlock, merkleRoot, 1) block11, err := insertBlock(blockchain, 11, blockCoinBase, currentBlock, merkleRoot, nil, 1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -54,7 +54,7 @@ func TestNotChangeSingerListIfNothingProposedOrVoted(t *testing.T) {
// Insert block 450 // Insert block 450
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", 450) blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", 450)
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
block, err := insertBlock(blockchain, 450, blockCoinBase, parentBlock, merkleRoot, 1) block, err := insertBlock(blockchain, 450, blockCoinBase, parentBlock, merkleRoot, nil, 1)
if err != nil { if err != nil {
t.Fatal(err) 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 // Now, let's mine another block to trigger the GAP block signerList update
block450CoinbaseAddress := "0xaaa0000000000000000000000000000000000450" block450CoinbaseAddress := "0xaaa0000000000000000000000000000000000450"
merkleRoot = "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772" merkleRoot = "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
block450, err := insertBlock(blockchain, 450, block450CoinbaseAddress, parentBlock, merkleRoot, 1) block450, err := insertBlock(blockchain, 450, block450CoinbaseAddress, parentBlock, merkleRoot, nil, 1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -240,7 +240,7 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
blockCoinBase451B := "0xbbb0000000000000000000000000000000000451" blockCoinBase451B := "0xbbb0000000000000000000000000000000000451"
merkleRoot = "068dfa09d7b4093441c0cc4d9807a71bc586f6101c072d939b214c21cd136eb3" merkleRoot = "068dfa09d7b4093441c0cc4d9807a71bc586f6101c072d939b214c21cd136eb3"
block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, 1) block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, nil, 1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -378,7 +378,7 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin
blockCoinBase451B := "0xbbb0000000000000000000000000000000000451" blockCoinBase451B := "0xbbb0000000000000000000000000000000000451"
merkleRoot = "184edaddeafc2404248f896ae46be503ae68949896c8eb6b6ad43695581e5022" merkleRoot = "184edaddeafc2404248f896ae46be503ae68949896c8eb6b6ad43695581e5022"
block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, 1) block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, nil, 1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -440,7 +440,7 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
// Insert normal blocks 450 A // Insert normal blocks 450 A
blockCoinBase450A := "0xaaa0000000000000000000000000000000000450" blockCoinBase450A := "0xaaa0000000000000000000000000000000000450"
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
block450A, err := insertBlock(blockchain, 450, blockCoinBase450A, parentBlock, merkleRoot, 1) block450A, err := insertBlock(blockchain, 450, blockCoinBase450A, parentBlock, merkleRoot, nil, 1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -476,21 +476,21 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
// Insert forked Block 450 B // Insert forked Block 450 B
blockCoinBase450B := "0xbbb0000000000000000000000000000000000450" blockCoinBase450B := "0xbbb0000000000000000000000000000000000450"
merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
block450B, err := insertBlock(blockchain, 450, blockCoinBase450B, parentBlock, merkleRoot, 1) block450B, err := insertBlock(blockchain, 450, blockCoinBase450B, parentBlock, merkleRoot, nil, 1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
blockCoinBase451B := "0xbbb0000000000000000000000000000000000451" blockCoinBase451B := "0xbbb0000000000000000000000000000000000451"
merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, 1) block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, nil, 1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
blockCoinBase452B := "0xbbb0000000000000000000000000000000000452" blockCoinBase452B := "0xbbb0000000000000000000000000000000000452"
merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
block452B, err := insertBlock(blockchain, 452, blockCoinBase452B, block451B, merkleRoot, 1) block452B, err := insertBlock(blockchain, 452, blockCoinBase452B, block451B, merkleRoot, nil, 1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -11,7 +11,7 @@ import (
// Snapshot try to read before blockchain is written // Snapshot try to read before blockchain is written
func TestRaceConditionOnBlockchainReadAndWrite(t *testing.T) { 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() state, err := blockchain.State()
if err != nil { if err != nil {
@ -104,7 +104,7 @@ func TestRaceConditionOnBlockchainReadAndWrite(t *testing.T) {
blockCoinBase451B := "0xbbb0000000000000000000000000000000000451" blockCoinBase451B := "0xbbb0000000000000000000000000000000000451"
merkleRoot = "184edaddeafc2404248f896ae46be503ae68949896c8eb6b6ad43695581e5022" merkleRoot = "184edaddeafc2404248f896ae46be503ae68949896c8eb6b6ad43695581e5022"
block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, 3) block451B, err := insertBlock(blockchain, 451, blockCoinBase451B, block450B, merkleRoot, nil, 3)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

View file

@ -10,10 +10,12 @@ import (
"testing" "testing"
"time" "time"
"github.com/XinFinOrg/XDPoSChain/accounts"
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends"
"github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract" contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract"
"github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core"
. "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++ { for i := 1; i <= numOfBlocks; i++ {
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i) blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i)
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -259,13 +325,14 @@ func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params
} }
// insert Block without transcation attached // 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( block, err := createXDPoSTestBlock(
blockchain, blockchain,
parentBlock.Hash().Hex(), parentBlock.Hash().Hex(),
blockCoinBase, blockNum, nil, blockCoinBase, blockNum, nil,
"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
common.HexToHash(root), common.HexToHash(root),
customExtra,
difficulty, difficulty,
) )
if err != nil { if err != nil {
@ -287,6 +354,7 @@ func insertBlockTxs(blockchain *BlockChain, blockNum int, blockCoinBase string,
blockCoinBase, blockNum, txs, blockCoinBase, blockNum, txs,
"0x9319777b782ba2c83a33c995481ff894ac96d9a92a1963091346a3e1e386705c", "0x9319777b782ba2c83a33c995481ff894ac96d9a92a1963091346a3e1e386705c",
common.HexToHash(root), common.HexToHash(root),
nil,
difficulty, difficulty,
) )
if err != nil { if err != nil {
@ -300,11 +368,14 @@ func insertBlockTxs(blockchain *BlockChain, blockNum int, blockCoinBase string,
return block, nil 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) { func createXDPoSTestBlock(bc *BlockChain, parentHash, coinbase string, number int, txs []*types.Transaction, receiptHash string, root common.Hash, customExtra []byte, difficulty int64) (*types.Block, error) {
extraSubstring := "d7830100018358444388676f312e31342e31856c696e75780000000000000000b185dc0d0e917d18e5dbf0746be6597d3331dd27ea0554e6db433feb2e81730b20b2807d33a1527bf43cd3bc057aa7f641609c2551ebe2fd575f4db704fbf38101" // Grabbed from existing mainnet block, it does not have any meaning except for the length validation if customExtra == nil {
//ReceiptHash = "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" extraSubstring := "d7830100018358444388676f312e31342e31856c696e75780000000000000000b185dc0d0e917d18e5dbf0746be6597d3331dd27ea0554e6db433feb2e81730b20b2807d33a1527bf43cd3bc057aa7f641609c2551ebe2fd575f4db704fbf38101" // Grabbed from existing mainnet block, it does not have any meaning except for the length validation
//Root := "0xc99c095e53ff1afe3b86750affd13c7550a2d24d51fb8e41b3c3ef2ea8274bcc" //ReceiptHash = "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
extraByte, _ := hex.DecodeString(extraSubstring) //Root := "0xc99c095e53ff1afe3b86750affd13c7550a2d24d51fb8e41b3c3ef2ea8274bcc"
customExtra, _ = hex.DecodeString(extraSubstring)
}
header := types.Header{ header := types.Header{
ParentHash: common.HexToHash(parentHash), ParentHash: common.HexToHash(parentHash),
UncleHash: types.EmptyUncleHash, UncleHash: types.EmptyUncleHash,
@ -317,14 +388,13 @@ func createXDPoSTestBlock(bc *BlockChain, parentHash, coinbase string, number in
Number: big.NewInt(int64(number)), Number: big.NewInt(int64(number)),
GasLimit: 1200000000, GasLimit: 1200000000,
Time: big.NewInt(int64(number * 10)), Time: big.NewInt(int64(number * 10)),
Extra: extraByte, Extra: customExtra,
} }
var block *types.Block var block *types.Block
if len(txs) == 0 { if len(txs) == 0 {
block = types.NewBlockWithHeader(&header) block = types.NewBlockWithHeader(&header)
} else { } else {
// Prepare Receipt // Prepare Receipt
statedb, err := bc.StateAt(bc.GetBlockByNumber(uint64(number - 1)).Root()) //Get parent root statedb, err := bc.StateAt(bc.GetBlockByNumber(uint64(number - 1)).Root()) //Get parent root
if err != nil { if err != nil {

View file

@ -42,14 +42,16 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) {
err := engineV2.TimeoutHandler(timeoutMsg) err := engineV2.TimeoutHandler(timeoutMsg)
assert.Nil(t, err) 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{ timeoutMsg = &utils.Timeout{
Round: utils.Round(1), Round: utils.Round(1),
Signature: []byte{2}, Signature: []byte{2},
} }
err = engineV2.TimeoutHandler(timeoutMsg) err = engineV2.TimeoutHandler(timeoutMsg)
assert.Nil(t, err) 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 // Create a timeout message that should trigger timeout pool hook
timeoutMsg = &utils.Timeout{ timeoutMsg = &utils.Timeout{
Round: utils.Round(1), Round: utils.Round(1),
@ -60,6 +62,9 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
syncInfoMsg := <-engineV2.BroadcastCh syncInfoMsg := <-engineV2.BroadcastCh
currentRound, _, _ = engineV2.GetProperties()
assert.NotNil(t, syncInfoMsg) assert.NotNil(t, syncInfoMsg)
// Should have QC, however, we did not inilise it, hence will show default empty value // 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)) assert.Equal(t, tc.Round, utils.Round(1))
sigatures := []utils.Signature{[]byte{1}, []byte{2}, []byte{3}} sigatures := []utils.Signature{[]byte{1}, []byte{2}, []byte{3}}
assert.ElementsMatch(t, tc.Signatures, sigatures) 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) { func TestThrowErrorIfTimeoutMsgRoundNotEqualToCurrentRound(t *testing.T) {
@ -99,106 +104,129 @@ func TestThrowErrorIfTimeoutMsgRoundNotEqualToCurrentRound(t *testing.T) {
} }
// VoteHandler // VoteHandler
func TestVoteMessageHandlerSuccessfullyGeneratedQC(t *testing.T) { func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) {
blockchain, _, _, _ := PrepareXDCTestBlockChain(t, 11, params.TestXDPoSMockChainConfigWithV2Engine) blockchain, _, currentBlock, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine)
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
// parentBlock := blockchain.GetBlockByHash(currentBlock.ParentHash())
// grandParentBlock := blockchain.GetBlockByHash(parentBlock.ParentHash())
blockInfo := &utils.BlockInfo{ blockInfo := &utils.BlockInfo{
Hash: common.HexToHash("0x1"), Hash: currentBlock.Hash(),
Round: utils.Round(1), Round: utils.Round(11),
Number: big.NewInt(999), Number: big.NewInt(11),
} }
// Set round to 1 // Set round to 11
engineV2.SetNewRoundFaker(utils.Round(1), false) engineV2.SetNewRoundFaker(utils.Round(11), false)
// Create two timeout message which will not reach vote pool threshold // Create two timeout message which will not reach vote pool threshold
voteMsg := &utils.Vote{ voteMsg := &utils.Vote{
ProposedBlockInfo: *blockInfo, ProposedBlockInfo: *blockInfo,
Signature: []byte{1}, Signature: []byte{1},
} }
err := engineV2.VoteHandler(voteMsg) err := engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) 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{ voteMsg = &utils.Vote{
ProposedBlockInfo: *blockInfo, ProposedBlockInfo: *blockInfo,
Signature: []byte{2}, Signature: []byte{2},
} }
err = engineV2.VoteHandler(voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) 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{ voteMsg = &utils.Vote{
ProposedBlockInfo: *blockInfo, ProposedBlockInfo: *blockInfo,
Signature: []byte{3}, Signature: []byte{3},
} }
err = engineV2.VoteHandler(voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) assert.Nil(t, err)
// Check round has now changed from 1 to 2 currentRound, lockQuorumCert, highestQuorumCert = engineV2.GetProperties()
assert.Equal(t, utils.Round(2), engineV2.GetCurrentRound()) // 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) { func TestThrowErrorIfVoteMsgRoundNotEqualToCurrentRound(t *testing.T) {
blockchain, _, _, _ := PrepareXDCTestBlockChain(t, 11, params.TestXDPoSMockChainConfigWithV2Engine) blockchain, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine)
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
blockInfo := &utils.BlockInfo{ blockInfo := &utils.BlockInfo{
Hash: common.HexToHash("0x1"), Hash: common.HexToHash("0x1"),
Round: utils.Round(2), Round: utils.Round(12),
Number: big.NewInt(999), Number: big.NewInt(999),
} }
// Set round to 3 // Set round to 13
engineV2.SetNewRoundFaker(utils.Round(3), false) engineV2.SetNewRoundFaker(utils.Round(13), false)
voteMsg := &utils.Vote{ voteMsg := &utils.Vote{
ProposedBlockInfo: *blockInfo, ProposedBlockInfo: *blockInfo,
Signature: []byte{1}, Signature: []byte{1},
} }
// voteRound > currentRound // voteRound > currentRound
err := engineV2.VoteHandler(voteMsg) err := engineV2.VoteHandler(blockchain, voteMsg)
assert.NotNil(t, err) 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 // Set round to 11
engineV2.SetNewRoundFaker(utils.Round(1), false) engineV2.SetNewRoundFaker(utils.Round(11), false)
err = engineV2.VoteHandler(voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.NotNil(t, err) assert.NotNil(t, err)
// voteRound < currentRound // 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) { func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
blockchain, _, _, _ := PrepareXDCTestBlockChain(t, 11, params.TestXDPoSMockChainConfigWithV2Engine) blockchain, _, currentBlock, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine)
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
// Set round to 1 // Set round to 1
engineV2.SetNewRoundFaker(utils.Round(1), false) engineV2.SetNewRoundFaker(utils.Round(11), false)
// Start with vote messages // Start with vote messages
blockInfo := &utils.BlockInfo{ blockInfo := &utils.BlockInfo{
Hash: common.HexToHash("0x1"), Hash: currentBlock.Hash(),
Round: utils.Round(1), Round: utils.Round(11),
Number: big.NewInt(999), 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{ voteMsg := &utils.Vote{
ProposedBlockInfo: *blockInfo, ProposedBlockInfo: *blockInfo,
Signature: []byte{1}, Signature: []byte{1},
} }
err := engineV2.VoteHandler(voteMsg) err := engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) 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{ voteMsg = &utils.Vote{
ProposedBlockInfo: *blockInfo, ProposedBlockInfo: *blockInfo,
Signature: []byte{2}, Signature: []byte{2},
} }
err = engineV2.VoteHandler(voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) 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 // Create a vote message that should trigger vote pool hook
voteMsg = &utils.Vote{ voteMsg = &utils.Vote{
@ -206,42 +234,51 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
Signature: []byte{3}, Signature: []byte{3},
} }
err = engineV2.VoteHandler(voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) assert.Nil(t, err)
// Check round has now changed from 1 to 2 // Check round has now changed from 11 to 12
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)
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 // 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 // First, all incoming old timeout msg shall not be processed
timeoutMsg := &utils.Timeout{ timeoutMsg := &utils.Timeout{
Round: utils.Round(1), Round: utils.Round(11),
Signature: []byte{1}, Signature: []byte{1},
} }
err = engineV2.TimeoutHandler(timeoutMsg) err = engineV2.TimeoutHandler(timeoutMsg)
assert.NotNil(t, err) 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 // 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{ timeoutMsg = &utils.Timeout{
Round: utils.Round(2), Round: utils.Round(12),
Signature: []byte{1}, Signature: []byte{1},
} }
err = engineV2.TimeoutHandler(timeoutMsg) err = engineV2.TimeoutHandler(timeoutMsg)
assert.Nil(t, err) 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{ timeoutMsg = &utils.Timeout{
Round: utils.Round(2), Round: utils.Round(12),
Signature: []byte{2}, Signature: []byte{2},
} }
err = engineV2.TimeoutHandler(timeoutMsg) err = engineV2.TimeoutHandler(timeoutMsg)
assert.Nil(t, err) 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 // Create a timeout message that should trigger timeout pool hook
timeoutMsg = &utils.Timeout{ timeoutMsg = &utils.Timeout{
Round: utils.Round(2), Round: utils.Round(12),
Signature: []byte{3}, Signature: []byte{3},
} }
@ -254,13 +291,14 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
// Should have HighestQuorumCert from previous round votes // Should have HighestQuorumCert from previous round votes
qc := syncInfoMsg.(utils.SyncInfo).HighestQuorumCert qc := syncInfoMsg.(utils.SyncInfo).HighestQuorumCert
assert.NotNil(t, qc) 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 tc := syncInfoMsg.(utils.SyncInfo).HighestTimeoutCert
assert.NotNil(t, tc) 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}} sigatures := []utils.Signature{[]byte{1}, []byte{2}, []byte{3}}
assert.ElementsMatch(t, tc.Signatures, sigatures) assert.ElementsMatch(t, tc.Signatures, sigatures)
// Round shall be +1 now // Round shall be +1 now
assert.Equal(t, utils.Round(3), engineV2.GetCurrentRound()) currentRound, _, _ = engineV2.GetProperties()
assert.Equal(t, utils.Round(13), currentRound)
} }