tendermint/p2p/switch_test.go

315 lines
8.4 KiB
Go
Raw Normal View History

2015-10-25 18:21:51 -07:00
package p2p
import (
"bytes"
2016-08-09 23:33:38 -07:00
"fmt"
2016-06-25 18:59:52 -07:00
"net"
2015-10-25 18:21:51 -07:00
"sync"
"testing"
"time"
2017-04-07 03:57:03 -07:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
crypto "github.com/tendermint/go-crypto"
wire "github.com/tendermint/go-wire"
2017-05-04 19:33:08 -07:00
cfg "github.com/tendermint/tendermint/config"
2017-05-02 00:53:32 -07:00
"github.com/tendermint/tmlibs/log"
2015-10-25 18:21:51 -07:00
)
2016-05-11 21:08:41 -07:00
var (
2017-05-04 19:33:08 -07:00
config *cfg.P2PConfig
2016-05-11 21:08:41 -07:00
)
func init() {
2017-05-04 19:33:08 -07:00
config = cfg.DefaultP2PConfig()
2017-05-01 19:05:26 -07:00
config.PexReactor = true
2016-05-11 21:08:41 -07:00
}
2015-10-25 18:21:51 -07:00
type PeerMessage struct {
PeerKey string
Bytes []byte
Counter int
}
type TestReactor struct {
BaseReactor
mtx sync.Mutex
channels []*ChannelDescriptor
2017-09-12 17:49:22 -07:00
peersAdded []Peer
peersRemoved []Peer
2015-10-25 18:21:51 -07:00
logMessages bool
msgsCounter int
msgsReceived map[byte][]PeerMessage
}
func NewTestReactor(channels []*ChannelDescriptor, logMessages bool) *TestReactor {
tr := &TestReactor{
channels: channels,
logMessages: logMessages,
msgsReceived: make(map[byte][]PeerMessage),
}
2017-05-02 00:53:32 -07:00
tr.BaseReactor = *NewBaseReactor("TestReactor", tr)
tr.SetLogger(log.TestingLogger())
2015-10-25 18:21:51 -07:00
return tr
}
func (tr *TestReactor) GetChannels() []*ChannelDescriptor {
return tr.channels
}
2017-09-12 17:49:22 -07:00
func (tr *TestReactor) AddPeer(peer Peer) {
2015-10-25 18:21:51 -07:00
tr.mtx.Lock()
defer tr.mtx.Unlock()
tr.peersAdded = append(tr.peersAdded, peer)
}
2017-09-12 17:49:22 -07:00
func (tr *TestReactor) RemovePeer(peer Peer, reason interface{}) {
2015-10-25 18:21:51 -07:00
tr.mtx.Lock()
defer tr.mtx.Unlock()
tr.peersRemoved = append(tr.peersRemoved, peer)
}
2017-09-12 17:49:22 -07:00
func (tr *TestReactor) Receive(chID byte, peer Peer, msgBytes []byte) {
2015-10-25 18:21:51 -07:00
if tr.logMessages {
tr.mtx.Lock()
defer tr.mtx.Unlock()
//fmt.Printf("Received: %X, %X\n", chID, msgBytes)
2017-09-12 17:49:22 -07:00
tr.msgsReceived[chID] = append(tr.msgsReceived[chID], PeerMessage{peer.Key(), msgBytes, tr.msgsCounter})
2015-10-25 18:21:51 -07:00
tr.msgsCounter++
}
}
2016-06-21 11:35:29 -07:00
func (tr *TestReactor) getMsgs(chID byte) []PeerMessage {
tr.mtx.Lock()
defer tr.mtx.Unlock()
return tr.msgsReceived[chID]
}
2015-10-25 18:21:51 -07:00
//-----------------------------------------------------------------------------
// convenience method for creating two switches connected to each other.
2016-06-25 18:59:52 -07:00
// XXX: note this uses net.Pipe and not a proper TCP conn
func makeSwitchPair(t testing.TB, initSwitch func(int, *Switch) *Switch) (*Switch, *Switch) {
2015-10-25 18:21:51 -07:00
// Create two switches that will be interconnected.
2017-05-01 19:05:26 -07:00
switches := MakeConnectedSwitches(config, 2, initSwitch, Connect2Switches)
2016-06-25 18:59:52 -07:00
return switches[0], switches[1]
2015-10-25 18:21:51 -07:00
}
2016-08-09 23:33:38 -07:00
func initSwitchFunc(i int, sw *Switch) *Switch {
// Make two reactors of two channels each
sw.AddReactor("foo", NewTestReactor([]*ChannelDescriptor{
&ChannelDescriptor{ID: byte(0x00), Priority: 10},
&ChannelDescriptor{ID: byte(0x01), Priority: 10},
}, true))
sw.AddReactor("bar", NewTestReactor([]*ChannelDescriptor{
&ChannelDescriptor{ID: byte(0x02), Priority: 10},
&ChannelDescriptor{ID: byte(0x03), Priority: 10},
}, true))
return sw
}
2015-10-25 18:21:51 -07:00
func TestSwitches(t *testing.T) {
2016-08-09 23:33:38 -07:00
s1, s2 := makeSwitchPair(t, initSwitchFunc)
2015-10-25 18:21:51 -07:00
defer s1.Stop()
defer s2.Stop()
if s1.Peers().Size() != 1 {
t.Errorf("Expected exactly 1 peer in s1, got %v", s1.Peers().Size())
}
if s2.Peers().Size() != 1 {
t.Errorf("Expected exactly 1 peer in s2, got %v", s2.Peers().Size())
}
2016-08-09 23:33:38 -07:00
// Lets send some messages
2015-10-25 18:21:51 -07:00
ch0Msg := "channel zero"
ch1Msg := "channel foo"
ch2Msg := "channel bar"
s1.Broadcast(byte(0x00), ch0Msg)
s1.Broadcast(byte(0x01), ch1Msg)
s1.Broadcast(byte(0x02), ch2Msg)
assertMsgReceivedWithTimeout(t, ch0Msg, byte(0x00), s2.Reactor("foo").(*TestReactor), 10*time.Millisecond, 5*time.Second)
assertMsgReceivedWithTimeout(t, ch1Msg, byte(0x01), s2.Reactor("foo").(*TestReactor), 10*time.Millisecond, 5*time.Second)
assertMsgReceivedWithTimeout(t, ch2Msg, byte(0x02), s2.Reactor("bar").(*TestReactor), 10*time.Millisecond, 5*time.Second)
}
2015-10-25 18:21:51 -07:00
func assertMsgReceivedWithTimeout(t *testing.T, msg string, channel byte, reactor *TestReactor, checkPeriod, timeout time.Duration) {
ticker := time.NewTicker(checkPeriod)
select {
case <-ticker.C:
msgs := reactor.getMsgs(channel)
if len(msgs) > 0 {
if !bytes.Equal(msgs[0].Bytes, wire.BinaryBytes(msg)) {
t.Fatalf("Unexpected message bytes. Wanted: %X, Got: %X", wire.BinaryBytes(msg), msgs[0].Bytes)
}
}
case <-time.After(timeout):
t.Fatalf("Expected to have received 1 message in channel #%v, got zero", channel)
2015-10-25 18:21:51 -07:00
}
}
2016-08-09 23:33:38 -07:00
func TestConnAddrFilter(t *testing.T) {
2017-05-01 19:05:26 -07:00
s1 := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc)
s2 := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc)
defer s1.Stop()
defer s2.Stop()
2016-08-09 23:33:38 -07:00
c1, c2 := net.Pipe()
s1.SetAddrFilter(func(addr net.Addr) error {
if addr.String() == c1.RemoteAddr().String() {
return fmt.Errorf("Error: pipe is blacklisted")
}
return nil
})
// connect to good peer
2017-04-07 03:57:03 -07:00
go func() {
2017-04-13 01:36:16 -07:00
s1.addPeerWithConnection(c1)
2017-04-07 03:57:03 -07:00
}()
go func() {
2017-04-13 01:36:16 -07:00
s2.addPeerWithConnection(c2)
2017-04-07 03:57:03 -07:00
}()
2016-08-09 23:33:38 -07:00
assertNoPeersWithTimeout(t, s1, 100*time.Millisecond, 400*time.Millisecond)
assertNoPeersWithTimeout(t, s2, 100*time.Millisecond, 400*time.Millisecond)
}
2016-08-09 23:33:38 -07:00
func assertNoPeersWithTimeout(t *testing.T, sw *Switch, checkPeriod, timeout time.Duration) {
ticker := time.NewTicker(checkPeriod)
select {
case <-ticker.C:
if sw.Peers().Size() != 0 {
t.Fatalf("Expected %v to not connect to some peers, got %d", sw, sw.Peers().Size())
}
case <-time.After(timeout):
return
2016-08-09 23:33:38 -07:00
}
}
func TestConnPubKeyFilter(t *testing.T) {
2017-05-01 19:05:26 -07:00
s1 := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc)
s2 := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc)
defer s1.Stop()
defer s2.Stop()
2016-08-09 23:33:38 -07:00
c1, c2 := net.Pipe()
// set pubkey filter
s1.SetPubKeyFilter(func(pubkey crypto.PubKeyEd25519) error {
if bytes.Equal(pubkey.Bytes(), s2.nodeInfo.PubKey.Bytes()) {
return fmt.Errorf("Error: pipe is blacklisted")
}
return nil
})
// connect to good peer
2017-04-07 03:57:03 -07:00
go func() {
2017-04-13 01:36:16 -07:00
s1.addPeerWithConnection(c1)
2017-04-07 03:57:03 -07:00
}()
go func() {
2017-04-13 01:36:16 -07:00
s2.addPeerWithConnection(c2)
2017-04-07 03:57:03 -07:00
}()
2016-08-09 23:33:38 -07:00
assertNoPeersWithTimeout(t, s1, 100*time.Millisecond, 400*time.Millisecond)
assertNoPeersWithTimeout(t, s2, 100*time.Millisecond, 400*time.Millisecond)
2016-08-09 23:33:38 -07:00
}
2017-04-07 03:57:03 -07:00
func TestSwitchStopsNonPersistentPeerOnError(t *testing.T) {
assert, require := assert.New(t), require.New(t)
2017-05-01 19:05:26 -07:00
sw := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc)
2017-04-07 03:57:03 -07:00
sw.Start()
defer sw.Stop()
2017-04-11 08:47:05 -07:00
// simulate remote peer
2017-04-14 01:43:28 -07:00
rp := &remotePeer{PrivKey: crypto.GenPrivKeyEd25519(), Config: DefaultPeerConfig()}
2017-04-11 08:47:05 -07:00
rp.Start()
defer rp.Stop()
2017-04-07 03:57:03 -07:00
2017-07-07 10:33:15 -07:00
peer, err := newOutboundPeer(rp.Addr(), sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodePrivKey, DefaultPeerConfig())
2017-04-07 03:57:03 -07:00
require.Nil(err)
2017-09-21 12:33:22 -07:00
err = sw.addPeer(peer)
2017-04-07 03:57:03 -07:00
require.Nil(err)
// simulate failure by closing connection
peer.CloseConn()
assertNoPeersWithTimeout(t, sw, 100*time.Millisecond, 100*time.Millisecond)
2017-04-07 03:57:03 -07:00
assert.False(peer.IsRunning())
}
func TestSwitchReconnectsToPersistentPeer(t *testing.T) {
2017-04-07 03:57:03 -07:00
assert, require := assert.New(t), require.New(t)
2017-05-01 19:05:26 -07:00
sw := makeSwitch(config, 1, "testing", "123.123.123", initSwitchFunc)
2017-04-07 03:57:03 -07:00
sw.Start()
defer sw.Stop()
2017-04-11 08:47:05 -07:00
// simulate remote peer
2017-04-14 01:43:28 -07:00
rp := &remotePeer{PrivKey: crypto.GenPrivKeyEd25519(), Config: DefaultPeerConfig()}
2017-04-11 08:47:05 -07:00
rp.Start()
defer rp.Stop()
2017-04-07 03:57:03 -07:00
2017-07-07 10:33:15 -07:00
peer, err := newOutboundPeer(rp.Addr(), sw.reactorsByCh, sw.chDescs, sw.StopPeerForError, sw.nodePrivKey, DefaultPeerConfig())
2017-04-10 13:04:55 -07:00
peer.makePersistent()
2017-04-07 03:57:03 -07:00
require.Nil(err)
2017-09-21 12:33:22 -07:00
err = sw.addPeer(peer)
2017-04-07 03:57:03 -07:00
require.Nil(err)
// simulate failure by closing connection
peer.CloseConn()
// TODO: actually detect the disconnection and wait for reconnect
2017-04-07 03:57:03 -07:00
time.Sleep(100 * time.Millisecond)
assert.NotZero(sw.Peers().Size())
assert.False(peer.IsRunning())
}
2015-10-25 18:21:51 -07:00
func BenchmarkSwitches(b *testing.B) {
b.StopTimer()
2016-06-25 18:59:52 -07:00
s1, s2 := makeSwitchPair(b, func(i int, sw *Switch) *Switch {
2015-10-25 18:21:51 -07:00
// Make bar reactors of bar channels each
sw.AddReactor("foo", NewTestReactor([]*ChannelDescriptor{
&ChannelDescriptor{ID: byte(0x00), Priority: 10},
&ChannelDescriptor{ID: byte(0x01), Priority: 10},
}, false))
sw.AddReactor("bar", NewTestReactor([]*ChannelDescriptor{
&ChannelDescriptor{ID: byte(0x02), Priority: 10},
&ChannelDescriptor{ID: byte(0x03), Priority: 10},
}, false))
return sw
})
defer s1.Stop()
defer s2.Stop()
// Allow time for goroutines to boot up
time.Sleep(1 * time.Second)
2015-10-25 18:21:51 -07:00
b.StartTimer()
numSuccess, numFailure := 0, 0
// Send random message from foo channel to another
for i := 0; i < b.N; i++ {
chID := byte(i % 4)
successChan := s1.Broadcast(chID, "test data")
for s := range successChan {
if s {
2017-04-07 03:57:03 -07:00
numSuccess++
2015-10-25 18:21:51 -07:00
} else {
2017-04-07 03:57:03 -07:00
numFailure++
2015-10-25 18:21:51 -07:00
}
}
}
2017-05-02 00:53:32 -07:00
b.Logf("success: %v, failure: %v", numSuccess, numFailure)
2015-10-25 18:21:51 -07:00
// Allow everything to flush before stopping switches & closing connections.
b.StopTimer()
}