mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 02:40:45 +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
|
// 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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue