package p2ptest import ( "math/rand" "testing" "time" "github.com/gogo/protobuf/proto" "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/libs/log" "github.com/tendermint/tendermint/p2p" ) // 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[p2p.NodeID]*Node logger log.Logger memoryNetwork *p2p.MemoryNetwork } // 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(t *testing.T, opts NetworkOptions) *Network { opts.setDefaults() logger := log.TestingLogger() network := &Network{ Nodes: map[p2p.NodeID]*Node{}, logger: logger, memoryNetwork: p2p.NewMemoryNetwork(logger, opts.BufferSize), } for i := 0; i < opts.NumNodes; i++ { node := network.MakeNode(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(t *testing.T) { // Set up a list of node addresses to dial, and a peer update subscription // for each node. dialQueue := []p2p.NodeAddress{} subs := map[p2p.NodeID]*p2p.PeerUpdates{} for _, node := range n.Nodes { dialQueue = append(dialQueue, node.NodeAddress) subs[node.NodeID] = node.PeerManager.Subscribe() defer subs[node.NodeID].Close() } // 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