You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

240 lines
6.8 KiB

  1. package p2ptest
  2. import (
  3. "math/rand"
  4. "testing"
  5. "time"
  6. "github.com/gogo/protobuf/proto"
  7. "github.com/stretchr/testify/require"
  8. dbm "github.com/tendermint/tm-db"
  9. "github.com/tendermint/tendermint/crypto"
  10. "github.com/tendermint/tendermint/crypto/ed25519"
  11. "github.com/tendermint/tendermint/libs/log"
  12. "github.com/tendermint/tendermint/p2p"
  13. )
  14. // Network sets up an in-memory network that can be used for high-level P2P
  15. // testing. It creates an arbitrary number of nodes that are connected to each
  16. // other, and can open channels across all nodes with custom reactors.
  17. type Network struct {
  18. Nodes map[p2p.NodeID]*Node
  19. logger log.Logger
  20. memoryNetwork *p2p.MemoryNetwork
  21. }
  22. // MakeNetwork creates a test network with the given number of nodes and
  23. // connects them to each other.
  24. func MakeNetwork(t *testing.T, nodes int) *Network {
  25. logger := log.TestingLogger()
  26. network := &Network{
  27. Nodes: map[p2p.NodeID]*Node{},
  28. logger: logger,
  29. memoryNetwork: p2p.NewMemoryNetwork(logger),
  30. }
  31. for i := 0; i < nodes; i++ {
  32. node := MakeNode(t, network)
  33. network.Nodes[node.NodeID] = node
  34. }
  35. // Set up a list of node addresses to dial, and a peer update subscription
  36. // for each node.
  37. dialQueue := []p2p.NodeAddress{}
  38. subs := map[p2p.NodeID]*p2p.PeerUpdates{}
  39. for _, node := range network.Nodes {
  40. dialQueue = append(dialQueue, node.NodeAddress)
  41. subs[node.NodeID] = node.PeerManager.Subscribe()
  42. defer subs[node.NodeID].Close()
  43. }
  44. // For each node, dial the nodes that it still doesn't have a connection to
  45. // (either inbound or outbound), and wait for both sides to confirm the
  46. // connection via the subscriptions.
  47. for i, sourceAddress := range dialQueue {
  48. sourceNode := network.Nodes[sourceAddress.NodeID]
  49. sourceSub := subs[sourceAddress.NodeID]
  50. for _, targetAddress := range dialQueue[i+1:] { // nodes <i already connected
  51. targetNode := network.Nodes[targetAddress.NodeID]
  52. targetSub := subs[targetAddress.NodeID]
  53. require.NoError(t, sourceNode.PeerManager.Add(targetAddress))
  54. select {
  55. case peerUpdate := <-sourceSub.Updates():
  56. require.Equal(t, p2p.PeerUpdate{
  57. NodeID: targetNode.NodeID,
  58. Status: p2p.PeerStatusUp,
  59. }, peerUpdate)
  60. case <-time.After(time.Second):
  61. require.Fail(t, "timed out waiting for peer", "%v dialing %v",
  62. sourceNode.NodeID, targetNode.NodeID)
  63. }
  64. select {
  65. case peerUpdate := <-targetSub.Updates():
  66. require.Equal(t, p2p.PeerUpdate{
  67. NodeID: sourceNode.NodeID,
  68. Status: p2p.PeerStatusUp,
  69. }, peerUpdate)
  70. case <-time.After(time.Second):
  71. require.Fail(t, "timed out waiting for peer", "%v accepting %v",
  72. targetNode.NodeID, sourceNode.NodeID)
  73. }
  74. // Add the address to the target as well, so it's able to dial the
  75. // source back if that's even necessary.
  76. require.NoError(t, targetNode.PeerManager.Add(sourceAddress))
  77. }
  78. }
  79. return network
  80. }
  81. // NodeIDs returns the network's node IDs.
  82. func (n *Network) NodeIDs() []p2p.NodeID {
  83. ids := []p2p.NodeID{}
  84. for id := range n.Nodes {
  85. ids = append(ids, id)
  86. }
  87. return ids
  88. }
  89. // MakeChannels makes a channel on all nodes and returns them, automatically
  90. // doing error checks and cleanups.
  91. func (n *Network) MakeChannels(
  92. t *testing.T,
  93. chID p2p.ChannelID,
  94. messageType proto.Message,
  95. ) map[p2p.NodeID]*p2p.Channel {
  96. channels := map[p2p.NodeID]*p2p.Channel{}
  97. for _, node := range n.Nodes {
  98. channels[node.NodeID] = node.MakeChannel(t, chID, messageType)
  99. }
  100. return channels
  101. }
  102. // RandomNode returns a random node.
  103. func (n *Network) RandomNode() *Node {
  104. nodes := make([]*Node, 0, len(n.Nodes))
  105. for _, node := range n.Nodes {
  106. nodes = append(nodes, node)
  107. }
  108. return nodes[rand.Intn(len(nodes))] // nolint:gosec
  109. }
  110. // Peers returns a node's peers (i.e. everyone except itself).
  111. func (n *Network) Peers(id p2p.NodeID) []*Node {
  112. peers := make([]*Node, 0, len(n.Nodes)-1)
  113. for _, peer := range n.Nodes {
  114. if peer.NodeID != id {
  115. peers = append(peers, peer)
  116. }
  117. }
  118. return peers
  119. }
  120. // Remove removes a node from the network, stopping it and waiting for all other
  121. // nodes to pick up the disconnection.
  122. func (n *Network) Remove(t *testing.T, id p2p.NodeID) {
  123. require.Contains(t, n.Nodes, id)
  124. node := n.Nodes[id]
  125. delete(n.Nodes, id)
  126. subs := []*p2p.PeerUpdates{}
  127. for _, peer := range n.Nodes {
  128. sub := peer.PeerManager.Subscribe()
  129. defer sub.Close()
  130. subs = append(subs, sub)
  131. }
  132. require.NoError(t, node.Transport.Close())
  133. if node.Router.IsRunning() {
  134. require.NoError(t, node.Router.Stop())
  135. }
  136. node.PeerManager.Close()
  137. for _, sub := range subs {
  138. RequireUpdate(t, sub, p2p.PeerUpdate{
  139. NodeID: node.NodeID,
  140. Status: p2p.PeerStatusDown,
  141. })
  142. }
  143. }
  144. // Node is a node in a Network, with a Router and a PeerManager.
  145. type Node struct {
  146. NodeID p2p.NodeID
  147. NodeInfo p2p.NodeInfo
  148. NodeAddress p2p.NodeAddress
  149. PrivKey crypto.PrivKey
  150. Router *p2p.Router
  151. PeerManager *p2p.PeerManager
  152. Transport *p2p.MemoryTransport
  153. }
  154. // MakeNode creates a new Node.
  155. func MakeNode(t *testing.T, network *Network) *Node {
  156. privKey := ed25519.GenPrivKey()
  157. nodeID := p2p.NodeIDFromPubKey(privKey.PubKey())
  158. nodeInfo := p2p.NodeInfo{
  159. NodeID: nodeID,
  160. ListenAddr: "0.0.0.0:0", // FIXME: We have to fake this for now.
  161. Moniker: string(nodeID),
  162. }
  163. transport := network.memoryNetwork.CreateTransport(nodeID)
  164. require.Len(t, transport.Endpoints(), 1, "transport not listening on 1 endpoint")
  165. peerManager, err := p2p.NewPeerManager(nodeID, dbm.NewMemDB(), p2p.PeerManagerOptions{
  166. MinRetryTime: 10 * time.Millisecond,
  167. })
  168. require.NoError(t, err)
  169. router, err := p2p.NewRouter(network.logger, nodeInfo, privKey, peerManager,
  170. []p2p.Transport{transport}, p2p.RouterOptions{})
  171. require.NoError(t, err)
  172. require.NoError(t, router.Start())
  173. t.Cleanup(func() {
  174. if router.IsRunning() {
  175. require.NoError(t, router.Stop())
  176. }
  177. peerManager.Close()
  178. require.NoError(t, transport.Close())
  179. })
  180. return &Node{
  181. NodeID: nodeID,
  182. NodeInfo: nodeInfo,
  183. NodeAddress: transport.Endpoints()[0].NodeAddress(nodeID),
  184. PrivKey: privKey,
  185. Router: router,
  186. PeerManager: peerManager,
  187. Transport: transport,
  188. }
  189. }
  190. // MakeChannel opens a channel, with automatic error handling and cleanup. On
  191. // test cleanup, it also checks that the channel is empty, to make sure
  192. // all expected messages have been asserted.
  193. func (n *Node) MakeChannel(t *testing.T, chID p2p.ChannelID, messageType proto.Message) *p2p.Channel {
  194. channel, err := n.Router.OpenChannel(chID, messageType)
  195. require.NoError(t, err)
  196. t.Cleanup(func() {
  197. RequireEmpty(t, channel)
  198. channel.Close()
  199. })
  200. return channel
  201. }
  202. // MakePeerUpdates opens a peer update subscription, with automatic cleanup.
  203. // It checks that all updates have been consumed during cleanup.
  204. func (n *Node) MakePeerUpdates(t *testing.T) *p2p.PeerUpdates {
  205. sub := n.PeerManager.Subscribe()
  206. t.Cleanup(func() {
  207. RequireNoUpdates(t, sub)
  208. sub.Close()
  209. })
  210. return sub
  211. }