mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-26 16:36:17 +00:00
add ProcessQC tests
This commit is contained in:
parent
bd60e1b0cf
commit
163ed0fab3
10 changed files with 321 additions and 262 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue