|
package blockchain
|
|
|
|
import (
|
|
"net"
|
|
"testing"
|
|
|
|
cmn "github.com/tendermint/tendermint/libs/common"
|
|
dbm "github.com/tendermint/tendermint/libs/db"
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
|
|
cfg "github.com/tendermint/tendermint/config"
|
|
"github.com/tendermint/tendermint/p2p"
|
|
"github.com/tendermint/tendermint/proxy"
|
|
sm "github.com/tendermint/tendermint/state"
|
|
"github.com/tendermint/tendermint/types"
|
|
)
|
|
|
|
func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore) {
|
|
config := cfg.ResetTestRoot("blockchain_reactor_test")
|
|
// blockDB := dbm.NewDebugDB("blockDB", dbm.NewMemDB())
|
|
// stateDB := dbm.NewDebugDB("stateDB", dbm.NewMemDB())
|
|
blockDB := dbm.NewMemDB()
|
|
stateDB := dbm.NewMemDB()
|
|
blockStore := NewBlockStore(blockDB)
|
|
state, err := sm.LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile())
|
|
if err != nil {
|
|
panic(cmn.ErrorWrap(err, "error constructing state from genesis file"))
|
|
}
|
|
return state, blockStore
|
|
}
|
|
|
|
func newBlockchainReactor(logger log.Logger, maxBlockHeight int64) *BlockchainReactor {
|
|
state, blockStore := makeStateAndBlockStore(logger)
|
|
|
|
// Make the blockchainReactor itself
|
|
fastSync := true
|
|
var nilApp proxy.AppConnConsensus
|
|
blockExec := sm.NewBlockExecutor(dbm.NewMemDB(), log.TestingLogger(), nilApp,
|
|
sm.MockMempool{}, sm.MockEvidencePool{})
|
|
|
|
bcReactor := NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync)
|
|
bcReactor.SetLogger(logger.With("module", "blockchain"))
|
|
|
|
// Next: we need to set a switch in order for peers to be added in
|
|
bcReactor.Switch = p2p.NewSwitch(cfg.DefaultP2PConfig(), nil)
|
|
|
|
// Lastly: let's add some blocks in
|
|
for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ {
|
|
firstBlock := makeBlock(blockHeight, state)
|
|
secondBlock := makeBlock(blockHeight+1, state)
|
|
firstParts := firstBlock.MakePartSet(types.BlockPartSizeBytes)
|
|
blockStore.SaveBlock(firstBlock, firstParts, secondBlock.LastCommit)
|
|
}
|
|
|
|
return bcReactor
|
|
}
|
|
|
|
func TestNoBlockResponse(t *testing.T) {
|
|
maxBlockHeight := int64(20)
|
|
|
|
bcr := newBlockchainReactor(log.TestingLogger(), maxBlockHeight)
|
|
bcr.Start()
|
|
defer bcr.Stop()
|
|
|
|
// Add some peers in
|
|
peer := newbcrTestPeer(p2p.ID(cmn.RandStr(12)))
|
|
bcr.AddPeer(peer)
|
|
|
|
chID := byte(0x01)
|
|
|
|
tests := []struct {
|
|
height int64
|
|
existent bool
|
|
}{
|
|
{maxBlockHeight + 2, false},
|
|
{10, true},
|
|
{1, true},
|
|
{100, false},
|
|
}
|
|
|
|
// receive a request message from peer,
|
|
// wait for our response to be received on the peer
|
|
for _, tt := range tests {
|
|
reqBlockMsg := &bcBlockRequestMessage{tt.height}
|
|
reqBlockBytes := cdc.MustMarshalBinaryBare(reqBlockMsg)
|
|
bcr.Receive(chID, peer, reqBlockBytes)
|
|
msg := peer.lastBlockchainMessage()
|
|
|
|
if tt.existent {
|
|
if blockMsg, ok := msg.(*bcBlockResponseMessage); !ok {
|
|
t.Fatalf("Expected to receive a block response for height %d", tt.height)
|
|
} else if blockMsg.Block.Height != tt.height {
|
|
t.Fatalf("Expected response to be for height %d, got %d", tt.height, blockMsg.Block.Height)
|
|
}
|
|
} else {
|
|
if noBlockMsg, ok := msg.(*bcNoBlockResponseMessage); !ok {
|
|
t.Fatalf("Expected to receive a no block response for height %d", tt.height)
|
|
} else if noBlockMsg.Height != tt.height {
|
|
t.Fatalf("Expected response to be for height %d, got %d", tt.height, noBlockMsg.Height)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
// NOTE: This is too hard to test without
|
|
// an easy way to add test peer to switch
|
|
// or without significant refactoring of the module.
|
|
// Alternatively we could actually dial a TCP conn but
|
|
// that seems extreme.
|
|
func TestBadBlockStopsPeer(t *testing.T) {
|
|
maxBlockHeight := int64(20)
|
|
|
|
bcr := newBlockchainReactor(log.TestingLogger(), maxBlockHeight)
|
|
bcr.Start()
|
|
defer bcr.Stop()
|
|
|
|
// Add some peers in
|
|
peer := newbcrTestPeer(p2p.ID(cmn.RandStr(12)))
|
|
|
|
// XXX: This doesn't add the peer to anything,
|
|
// so it's hard to check that it's later removed
|
|
bcr.AddPeer(peer)
|
|
assert.True(t, bcr.Switch.Peers().Size() > 0)
|
|
|
|
// send a bad block from the peer
|
|
// default blocks already dont have commits, so should fail
|
|
block := bcr.store.LoadBlock(3)
|
|
msg := &bcBlockResponseMessage{Block: block}
|
|
peer.Send(BlockchainChannel, struct{ BlockchainMessage }{msg})
|
|
|
|
ticker := time.NewTicker(time.Millisecond * 10)
|
|
timer := time.NewTimer(time.Second * 2)
|
|
LOOP:
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
if bcr.Switch.Peers().Size() == 0 {
|
|
break LOOP
|
|
}
|
|
case <-timer.C:
|
|
t.Fatal("Timed out waiting to disconnect peer")
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
//----------------------------------------------
|
|
// utility funcs
|
|
|
|
func makeTxs(height int64) (txs []types.Tx) {
|
|
for i := 0; i < 10; i++ {
|
|
txs = append(txs, types.Tx([]byte{byte(height), byte(i)}))
|
|
}
|
|
return txs
|
|
}
|
|
|
|
func makeBlock(height int64, state sm.State) *types.Block {
|
|
block, _ := state.MakeBlock(height, makeTxs(height), new(types.Commit), nil, state.Validators.GetProposer().Address)
|
|
return block
|
|
}
|
|
|
|
// The Test peer
|
|
type bcrTestPeer struct {
|
|
cmn.BaseService
|
|
id p2p.ID
|
|
ch chan interface{}
|
|
}
|
|
|
|
var _ p2p.Peer = (*bcrTestPeer)(nil)
|
|
|
|
func newbcrTestPeer(id p2p.ID) *bcrTestPeer {
|
|
bcr := &bcrTestPeer{
|
|
id: id,
|
|
ch: make(chan interface{}, 2),
|
|
}
|
|
bcr.BaseService = *cmn.NewBaseService(nil, "bcrTestPeer", bcr)
|
|
return bcr
|
|
}
|
|
|
|
func (tp *bcrTestPeer) lastBlockchainMessage() interface{} { return <-tp.ch }
|
|
|
|
func (tp *bcrTestPeer) TrySend(chID byte, msgBytes []byte) bool {
|
|
var msg BlockchainMessage
|
|
err := cdc.UnmarshalBinaryBare(msgBytes, &msg)
|
|
if err != nil {
|
|
panic(cmn.ErrorWrap(err, "Error while trying to parse a BlockchainMessage"))
|
|
}
|
|
if _, ok := msg.(*bcStatusResponseMessage); ok {
|
|
// Discard status response messages since they skew our results
|
|
// We only want to deal with:
|
|
// + bcBlockResponseMessage
|
|
// + bcNoBlockResponseMessage
|
|
} else {
|
|
tp.ch <- msg
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (tp *bcrTestPeer) FlushStop() {}
|
|
func (tp *bcrTestPeer) Send(chID byte, msgBytes []byte) bool { return tp.TrySend(chID, msgBytes) }
|
|
func (tp *bcrTestPeer) NodeInfo() p2p.NodeInfo { return p2p.DefaultNodeInfo{} }
|
|
func (tp *bcrTestPeer) Status() p2p.ConnectionStatus { return p2p.ConnectionStatus{} }
|
|
func (tp *bcrTestPeer) ID() p2p.ID { return tp.id }
|
|
func (tp *bcrTestPeer) IsOutbound() bool { return false }
|
|
func (tp *bcrTestPeer) IsPersistent() bool { return true }
|
|
func (tp *bcrTestPeer) Get(s string) interface{} { return s }
|
|
func (tp *bcrTestPeer) Set(string, interface{}) {}
|
|
func (tp *bcrTestPeer) RemoteIP() net.IP { return []byte{127, 0, 0, 1} }
|
|
func (tp *bcrTestPeer) OriginalAddr() *p2p.NetAddress { return nil }
|