mirror of
https://github.com/fluencelabs/tendermint
synced 2025-05-09 13:25:04 +00:00
288 lines
7.7 KiB
Go
288 lines
7.7 KiB
Go
|
package blockchain_new
|
||
|
|
||
|
import (
|
||
|
"github.com/stretchr/testify/assert"
|
||
|
cmn "github.com/tendermint/tendermint/libs/common"
|
||
|
dbm "github.com/tendermint/tendermint/libs/db"
|
||
|
"github.com/tendermint/tendermint/libs/log"
|
||
|
"github.com/tendermint/tendermint/p2p"
|
||
|
"github.com/tendermint/tendermint/types"
|
||
|
|
||
|
"testing"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
failSendStatusRequest bool
|
||
|
failSendBlockRequest bool
|
||
|
numStatusRequests int
|
||
|
numBlockRequests int
|
||
|
)
|
||
|
|
||
|
type lastBlockRequestT struct {
|
||
|
peerID p2p.ID
|
||
|
height int64
|
||
|
}
|
||
|
|
||
|
var lastBlockRequest lastBlockRequestT
|
||
|
|
||
|
type lastPeerErrorT struct {
|
||
|
peerID p2p.ID
|
||
|
err error
|
||
|
}
|
||
|
|
||
|
var lastPeerError lastPeerErrorT
|
||
|
|
||
|
var stateTimerStarts map[string]int
|
||
|
|
||
|
func resetTestValues() {
|
||
|
stateTimerStarts = make(map[string]int)
|
||
|
failSendBlockRequest = false
|
||
|
failSendStatusRequest = false
|
||
|
numStatusRequests = 0
|
||
|
numBlockRequests = 0
|
||
|
lastBlockRequest.peerID = ""
|
||
|
lastBlockRequest.height = 0
|
||
|
lastPeerError.peerID = ""
|
||
|
lastPeerError.err = nil
|
||
|
}
|
||
|
|
||
|
type fsmStepTestValues struct {
|
||
|
currentState string
|
||
|
event bReactorEvent
|
||
|
expectedState string
|
||
|
|
||
|
// input
|
||
|
failStatusReq bool
|
||
|
shouldSendStatusReq bool
|
||
|
|
||
|
failBlockReq bool
|
||
|
blockReqIncreased bool
|
||
|
|
||
|
data bReactorEventData
|
||
|
|
||
|
expectedLastBlockReq *lastBlockRequestT
|
||
|
}
|
||
|
|
||
|
// WIP
|
||
|
func TestFSMTransitionSequences(t *testing.T) {
|
||
|
maxRequestBatchSize = 2
|
||
|
fsmTransitionSequenceTests := [][]fsmStepTestValues{
|
||
|
{
|
||
|
{currentState: "unknown", event: startFSMEv, shouldSendStatusReq: true,
|
||
|
expectedState: "waitForPeer"},
|
||
|
{currentState: "waitForPeer", event: statusResponseEv,
|
||
|
data: bReactorEventData{peerId: "P1", height: 10},
|
||
|
blockReqIncreased: true,
|
||
|
expectedState: "waitForBlock"},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, tt := range fsmTransitionSequenceTests {
|
||
|
// Create and start the FSM
|
||
|
testBcR := &testReactor{logger: log.TestingLogger()}
|
||
|
blockDB := dbm.NewMemDB()
|
||
|
store := NewBlockStore(blockDB)
|
||
|
fsm := NewFSM(store, testBcR)
|
||
|
fsm.setLogger(log.TestingLogger())
|
||
|
resetTestValues()
|
||
|
|
||
|
// always start from unknown
|
||
|
fsm.resetStateTimer(unknown)
|
||
|
assert.Equal(t, 1, stateTimerStarts[unknown.name])
|
||
|
|
||
|
for _, step := range tt {
|
||
|
assert.Equal(t, step.currentState, fsm.state.name)
|
||
|
failSendStatusRequest = step.failStatusReq
|
||
|
failSendBlockRequest = step.failBlockReq
|
||
|
|
||
|
oldNumStatusRequests := numStatusRequests
|
||
|
oldNumBlockRequests := numBlockRequests
|
||
|
|
||
|
_ = sendEventToFSM(fsm, step.event, step.data)
|
||
|
if step.shouldSendStatusReq {
|
||
|
assert.Equal(t, oldNumStatusRequests+1, numStatusRequests)
|
||
|
} else {
|
||
|
assert.Equal(t, oldNumStatusRequests, numStatusRequests)
|
||
|
}
|
||
|
|
||
|
if step.blockReqIncreased {
|
||
|
assert.Equal(t, oldNumBlockRequests+maxRequestBatchSize, numBlockRequests)
|
||
|
} else {
|
||
|
assert.Equal(t, oldNumBlockRequests, numBlockRequests)
|
||
|
}
|
||
|
|
||
|
assert.Equal(t, step.expectedState, fsm.state.name)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestReactorFSMBasic(t *testing.T) {
|
||
|
maxRequestBatchSize = 2
|
||
|
|
||
|
resetTestValues()
|
||
|
// Create and start the FSM
|
||
|
testBcR := &testReactor{logger: log.TestingLogger()}
|
||
|
blockDB := dbm.NewMemDB()
|
||
|
store := NewBlockStore(blockDB)
|
||
|
fsm := NewFSM(store, testBcR)
|
||
|
fsm.setLogger(log.TestingLogger())
|
||
|
|
||
|
if err := fsm.handle(&bReactorMessageData{event: startFSMEv}); err != nil {
|
||
|
}
|
||
|
|
||
|
// Check that FSM sends a status request message
|
||
|
assert.Equal(t, 1, numStatusRequests)
|
||
|
assert.Equal(t, waitForPeer.name, fsm.state.name)
|
||
|
|
||
|
// Send a status response message to FSM
|
||
|
peerID := p2p.ID(cmn.RandStr(12))
|
||
|
sendStatusResponse2(fsm, peerID, 10)
|
||
|
|
||
|
// Check that FSM sends a block request message and...
|
||
|
assert.Equal(t, maxRequestBatchSize, numBlockRequests)
|
||
|
// ... the block request has the expected height
|
||
|
assert.Equal(t, int64(maxRequestBatchSize), lastBlockRequest.height)
|
||
|
assert.Equal(t, waitForBlock.name, fsm.state.name)
|
||
|
}
|
||
|
|
||
|
func TestReactorFSMPeerTimeout(t *testing.T) {
|
||
|
maxRequestBatchSize = 2
|
||
|
resetTestValues()
|
||
|
peerTimeout = 20 * time.Millisecond
|
||
|
// Create and start the FSM
|
||
|
testBcR := &testReactor{logger: log.TestingLogger()}
|
||
|
blockDB := dbm.NewMemDB()
|
||
|
store := NewBlockStore(blockDB)
|
||
|
fsm := NewFSM(store, testBcR)
|
||
|
fsm.setLogger(log.TestingLogger())
|
||
|
fsm.start()
|
||
|
|
||
|
// Check that FSM sends a status request message
|
||
|
time.Sleep(time.Millisecond)
|
||
|
assert.Equal(t, 1, numStatusRequests)
|
||
|
|
||
|
// Send a status response message to FSM
|
||
|
peerID := p2p.ID(cmn.RandStr(12))
|
||
|
sendStatusResponse(fsm, peerID, 10)
|
||
|
time.Sleep(5 * time.Millisecond)
|
||
|
|
||
|
// Check that FSM sends a block request message and...
|
||
|
assert.Equal(t, maxRequestBatchSize, numBlockRequests)
|
||
|
// ... the block request has the expected height and peer
|
||
|
assert.Equal(t, int64(maxRequestBatchSize), lastBlockRequest.height)
|
||
|
assert.Equal(t, peerID, lastBlockRequest.peerID)
|
||
|
|
||
|
// let FSM timeout on the block response message
|
||
|
time.Sleep(100 * time.Millisecond)
|
||
|
|
||
|
}
|
||
|
|
||
|
// reactor for FSM testing
|
||
|
type testReactor struct {
|
||
|
logger log.Logger
|
||
|
fsm *bReactorFSM
|
||
|
testCh chan bReactorMessageData
|
||
|
}
|
||
|
|
||
|
func sendEventToFSM(fsm *bReactorFSM, ev bReactorEvent, data bReactorEventData) error {
|
||
|
return fsm.handle(&bReactorMessageData{event: ev, data: data})
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------
|
||
|
// implementation for the test reactor APIs
|
||
|
|
||
|
func (testR *testReactor) sendPeerError(err error, peerID p2p.ID) {
|
||
|
testR.logger.Info("Reactor received sendPeerError call from FSM", "peer", peerID, "err", err)
|
||
|
lastPeerError.peerID = peerID
|
||
|
lastPeerError.err = err
|
||
|
}
|
||
|
|
||
|
func (testR *testReactor) sendStatusRequest() error {
|
||
|
testR.logger.Info("Reactor received sendStatusRequest call from FSM")
|
||
|
numStatusRequests++
|
||
|
if failSendStatusRequest {
|
||
|
return errSendQueueFull
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (testR *testReactor) sendBlockRequest(peerID p2p.ID, height int64) error {
|
||
|
testR.logger.Info("Reactor received sendBlockRequest call from FSM", "peer", peerID, "height", height)
|
||
|
numBlockRequests++
|
||
|
lastBlockRequest.peerID = peerID
|
||
|
lastBlockRequest.height = height
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (testR *testReactor) resetStateTimer(name string, timer *time.Timer, timeout time.Duration, f func()) {
|
||
|
testR.logger.Info("Reactor received resetStateTimer call from FSM", "state", name, "timeout", timeout)
|
||
|
if _, ok := stateTimerStarts[name]; !ok {
|
||
|
stateTimerStarts[name] = 1
|
||
|
} else {
|
||
|
stateTimerStarts[name]++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (testR *testReactor) processBlocks(first *types.Block, second *types.Block) error {
|
||
|
testR.logger.Info("Reactor received processBlocks call from FSM", "first", first.Height, "second", second.Height)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (testR *testReactor) switchToConsensus() {
|
||
|
testR.logger.Info("Reactor received switchToConsensus call from FSM")
|
||
|
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------
|
||
|
|
||
|
// -------------------------------------------------------
|
||
|
// helper functions for tests to simulate different events
|
||
|
func sendStatusResponse(fsm *bReactorFSM, peerID p2p.ID, height int64) {
|
||
|
msgBytes := makeStatusResponseMessage(height)
|
||
|
msgData := bReactorMessageData{
|
||
|
event: statusResponseEv,
|
||
|
data: bReactorEventData{
|
||
|
peerId: peerID,
|
||
|
height: height,
|
||
|
length: len(msgBytes),
|
||
|
},
|
||
|
}
|
||
|
|
||
|
sendMessageToFSM(fsm, msgData)
|
||
|
}
|
||
|
|
||
|
func sendStatusResponse2(fsm *bReactorFSM, peerID p2p.ID, height int64) {
|
||
|
msgBytes := makeStatusResponseMessage(height)
|
||
|
msgData := &bReactorMessageData{
|
||
|
event: statusResponseEv,
|
||
|
data: bReactorEventData{
|
||
|
peerId: peerID,
|
||
|
height: height,
|
||
|
length: len(msgBytes),
|
||
|
},
|
||
|
}
|
||
|
_ = fsm.handle(msgData)
|
||
|
}
|
||
|
|
||
|
func sendStateTimeout(fsm *bReactorFSM, name string) {
|
||
|
msgData := &bReactorMessageData{
|
||
|
event: stateTimeoutEv,
|
||
|
data: bReactorEventData{
|
||
|
stateName: name,
|
||
|
},
|
||
|
}
|
||
|
_ = fsm.handle(msgData)
|
||
|
}
|
||
|
|
||
|
// -------------------------------------------------------
|
||
|
|
||
|
// ----------------------------------------------------
|
||
|
// helper functions to make blockchain reactor messages
|
||
|
func makeStatusResponseMessage(height int64) []byte {
|
||
|
msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{height})
|
||
|
return msgBytes
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------
|