mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-25 14:52:17 +00:00
* p2p/conn: FlushStop. Use in pex. Closes #2092 In seed mode, we call StopPeer immediately after Send. Since flushing msgs to the peer happens in the background, the peer connection is often closed before the messages are actually sent out. The new FlushStop method allows all msgs to first be written and flushed out on the conn before it is closed. * fix dummy peer * typo * fixes from review * more comments * ensure pex doesn't call FlushStop more than once FlushStop is not safe to call more than once, but we call it from Receive in a go-routine so Receive doesn't block. To ensure we only call it once, we use the lastReceivedRequests map - if an entry already exists, then FlushStop should already have been called and we can return.
211 lines
6.5 KiB
Go
211 lines
6.5 KiB
Go
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 }
|