|
@ -2,10 +2,12 @@ package pex_test |
|
|
|
|
|
|
|
|
import ( |
|
|
import ( |
|
|
"context" |
|
|
"context" |
|
|
|
|
|
"strings" |
|
|
"testing" |
|
|
"testing" |
|
|
"time" |
|
|
"time" |
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/require" |
|
|
"github.com/stretchr/testify/require" |
|
|
|
|
|
dbm "github.com/tendermint/tm-db" |
|
|
|
|
|
|
|
|
"github.com/tendermint/tendermint/libs/log" |
|
|
"github.com/tendermint/tendermint/libs/log" |
|
|
"github.com/tendermint/tendermint/p2p" |
|
|
"github.com/tendermint/tendermint/p2p" |
|
@ -28,7 +30,7 @@ const ( |
|
|
|
|
|
|
|
|
func TestReactorBasic(t *testing.T) { |
|
|
func TestReactorBasic(t *testing.T) { |
|
|
// start a network with one mock reactor and one "real" reactor
|
|
|
// start a network with one mock reactor and one "real" reactor
|
|
|
testNet := setup(t, testOptions{ |
|
|
|
|
|
|
|
|
testNet := setupNetwork(t, testOptions{ |
|
|
MockNodes: 1, |
|
|
MockNodes: 1, |
|
|
TotalNodes: 2, |
|
|
TotalNodes: 2, |
|
|
}) |
|
|
}) |
|
@ -45,7 +47,7 @@ func TestReactorBasic(t *testing.T) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func TestReactorConnectFullNetwork(t *testing.T) { |
|
|
func TestReactorConnectFullNetwork(t *testing.T) { |
|
|
testNet := setup(t, testOptions{ |
|
|
|
|
|
|
|
|
testNet := setupNetwork(t, testOptions{ |
|
|
TotalNodes: 8, |
|
|
TotalNodes: 8, |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
@ -61,29 +63,34 @@ func TestReactorConnectFullNetwork(t *testing.T) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func TestReactorSendsRequestsTooOften(t *testing.T) { |
|
|
func TestReactorSendsRequestsTooOften(t *testing.T) { |
|
|
testNet := setup(t, testOptions{ |
|
|
|
|
|
MockNodes: 1, |
|
|
|
|
|
TotalNodes: 3, |
|
|
|
|
|
}) |
|
|
|
|
|
testNet.connectAll(t) |
|
|
|
|
|
testNet.start(t) |
|
|
|
|
|
|
|
|
r := setupSingle(t) |
|
|
|
|
|
|
|
|
// firstNode sends two requests to the secondNode
|
|
|
|
|
|
testNet.sendRequest(t, firstNode, secondNode, true) |
|
|
|
|
|
testNet.sendRequest(t, firstNode, secondNode, true) |
|
|
|
|
|
|
|
|
badNode := newNodeID(t, "b") |
|
|
|
|
|
|
|
|
// assert that the secondNode evicts the first node (although they reconnect
|
|
|
|
|
|
// straight away again)
|
|
|
|
|
|
testNet.listenForPeerUpdate(t, secondNode, firstNode, p2p.PeerStatusDown, shortWait) |
|
|
|
|
|
|
|
|
r.pexInCh <- p2p.Envelope{ |
|
|
|
|
|
From: badNode, |
|
|
|
|
|
Message: &proto.PexRequestV2{}, |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// firstNode should still receive the address of the thirdNode by the secondNode
|
|
|
|
|
|
expectedAddrs := testNet.getV2AddressesFor([]int{thirdNode}) |
|
|
|
|
|
testNet.listenForResponse(t, secondNode, firstNode, shortWait, expectedAddrs) |
|
|
|
|
|
|
|
|
resp := <-r.pexOutCh |
|
|
|
|
|
msg, ok := resp.Message.(*proto.PexResponseV2) |
|
|
|
|
|
require.True(t, ok) |
|
|
|
|
|
require.Empty(t, msg.Addresses) |
|
|
|
|
|
|
|
|
|
|
|
r.pexInCh <- p2p.Envelope{ |
|
|
|
|
|
From: badNode, |
|
|
|
|
|
Message: &proto.PexRequestV2{}, |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
peerErr := <-r.pexErrCh |
|
|
|
|
|
require.Error(t, peerErr.Err) |
|
|
|
|
|
require.Empty(t, r.pexOutCh) |
|
|
|
|
|
require.Contains(t, peerErr.Err.Error(), "peer sent a request too close after a prior one") |
|
|
|
|
|
require.Equal(t, badNode, peerErr.NodeID) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func TestReactorSendsResponseWithoutRequest(t *testing.T) { |
|
|
func TestReactorSendsResponseWithoutRequest(t *testing.T) { |
|
|
testNet := setup(t, testOptions{ |
|
|
|
|
|
|
|
|
testNet := setupNetwork(t, testOptions{ |
|
|
MockNodes: 1, |
|
|
MockNodes: 1, |
|
|
TotalNodes: 3, |
|
|
TotalNodes: 3, |
|
|
}) |
|
|
}) |
|
@ -101,7 +108,7 @@ func TestReactorSendsResponseWithoutRequest(t *testing.T) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func TestReactorNeverSendsTooManyPeers(t *testing.T) { |
|
|
func TestReactorNeverSendsTooManyPeers(t *testing.T) { |
|
|
testNet := setup(t, testOptions{ |
|
|
|
|
|
|
|
|
testNet := setupNetwork(t, testOptions{ |
|
|
MockNodes: 1, |
|
|
MockNodes: 1, |
|
|
TotalNodes: 2, |
|
|
TotalNodes: 2, |
|
|
}) |
|
|
}) |
|
@ -121,7 +128,7 @@ func TestReactorNeverSendsTooManyPeers(t *testing.T) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func TestReactorErrorsOnReceivingTooManyPeers(t *testing.T) { |
|
|
func TestReactorErrorsOnReceivingTooManyPeers(t *testing.T) { |
|
|
testNet := setup(t, testOptions{ |
|
|
|
|
|
|
|
|
testNet := setupNetwork(t, testOptions{ |
|
|
MockNodes: 1, |
|
|
MockNodes: 1, |
|
|
TotalNodes: 2, |
|
|
TotalNodes: 2, |
|
|
}) |
|
|
}) |
|
@ -141,7 +148,7 @@ func TestReactorErrorsOnReceivingTooManyPeers(t *testing.T) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func TestReactorSmallPeerStoreInALargeNetwork(t *testing.T) { |
|
|
func TestReactorSmallPeerStoreInALargeNetwork(t *testing.T) { |
|
|
testNet := setup(t, testOptions{ |
|
|
|
|
|
|
|
|
testNet := setupNetwork(t, testOptions{ |
|
|
TotalNodes: 16, |
|
|
TotalNodes: 16, |
|
|
MaxPeers: 8, |
|
|
MaxPeers: 8, |
|
|
MaxConnected: 6, |
|
|
MaxConnected: 6, |
|
@ -160,7 +167,7 @@ func TestReactorSmallPeerStoreInALargeNetwork(t *testing.T) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func TestReactorLargePeerStoreInASmallNetwork(t *testing.T) { |
|
|
func TestReactorLargePeerStoreInASmallNetwork(t *testing.T) { |
|
|
testNet := setup(t, testOptions{ |
|
|
|
|
|
|
|
|
testNet := setupNetwork(t, testOptions{ |
|
|
TotalNodes: 10, |
|
|
TotalNodes: 10, |
|
|
MaxPeers: 100, |
|
|
MaxPeers: 100, |
|
|
MaxConnected: 100, |
|
|
MaxConnected: 100, |
|
@ -176,7 +183,7 @@ func TestReactorLargePeerStoreInASmallNetwork(t *testing.T) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func TestReactorWithNetworkGrowth(t *testing.T) { |
|
|
func TestReactorWithNetworkGrowth(t *testing.T) { |
|
|
testNet := setup(t, testOptions{ |
|
|
|
|
|
|
|
|
testNet := setupNetwork(t, testOptions{ |
|
|
TotalNodes: 5, |
|
|
TotalNodes: 5, |
|
|
BufferSize: 5, |
|
|
BufferSize: 5, |
|
|
}) |
|
|
}) |
|
@ -207,7 +214,7 @@ func TestReactorWithNetworkGrowth(t *testing.T) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func TestReactorIntegrationWithLegacyHandleRequest(t *testing.T) { |
|
|
func TestReactorIntegrationWithLegacyHandleRequest(t *testing.T) { |
|
|
testNet := setup(t, testOptions{ |
|
|
|
|
|
|
|
|
testNet := setupNetwork(t, testOptions{ |
|
|
MockNodes: 1, |
|
|
MockNodes: 1, |
|
|
TotalNodes: 3, |
|
|
TotalNodes: 3, |
|
|
}) |
|
|
}) |
|
@ -222,7 +229,7 @@ func TestReactorIntegrationWithLegacyHandleRequest(t *testing.T) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func TestReactorIntegrationWithLegacyHandleResponse(t *testing.T) { |
|
|
func TestReactorIntegrationWithLegacyHandleResponse(t *testing.T) { |
|
|
testNet := setup(t, testOptions{ |
|
|
|
|
|
|
|
|
testNet := setupNetwork(t, testOptions{ |
|
|
MockNodes: 1, |
|
|
MockNodes: 1, |
|
|
TotalNodes: 4, |
|
|
TotalNodes: 4, |
|
|
BufferSize: 4, |
|
|
BufferSize: 4, |
|
@ -238,6 +245,53 @@ func TestReactorIntegrationWithLegacyHandleResponse(t *testing.T) { |
|
|
testNet.requireNumberOfPeers(t, secondNode, len(testNet.nodes)-1, shortWait) |
|
|
testNet.requireNumberOfPeers(t, secondNode, len(testNet.nodes)-1, shortWait) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type singleTestReactor struct { |
|
|
|
|
|
reactor *pex.ReactorV2 |
|
|
|
|
|
pexInCh chan p2p.Envelope |
|
|
|
|
|
pexOutCh chan p2p.Envelope |
|
|
|
|
|
pexErrCh chan p2p.PeerError |
|
|
|
|
|
pexCh *p2p.Channel |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func setupSingle(t *testing.T) *singleTestReactor { |
|
|
|
|
|
t.Helper() |
|
|
|
|
|
nodeID := newNodeID(t, "a") |
|
|
|
|
|
chBuf := 2 |
|
|
|
|
|
pexInCh := make(chan p2p.Envelope, chBuf) |
|
|
|
|
|
pexOutCh := make(chan p2p.Envelope, chBuf) |
|
|
|
|
|
pexErrCh := make(chan p2p.PeerError, chBuf) |
|
|
|
|
|
pexCh := p2p.NewChannel( |
|
|
|
|
|
p2p.ChannelID(pex.PexChannel), |
|
|
|
|
|
new(proto.PexMessage), |
|
|
|
|
|
pexInCh, |
|
|
|
|
|
pexOutCh, |
|
|
|
|
|
pexErrCh, |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
peerUpdates := p2p.NewPeerUpdates(make(chan p2p.PeerUpdate), chBuf) |
|
|
|
|
|
peerManager, err := p2p.NewPeerManager(nodeID, dbm.NewMemDB(), p2p.PeerManagerOptions{}) |
|
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
|
|
reactor := pex.NewReactorV2(log.TestingLogger(), peerManager, pexCh, peerUpdates) |
|
|
|
|
|
require.NoError(t, reactor.Start()) |
|
|
|
|
|
t.Cleanup(func() { |
|
|
|
|
|
err := reactor.Stop() |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
t.Fatal(err) |
|
|
|
|
|
} |
|
|
|
|
|
pexCh.Close() |
|
|
|
|
|
peerUpdates.Close() |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
return &singleTestReactor{ |
|
|
|
|
|
reactor: reactor, |
|
|
|
|
|
pexInCh: pexInCh, |
|
|
|
|
|
pexOutCh: pexOutCh, |
|
|
|
|
|
pexErrCh: pexErrCh, |
|
|
|
|
|
pexCh: pexCh, |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
type reactorTestSuite struct { |
|
|
type reactorTestSuite struct { |
|
|
network *p2ptest.Network |
|
|
network *p2ptest.Network |
|
|
logger log.Logger |
|
|
logger log.Logger |
|
@ -264,7 +318,7 @@ type testOptions struct { |
|
|
|
|
|
|
|
|
// setup setups a test suite with a network of nodes. Mocknodes represent the
|
|
|
// setup setups a test suite with a network of nodes. Mocknodes represent the
|
|
|
// hollow nodes that the test can listen and send on
|
|
|
// hollow nodes that the test can listen and send on
|
|
|
func setup(t *testing.T, opts testOptions) *reactorTestSuite { |
|
|
|
|
|
|
|
|
func setupNetwork(t *testing.T, opts testOptions) *reactorTestSuite { |
|
|
t.Helper() |
|
|
t.Helper() |
|
|
|
|
|
|
|
|
require.Greater(t, opts.TotalNodes, opts.MockNodes) |
|
|
require.Greater(t, opts.TotalNodes, opts.MockNodes) |
|
@ -720,3 +774,9 @@ func (r *reactorTestSuite) addAddresses(t *testing.T, node int, addrs []int) { |
|
|
require.True(t, added) |
|
|
require.True(t, added) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func newNodeID(t *testing.T, id string) p2p.NodeID { |
|
|
|
|
|
nodeID, err := p2p.NewNodeID(strings.Repeat(id, 2*p2p.NodeIDByteLength)) |
|
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
return nodeID |
|
|
|
|
|
} |