go-ethereum/eth/bft/bft_hander_test.go
Liam 6c5fe34615 v2 miner function implementation and happy path (#22)
* New struct in consensus/XDPoS/utils/types.go, util functions, and test. (#14)

* define vote, timeout, sync info, qc, tc, extra fields in types.go, add test in types_test.go

* add json tag in types.go, refine encoder decoder of extra fields

* refactor types.go utils.go

* re-write types, comments

* add Hash SigHash for types, and tests

* define Round type

* remove unnecessary logs

* add v2 engine functions placeholder

* typo fix on the consensus v2 function placeholders

* add countdown timer

* make initilised private to countdown

* add v2 specific config struct

* rename some config variables

* Implement BFT Message receiver (#13)

* fix or skip tests due to PR-136 changes

* add bft receiver functions

* add bft receiver functions

* rename tc to TimeoutCert

* implement more functions

* New struct in consensus/XDPoS/utils/types.go, util functions, and test. (#14)

* define vote, timeout, sync info, qc, tc, extra fields in types.go, add test in types_test.go

* add json tag in types.go, refine encoder decoder of extra fields

* refactor types.go utils.go

* re-write types, comments

* add Hash SigHash for types, and tests

* define Round type

* remove unnecessary logs

* add temp functions

* add v2 engine functions placeholder

* typo fix on the consensus v2 function placeholders

* add countdown timer

* make initilised private to countdown

* push verify function

* add test on receiving vote

* revert type change

* add async on broadcast function

* add quit initial

* fix test

Co-authored-by: Jianrong <wjrjerome@gmail.com>
Co-authored-by: wgr523 <wgr523@gmail.com>

* generate and verify timeout message

* Consensus V2 variable, timeout pool (#19)

* fill in XDPoS_v2 variables and processQC/TC

* add timeout pool, refine engine variables

* refactor type functions

* solve a small pointer bug

* create general pool and its test, refine engine

* refine pool, add xdpos v2 config cert threshold

* refine config

* vote and timeout handlers

* fix pool test

* bft miner preparation

* review comment improvement

* update

* relocate tests

* add and remove comment

* fix the syntax error

* update network layer and add handler functions (#23)

* update network layer and add handler functions

* fix test syntax error

* add ProcessQC implementation

* add ProcessQC tests

* add snapshot test

* add wait qc process

* remove testing files

* add route snapshot

* fix merge issue

* add default v2 behaviour (#24)

* add v2 ecrecover functions and refactor test

* fix all the tests

* put minimun lock variable

* debugging prepare and seal v2 blocks

* Trigger proposeBlockHandler after v2 block received and verified in fetcher

* skip snapshot apply related tests

* update test check

* rename bfter to bft handler and ignore normal behviour

* fix bugs during local 4 node run

* fix test

* fix sync info test

* fix bugs during local 4 node run

* rebase and fix bug

* remove hook validators function"

Co-authored-by: wgr523 <wgr523@gmail.com>
Co-authored-by: Jianrong <wjrjerome@gmail.com>
2021-12-30 11:45:18 +11:00

205 lines
5.7 KiB
Go

package bft
import (
"fmt"
"sync/atomic"
"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"
"github.com/stretchr/testify/assert"
)
// make different votes based on Signatures
func makeVotes(n int) []utils.Vote {
var votes []utils.Vote
for i := 0; i < n; i++ {
votes = append(votes, utils.Vote{
ProposedBlockInfo: &utils.BlockInfo{},
Signature: []byte{byte(i)},
})
}
return votes
}
// bfterTester is a test simulator for mocking out bfter worker.
type bfterTester struct {
bfter *Bfter
}
// newTester creates a new bft fetcher test mocker.
func newTester() *bfterTester {
testConsensus := &XDPoS.XDPoS{EngineV2: &engine_v2.XDPoS_v2{}}
broadcasts := BroadcastFns{}
blockChain := &core.BlockChain{}
tester := &bfterTester{}
tester.bfter = New(broadcasts, blockChain)
tester.bfter.SetConsensusFuns(testConsensus)
tester.bfter.broadcastCh = make(chan interface{})
tester.bfter.Start()
return tester
}
// Tests that a bfter accepts vote and process verfiy and broadcast
func TestSequentialVotes(t *testing.T) {
tester := newTester()
verifyCounter := uint32(0)
handlerCounter := uint32(0)
broadcastCounter := uint32(0)
targetVotes := 10
tester.bfter.consensus.verifyVote = func(vote *utils.Vote) error {
atomic.AddUint32(&verifyCounter, 1)
return nil
}
tester.bfter.consensus.voteHandler = func(chain consensus.ChainReader, vote *utils.Vote) error {
atomic.AddUint32(&handlerCounter, 1)
return nil
}
tester.bfter.broadcast.Vote = func(*utils.Vote) {
atomic.AddUint32(&broadcastCounter, 1)
}
votes := makeVotes(targetVotes)
for _, vote := range votes {
err := tester.bfter.Vote(&vote)
if err != nil {
t.Fatal(err)
}
}
time.Sleep(100 * time.Millisecond)
if int(verifyCounter) != targetVotes || int(handlerCounter) != targetVotes || int(broadcastCounter) != targetVotes {
t.Fatalf("count mismatch: have %v on verify, %v on handler, %v on broadcast, want %v", verifyCounter, handlerCounter, broadcastCounter, targetVotes)
}
}
// Tests that vote already being retrieved will not be duplicated.
func TestDuplicateVotes(t *testing.T) {
tester := newTester()
verifyCounter := uint32(0)
handlerCounter := uint32(0)
broadcastCounter := uint32(0)
targetVotes := 1
tester.bfter.consensus.verifyVote = func(vote *utils.Vote) error {
atomic.AddUint32(&verifyCounter, 1)
return nil
}
tester.bfter.consensus.voteHandler = func(chain consensus.ChainReader, vote *utils.Vote) error {
atomic.AddUint32(&handlerCounter, 1)
return nil
}
tester.bfter.broadcast.Vote = func(*utils.Vote) {
atomic.AddUint32(&broadcastCounter, 1)
}
vote := utils.Vote{ProposedBlockInfo: &utils.BlockInfo{}}
// send twice
tester.bfter.Vote(&vote)
tester.bfter.Vote(&vote)
time.Sleep(50 * time.Millisecond)
if int(verifyCounter) != targetVotes || int(handlerCounter) != targetVotes || int(broadcastCounter) != targetVotes {
t.Fatalf("count mismatch: have %v on verify, %v on handler, %v on broadcast, want %v", verifyCounter, handlerCounter, broadcastCounter, targetVotes)
}
}
// Test that avoid boardcast if there is bad vote
func TestNotBoardcastInvalidVote(t *testing.T) {
tester := newTester()
handlerCounter := uint32(0)
broadcastCounter := uint32(0)
targetVotes := 0
tester.bfter.consensus.verifyVote = func(vote *utils.Vote) error {
return fmt.Errorf("This is invalid vote")
}
tester.bfter.consensus.voteHandler = func(chain consensus.ChainReader, vote *utils.Vote) error {
atomic.AddUint32(&handlerCounter, 1)
return nil
}
tester.bfter.broadcast.Vote = func(*utils.Vote) {
atomic.AddUint32(&broadcastCounter, 1)
}
vote := utils.Vote{ProposedBlockInfo: &utils.BlockInfo{}}
tester.bfter.Vote(&vote)
time.Sleep(50 * time.Millisecond)
if int(handlerCounter) != targetVotes || int(broadcastCounter) != targetVotes {
t.Fatalf("count mismatch: have %v on handler, %v on broadcast, want %v", handlerCounter, broadcastCounter, targetVotes)
}
}
// TODO: SyncInfo and Timeout Test, should be same as Vote.
// Once all test on vote covered, then duplicate to others
func TestTimeoutHandler(t *testing.T) {
tester := newTester()
verifyCounter := uint32(0)
handlerCounter := uint32(0)
broadcastCounter := uint32(0)
targetVotes := 1
tester.bfter.consensus.verifyTimeout = func(timeout *utils.Timeout) error {
atomic.AddUint32(&verifyCounter, 1)
return nil
}
tester.bfter.consensus.timeoutHandler = func(timeout *utils.Timeout) error {
atomic.AddUint32(&handlerCounter, 1)
return nil
}
tester.bfter.broadcast.Timeout = func(*utils.Timeout) {
atomic.AddUint32(&broadcastCounter, 1)
}
timeoutMsg := &utils.Timeout{}
err := tester.bfter.Timeout(timeoutMsg)
if err != nil {
t.Fatal(err)
}
time.Sleep(100 * time.Millisecond)
if int(verifyCounter) != targetVotes || int(handlerCounter) != targetVotes || int(broadcastCounter) != targetVotes {
t.Fatalf("count mismatch: have %v on verify, %v on handler, %v on broadcast, want %v", verifyCounter, handlerCounter, broadcastCounter, targetVotes)
}
}
func TestTimeoutHandlerRoundNotEqual(t *testing.T) {
tester := newTester()
tester.bfter.consensus.verifyTimeout = func(timeout *utils.Timeout) error {
return nil
}
tester.bfter.consensus.timeoutHandler = func(timeout *utils.Timeout) error {
return &utils.ErrIncomingMessageRoundNotEqualCurrentRound{utils.Round(1), utils.Round(2)}
}
tester.bfter.broadcast.Timeout = func(*utils.Timeout) {
return
}
timeoutMsg := &utils.Timeout{}
err := tester.bfter.Timeout(timeoutMsg)
assert.Equal(t, "Timeout message round number: 1 does not match currentRound: 2", err.Error())
}