package p2ptest import ( "context" "math/rand" "testing" "time" "github.com/stretchr/testify/require" dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/internal/p2p" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/types" ) // Network sets up an in-memory network that can be used for high-level P2P // testing. It creates an arbitrary number of nodes that are connected to each // other, and can open channels across all nodes with custom reactors. type Network struct { Nodes map[types.NodeID]*Node logger log.Logger memoryNetwork *p2p.MemoryNetwork cancel context.CancelFunc } // NetworkOptions is an argument structure to parameterize the // MakeNetwork function. type NetworkOptions struct { NumNodes int BufferSize int NodeOpts NodeOptions } type NodeOptions struct { MaxPeers uint16 MaxConnected uint16 } func (opts *NetworkOptions) setDefaults() { if opts.BufferSize == 0 { opts.BufferSize = 1 } } // MakeNetwork creates a test network with the given number of nodes and // connects them to each other. func MakeNetwork(ctx context.Context, t *testing.T, opts NetworkOptions) *Network { opts.setDefaults() logger := log.NewNopLogger() network := &Network{ Nodes: map[types.NodeID]*Node{}, logger: logger, memoryNetwork: p2p.NewMemoryNetwork(logger, opts.BufferSize), } for i := 0; i < opts.NumNodes; i++ { node := network.MakeNode(ctx, t, opts.NodeOpts) network.Nodes[node.NodeID] = node } return network } // Start starts the network by setting up a list of node addresses to dial in // addition to creating a peer update subscription for each node. Finally, all // nodes are connected to each other. func (n *Network) Start(ctx context.Context, t *testing.T) { ctx, n.cancel = context.WithCancel(ctx) t.Cleanup(n.cancel) // Set up a list of node addresses to dial, and a peer update subscription // for each node. dialQueue := []p2p.NodeAddress{} subs := map[types.NodeID]*p2p.PeerUpdates{} subctx, subcancel := context.WithCancel(ctx) defer subcancel() for _, node := range n.Nodes { dialQueue = append(dialQueue, node.NodeAddress) subs[node.NodeID] = node.PeerManager.Subscribe(subctx) } // For each node, dial the nodes that it still doesn't have a connection to // (either inbound or outbound), and wait for both sides to confirm the // connection via the subscriptions. for i, sourceAddress := range dialQueue { sourceNode := n.Nodes[sourceAddress.NodeID] sourceSub := subs[sourceAddress.NodeID] for _, targetAddress := range dialQueue[i+1:] { // nodes