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.

623 lines
17 KiB

p2p: seed mode refactoring (#3011) ListOfKnownAddresses is removed panic if addrbook size is less than zero CrawlPeers does not attempt to connect to existing or peers we're currently dialing various perf. fixes improved tests (though not complete) move IsDialingOrExistingAddress check into DialPeerWithAddress (Fixes #2716) * addrbook: preallocate memory when saving addrbook to file * addrbook: remove oldestFirst struct and check for ID * oldestFirst replaced with sort.Slice * ID is now mandatory, so no need to check * addrbook: remove ListOfKnownAddresses GetSelection is used instead in seed mode. * addrbook: panic if size is less than 0 * rewrite addrbook#saveToFile to not use a counter * test AttemptDisconnects func * move IsDialingOrExistingAddress check into DialPeerWithAddress * save and cleanup crawl peer data * get rid of DefaultSeedDisconnectWaitPeriod * make linter happy * fix TestPEXReactorSeedMode * fix comment * add a changelog entry * Apply suggestions from code review Co-Authored-By: melekes <anton.kalyaev@gmail.com> * rename ErrDialingOrExistingAddress to ErrCurrentlyDialingOrExistingAddress * lowercase errors * do not persist seed data pros: - no extra files - less IO cons: - if the node crashes, seed might crawl a peer too soon * fixes after Ethan's review * add a changelog entry * we should only consult Switch about peers checking addrbook size does not make sense since only PEX reactor uses it for dialing peers! https://github.com/tendermint/tendermint/pull/3011#discussion_r270948875
6 years ago
p2p: seed mode refactoring (#3011) ListOfKnownAddresses is removed panic if addrbook size is less than zero CrawlPeers does not attempt to connect to existing or peers we're currently dialing various perf. fixes improved tests (though not complete) move IsDialingOrExistingAddress check into DialPeerWithAddress (Fixes #2716) * addrbook: preallocate memory when saving addrbook to file * addrbook: remove oldestFirst struct and check for ID * oldestFirst replaced with sort.Slice * ID is now mandatory, so no need to check * addrbook: remove ListOfKnownAddresses GetSelection is used instead in seed mode. * addrbook: panic if size is less than 0 * rewrite addrbook#saveToFile to not use a counter * test AttemptDisconnects func * move IsDialingOrExistingAddress check into DialPeerWithAddress * save and cleanup crawl peer data * get rid of DefaultSeedDisconnectWaitPeriod * make linter happy * fix TestPEXReactorSeedMode * fix comment * add a changelog entry * Apply suggestions from code review Co-Authored-By: melekes <anton.kalyaev@gmail.com> * rename ErrDialingOrExistingAddress to ErrCurrentlyDialingOrExistingAddress * lowercase errors * do not persist seed data pros: - no extra files - less IO cons: - if the node crashes, seed might crawl a peer too soon * fixes after Ethan's review * add a changelog entry * we should only consult Switch about peers checking addrbook size does not make sense since only PEX reactor uses it for dialing peers! https://github.com/tendermint/tendermint/pull/3011#discussion_r270948875
6 years ago
p2p: seed mode refactoring (#3011) ListOfKnownAddresses is removed panic if addrbook size is less than zero CrawlPeers does not attempt to connect to existing or peers we're currently dialing various perf. fixes improved tests (though not complete) move IsDialingOrExistingAddress check into DialPeerWithAddress (Fixes #2716) * addrbook: preallocate memory when saving addrbook to file * addrbook: remove oldestFirst struct and check for ID * oldestFirst replaced with sort.Slice * ID is now mandatory, so no need to check * addrbook: remove ListOfKnownAddresses GetSelection is used instead in seed mode. * addrbook: panic if size is less than 0 * rewrite addrbook#saveToFile to not use a counter * test AttemptDisconnects func * move IsDialingOrExistingAddress check into DialPeerWithAddress * save and cleanup crawl peer data * get rid of DefaultSeedDisconnectWaitPeriod * make linter happy * fix TestPEXReactorSeedMode * fix comment * add a changelog entry * Apply suggestions from code review Co-Authored-By: melekes <anton.kalyaev@gmail.com> * rename ErrDialingOrExistingAddress to ErrCurrentlyDialingOrExistingAddress * lowercase errors * do not persist seed data pros: - no extra files - less IO cons: - if the node crashes, seed might crawl a peer too soon * fixes after Ethan's review * add a changelog entry * we should only consult Switch about peers checking addrbook size does not make sense since only PEX reactor uses it for dialing peers! https://github.com/tendermint/tendermint/pull/3011#discussion_r270948875
6 years ago
p2p: seed mode refactoring (#3011) ListOfKnownAddresses is removed panic if addrbook size is less than zero CrawlPeers does not attempt to connect to existing or peers we're currently dialing various perf. fixes improved tests (though not complete) move IsDialingOrExistingAddress check into DialPeerWithAddress (Fixes #2716) * addrbook: preallocate memory when saving addrbook to file * addrbook: remove oldestFirst struct and check for ID * oldestFirst replaced with sort.Slice * ID is now mandatory, so no need to check * addrbook: remove ListOfKnownAddresses GetSelection is used instead in seed mode. * addrbook: panic if size is less than 0 * rewrite addrbook#saveToFile to not use a counter * test AttemptDisconnects func * move IsDialingOrExistingAddress check into DialPeerWithAddress * save and cleanup crawl peer data * get rid of DefaultSeedDisconnectWaitPeriod * make linter happy * fix TestPEXReactorSeedMode * fix comment * add a changelog entry * Apply suggestions from code review Co-Authored-By: melekes <anton.kalyaev@gmail.com> * rename ErrDialingOrExistingAddress to ErrCurrentlyDialingOrExistingAddress * lowercase errors * do not persist seed data pros: - no extra files - less IO cons: - if the node crashes, seed might crawl a peer too soon * fixes after Ethan's review * add a changelog entry * we should only consult Switch about peers checking addrbook size does not make sense since only PEX reactor uses it for dialing peers! https://github.com/tendermint/tendermint/pull/3011#discussion_r270948875
6 years ago
p2p: seed mode refactoring (#3011) ListOfKnownAddresses is removed panic if addrbook size is less than zero CrawlPeers does not attempt to connect to existing or peers we're currently dialing various perf. fixes improved tests (though not complete) move IsDialingOrExistingAddress check into DialPeerWithAddress (Fixes #2716) * addrbook: preallocate memory when saving addrbook to file * addrbook: remove oldestFirst struct and check for ID * oldestFirst replaced with sort.Slice * ID is now mandatory, so no need to check * addrbook: remove ListOfKnownAddresses GetSelection is used instead in seed mode. * addrbook: panic if size is less than 0 * rewrite addrbook#saveToFile to not use a counter * test AttemptDisconnects func * move IsDialingOrExistingAddress check into DialPeerWithAddress * save and cleanup crawl peer data * get rid of DefaultSeedDisconnectWaitPeriod * make linter happy * fix TestPEXReactorSeedMode * fix comment * add a changelog entry * Apply suggestions from code review Co-Authored-By: melekes <anton.kalyaev@gmail.com> * rename ErrDialingOrExistingAddress to ErrCurrentlyDialingOrExistingAddress * lowercase errors * do not persist seed data pros: - no extra files - less IO cons: - if the node crashes, seed might crawl a peer too soon * fixes after Ethan's review * add a changelog entry * we should only consult Switch about peers checking addrbook size does not make sense since only PEX reactor uses it for dialing peers! https://github.com/tendermint/tendermint/pull/3011#discussion_r270948875
6 years ago
p2p: seed mode refactoring (#3011) ListOfKnownAddresses is removed panic if addrbook size is less than zero CrawlPeers does not attempt to connect to existing or peers we're currently dialing various perf. fixes improved tests (though not complete) move IsDialingOrExistingAddress check into DialPeerWithAddress (Fixes #2716) * addrbook: preallocate memory when saving addrbook to file * addrbook: remove oldestFirst struct and check for ID * oldestFirst replaced with sort.Slice * ID is now mandatory, so no need to check * addrbook: remove ListOfKnownAddresses GetSelection is used instead in seed mode. * addrbook: panic if size is less than 0 * rewrite addrbook#saveToFile to not use a counter * test AttemptDisconnects func * move IsDialingOrExistingAddress check into DialPeerWithAddress * save and cleanup crawl peer data * get rid of DefaultSeedDisconnectWaitPeriod * make linter happy * fix TestPEXReactorSeedMode * fix comment * add a changelog entry * Apply suggestions from code review Co-Authored-By: melekes <anton.kalyaev@gmail.com> * rename ErrDialingOrExistingAddress to ErrCurrentlyDialingOrExistingAddress * lowercase errors * do not persist seed data pros: - no extra files - less IO cons: - if the node crashes, seed might crawl a peer too soon * fixes after Ethan's review * add a changelog entry * we should only consult Switch about peers checking addrbook size does not make sense since only PEX reactor uses it for dialing peers! https://github.com/tendermint/tendermint/pull/3011#discussion_r270948875
6 years ago
p2p: seed mode refactoring (#3011) ListOfKnownAddresses is removed panic if addrbook size is less than zero CrawlPeers does not attempt to connect to existing or peers we're currently dialing various perf. fixes improved tests (though not complete) move IsDialingOrExistingAddress check into DialPeerWithAddress (Fixes #2716) * addrbook: preallocate memory when saving addrbook to file * addrbook: remove oldestFirst struct and check for ID * oldestFirst replaced with sort.Slice * ID is now mandatory, so no need to check * addrbook: remove ListOfKnownAddresses GetSelection is used instead in seed mode. * addrbook: panic if size is less than 0 * rewrite addrbook#saveToFile to not use a counter * test AttemptDisconnects func * move IsDialingOrExistingAddress check into DialPeerWithAddress * save and cleanup crawl peer data * get rid of DefaultSeedDisconnectWaitPeriod * make linter happy * fix TestPEXReactorSeedMode * fix comment * add a changelog entry * Apply suggestions from code review Co-Authored-By: melekes <anton.kalyaev@gmail.com> * rename ErrDialingOrExistingAddress to ErrCurrentlyDialingOrExistingAddress * lowercase errors * do not persist seed data pros: - no extra files - less IO cons: - if the node crashes, seed might crawl a peer too soon * fixes after Ethan's review * add a changelog entry * we should only consult Switch about peers checking addrbook size does not make sense since only PEX reactor uses it for dialing peers! https://github.com/tendermint/tendermint/pull/3011#discussion_r270948875
6 years ago
p2p: seed mode refactoring (#3011) ListOfKnownAddresses is removed panic if addrbook size is less than zero CrawlPeers does not attempt to connect to existing or peers we're currently dialing various perf. fixes improved tests (though not complete) move IsDialingOrExistingAddress check into DialPeerWithAddress (Fixes #2716) * addrbook: preallocate memory when saving addrbook to file * addrbook: remove oldestFirst struct and check for ID * oldestFirst replaced with sort.Slice * ID is now mandatory, so no need to check * addrbook: remove ListOfKnownAddresses GetSelection is used instead in seed mode. * addrbook: panic if size is less than 0 * rewrite addrbook#saveToFile to not use a counter * test AttemptDisconnects func * move IsDialingOrExistingAddress check into DialPeerWithAddress * save and cleanup crawl peer data * get rid of DefaultSeedDisconnectWaitPeriod * make linter happy * fix TestPEXReactorSeedMode * fix comment * add a changelog entry * Apply suggestions from code review Co-Authored-By: melekes <anton.kalyaev@gmail.com> * rename ErrDialingOrExistingAddress to ErrCurrentlyDialingOrExistingAddress * lowercase errors * do not persist seed data pros: - no extra files - less IO cons: - if the node crashes, seed might crawl a peer too soon * fixes after Ethan's review * add a changelog entry * we should only consult Switch about peers checking addrbook size does not make sense since only PEX reactor uses it for dialing peers! https://github.com/tendermint/tendermint/pull/3011#discussion_r270948875
6 years ago
p2p: seed mode refactoring (#3011) ListOfKnownAddresses is removed panic if addrbook size is less than zero CrawlPeers does not attempt to connect to existing or peers we're currently dialing various perf. fixes improved tests (though not complete) move IsDialingOrExistingAddress check into DialPeerWithAddress (Fixes #2716) * addrbook: preallocate memory when saving addrbook to file * addrbook: remove oldestFirst struct and check for ID * oldestFirst replaced with sort.Slice * ID is now mandatory, so no need to check * addrbook: remove ListOfKnownAddresses GetSelection is used instead in seed mode. * addrbook: panic if size is less than 0 * rewrite addrbook#saveToFile to not use a counter * test AttemptDisconnects func * move IsDialingOrExistingAddress check into DialPeerWithAddress * save and cleanup crawl peer data * get rid of DefaultSeedDisconnectWaitPeriod * make linter happy * fix TestPEXReactorSeedMode * fix comment * add a changelog entry * Apply suggestions from code review Co-Authored-By: melekes <anton.kalyaev@gmail.com> * rename ErrDialingOrExistingAddress to ErrCurrentlyDialingOrExistingAddress * lowercase errors * do not persist seed data pros: - no extra files - less IO cons: - if the node crashes, seed might crawl a peer too soon * fixes after Ethan's review * add a changelog entry * we should only consult Switch about peers checking addrbook size does not make sense since only PEX reactor uses it for dialing peers! https://github.com/tendermint/tendermint/pull/3011#discussion_r270948875
6 years ago
p2p: seed mode refactoring (#3011) ListOfKnownAddresses is removed panic if addrbook size is less than zero CrawlPeers does not attempt to connect to existing or peers we're currently dialing various perf. fixes improved tests (though not complete) move IsDialingOrExistingAddress check into DialPeerWithAddress (Fixes #2716) * addrbook: preallocate memory when saving addrbook to file * addrbook: remove oldestFirst struct and check for ID * oldestFirst replaced with sort.Slice * ID is now mandatory, so no need to check * addrbook: remove ListOfKnownAddresses GetSelection is used instead in seed mode. * addrbook: panic if size is less than 0 * rewrite addrbook#saveToFile to not use a counter * test AttemptDisconnects func * move IsDialingOrExistingAddress check into DialPeerWithAddress * save and cleanup crawl peer data * get rid of DefaultSeedDisconnectWaitPeriod * make linter happy * fix TestPEXReactorSeedMode * fix comment * add a changelog entry * Apply suggestions from code review Co-Authored-By: melekes <anton.kalyaev@gmail.com> * rename ErrDialingOrExistingAddress to ErrCurrentlyDialingOrExistingAddress * lowercase errors * do not persist seed data pros: - no extra files - less IO cons: - if the node crashes, seed might crawl a peer too soon * fixes after Ethan's review * add a changelog entry * we should only consult Switch about peers checking addrbook size does not make sense since only PEX reactor uses it for dialing peers! https://github.com/tendermint/tendermint/pull/3011#discussion_r270948875
6 years ago
p2p: seed mode refactoring (#3011) ListOfKnownAddresses is removed panic if addrbook size is less than zero CrawlPeers does not attempt to connect to existing or peers we're currently dialing various perf. fixes improved tests (though not complete) move IsDialingOrExistingAddress check into DialPeerWithAddress (Fixes #2716) * addrbook: preallocate memory when saving addrbook to file * addrbook: remove oldestFirst struct and check for ID * oldestFirst replaced with sort.Slice * ID is now mandatory, so no need to check * addrbook: remove ListOfKnownAddresses GetSelection is used instead in seed mode. * addrbook: panic if size is less than 0 * rewrite addrbook#saveToFile to not use a counter * test AttemptDisconnects func * move IsDialingOrExistingAddress check into DialPeerWithAddress * save and cleanup crawl peer data * get rid of DefaultSeedDisconnectWaitPeriod * make linter happy * fix TestPEXReactorSeedMode * fix comment * add a changelog entry * Apply suggestions from code review Co-Authored-By: melekes <anton.kalyaev@gmail.com> * rename ErrDialingOrExistingAddress to ErrCurrentlyDialingOrExistingAddress * lowercase errors * do not persist seed data pros: - no extra files - less IO cons: - if the node crashes, seed might crawl a peer too soon * fixes after Ethan's review * add a changelog entry * we should only consult Switch about peers checking addrbook size does not make sense since only PEX reactor uses it for dialing peers! https://github.com/tendermint/tendermint/pull/3011#discussion_r270948875
6 years ago
  1. package pex
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "path/filepath"
  7. "testing"
  8. "time"
  9. "github.com/stretchr/testify/assert"
  10. "github.com/stretchr/testify/require"
  11. "github.com/tendermint/tendermint/config"
  12. "github.com/tendermint/tendermint/libs/log"
  13. "github.com/tendermint/tendermint/p2p"
  14. "github.com/tendermint/tendermint/p2p/mock"
  15. )
  16. var (
  17. cfg *config.P2PConfig
  18. )
  19. func init() {
  20. cfg = config.DefaultP2PConfig()
  21. cfg.PexReactor = true
  22. cfg.AllowDuplicateIP = true
  23. }
  24. func TestPEXReactorBasic(t *testing.T) {
  25. r, book := createReactor(&PEXReactorConfig{})
  26. defer teardownReactor(book)
  27. assert.NotNil(t, r)
  28. assert.NotEmpty(t, r.GetChannels())
  29. }
  30. func TestPEXReactorAddRemovePeer(t *testing.T) {
  31. r, book := createReactor(&PEXReactorConfig{})
  32. defer teardownReactor(book)
  33. size := book.Size()
  34. peer := p2p.CreateRandomPeer(false)
  35. r.AddPeer(peer)
  36. assert.Equal(t, size+1, book.Size())
  37. r.RemovePeer(peer, "peer not available")
  38. outboundPeer := p2p.CreateRandomPeer(true)
  39. r.AddPeer(outboundPeer)
  40. assert.Equal(t, size+1, book.Size(), "outbound peers should not be added to the address book")
  41. r.RemovePeer(outboundPeer, "peer not available")
  42. }
  43. // --- FAIL: TestPEXReactorRunning (11.10s)
  44. // pex_reactor_test.go:411: expected all switches to be connected to at
  45. // least one peer (switches: 0 => {outbound: 1, inbound: 0}, 1 =>
  46. // {outbound: 0, inbound: 1}, 2 => {outbound: 0, inbound: 0}, )
  47. //
  48. // EXPLANATION: peers are getting rejected because in switch#addPeer we check
  49. // if any peer (who we already connected to) has the same IP. Even though local
  50. // peers have different IP addresses, they all have the same underlying remote
  51. // IP: 127.0.0.1.
  52. //
  53. func TestPEXReactorRunning(t *testing.T) {
  54. N := 3
  55. switches := make([]*p2p.Switch, N)
  56. // directory to store address books
  57. dir, err := ioutil.TempDir("", "pex_reactor")
  58. require.Nil(t, err)
  59. defer os.RemoveAll(dir) // nolint: errcheck
  60. books := make([]*addrBook, N)
  61. logger := log.TestingLogger()
  62. // create switches
  63. for i := 0; i < N; i++ {
  64. switches[i] = p2p.MakeSwitch(cfg, i, "testing", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch {
  65. books[i] = NewAddrBook(filepath.Join(dir, fmt.Sprintf("addrbook%d.json", i)), false)
  66. books[i].SetLogger(logger.With("pex", i))
  67. sw.SetAddrBook(books[i])
  68. sw.SetLogger(logger.With("pex", i))
  69. r := NewPEXReactor(books[i], &PEXReactorConfig{})
  70. r.SetLogger(logger.With("pex", i))
  71. r.SetEnsurePeersPeriod(250 * time.Millisecond)
  72. sw.AddReactor("pex", r)
  73. return sw
  74. })
  75. }
  76. addOtherNodeAddrToAddrBook := func(switchIndex, otherSwitchIndex int) {
  77. addr := switches[otherSwitchIndex].NetAddress()
  78. books[switchIndex].AddAddress(addr, addr)
  79. }
  80. addOtherNodeAddrToAddrBook(0, 1)
  81. addOtherNodeAddrToAddrBook(1, 0)
  82. addOtherNodeAddrToAddrBook(2, 1)
  83. for _, sw := range switches {
  84. err := sw.Start() // start switch and reactors
  85. require.Nil(t, err)
  86. }
  87. assertPeersWithTimeout(t, switches, 10*time.Millisecond, 10*time.Second, N-1)
  88. // stop them
  89. for _, s := range switches {
  90. s.Stop()
  91. }
  92. }
  93. func TestPEXReactorReceive(t *testing.T) {
  94. r, book := createReactor(&PEXReactorConfig{})
  95. defer teardownReactor(book)
  96. peer := p2p.CreateRandomPeer(false)
  97. // we have to send a request to receive responses
  98. r.RequestAddrs(peer)
  99. size := book.Size()
  100. addrs := []*p2p.NetAddress{peer.SocketAddr()}
  101. msg := cdc.MustMarshalBinaryBare(&pexAddrsMessage{Addrs: addrs})
  102. r.Receive(PexChannel, peer, msg)
  103. assert.Equal(t, size+1, book.Size())
  104. msg = cdc.MustMarshalBinaryBare(&pexRequestMessage{})
  105. r.Receive(PexChannel, peer, msg) // should not panic.
  106. }
  107. func TestPEXReactorRequestMessageAbuse(t *testing.T) {
  108. r, book := createReactor(&PEXReactorConfig{})
  109. defer teardownReactor(book)
  110. sw := createSwitchAndAddReactors(r)
  111. sw.SetAddrBook(book)
  112. peer := mock.NewPeer(nil)
  113. p2p.AddPeerToSwitch(sw, peer)
  114. assert.True(t, sw.Peers().Has(peer.ID()))
  115. id := string(peer.ID())
  116. msg := cdc.MustMarshalBinaryBare(&pexRequestMessage{})
  117. // first time creates the entry
  118. r.Receive(PexChannel, peer, msg)
  119. assert.True(t, r.lastReceivedRequests.Has(id))
  120. assert.True(t, sw.Peers().Has(peer.ID()))
  121. // next time sets the last time value
  122. r.Receive(PexChannel, peer, msg)
  123. assert.True(t, r.lastReceivedRequests.Has(id))
  124. assert.True(t, sw.Peers().Has(peer.ID()))
  125. // third time is too many too soon - peer is removed
  126. r.Receive(PexChannel, peer, msg)
  127. assert.False(t, r.lastReceivedRequests.Has(id))
  128. assert.False(t, sw.Peers().Has(peer.ID()))
  129. }
  130. func TestPEXReactorAddrsMessageAbuse(t *testing.T) {
  131. r, book := createReactor(&PEXReactorConfig{})
  132. defer teardownReactor(book)
  133. sw := createSwitchAndAddReactors(r)
  134. sw.SetAddrBook(book)
  135. peer := mock.NewPeer(nil)
  136. p2p.AddPeerToSwitch(sw, peer)
  137. assert.True(t, sw.Peers().Has(peer.ID()))
  138. id := string(peer.ID())
  139. // request addrs from the peer
  140. r.RequestAddrs(peer)
  141. assert.True(t, r.requestsSent.Has(id))
  142. assert.True(t, sw.Peers().Has(peer.ID()))
  143. addrs := []*p2p.NetAddress{peer.SocketAddr()}
  144. msg := cdc.MustMarshalBinaryBare(&pexAddrsMessage{Addrs: addrs})
  145. // receive some addrs. should clear the request
  146. r.Receive(PexChannel, peer, msg)
  147. assert.False(t, r.requestsSent.Has(id))
  148. assert.True(t, sw.Peers().Has(peer.ID()))
  149. // receiving more addrs causes a disconnect
  150. r.Receive(PexChannel, peer, msg)
  151. assert.False(t, sw.Peers().Has(peer.ID()))
  152. }
  153. func TestCheckSeeds(t *testing.T) {
  154. // directory to store address books
  155. dir, err := ioutil.TempDir("", "pex_reactor")
  156. require.Nil(t, err)
  157. defer os.RemoveAll(dir) // nolint: errcheck
  158. // 1. test creating peer with no seeds works
  159. peerSwitch := testCreateDefaultPeer(dir, 0)
  160. require.Nil(t, peerSwitch.Start())
  161. peerSwitch.Stop()
  162. // 2. create seed
  163. seed := testCreateSeed(dir, 1, []*p2p.NetAddress{}, []*p2p.NetAddress{})
  164. // 3. test create peer with online seed works
  165. peerSwitch = testCreatePeerWithSeed(dir, 2, seed)
  166. require.Nil(t, peerSwitch.Start())
  167. peerSwitch.Stop()
  168. // 4. test create peer with all seeds having unresolvable DNS fails
  169. badPeerConfig := &PEXReactorConfig{
  170. Seeds: []string{"ed3dfd27bfc4af18f67a49862f04cc100696e84d@bad.network.addr:26657",
  171. "d824b13cb5d40fa1d8a614e089357c7eff31b670@anotherbad.network.addr:26657"},
  172. }
  173. peerSwitch = testCreatePeerWithConfig(dir, 2, badPeerConfig)
  174. require.Error(t, peerSwitch.Start())
  175. peerSwitch.Stop()
  176. // 5. test create peer with one good seed address succeeds
  177. badPeerConfig = &PEXReactorConfig{
  178. Seeds: []string{"ed3dfd27bfc4af18f67a49862f04cc100696e84d@bad.network.addr:26657",
  179. "d824b13cb5d40fa1d8a614e089357c7eff31b670@anotherbad.network.addr:26657",
  180. seed.NetAddress().String()},
  181. }
  182. peerSwitch = testCreatePeerWithConfig(dir, 2, badPeerConfig)
  183. require.Nil(t, peerSwitch.Start())
  184. peerSwitch.Stop()
  185. }
  186. func TestPEXReactorUsesSeedsIfNeeded(t *testing.T) {
  187. // directory to store address books
  188. dir, err := ioutil.TempDir("", "pex_reactor")
  189. require.Nil(t, err)
  190. defer os.RemoveAll(dir) // nolint: errcheck
  191. // 1. create seed
  192. seed := testCreateSeed(dir, 0, []*p2p.NetAddress{}, []*p2p.NetAddress{})
  193. require.Nil(t, seed.Start())
  194. defer seed.Stop()
  195. // 2. create usual peer with only seed configured.
  196. peer := testCreatePeerWithSeed(dir, 1, seed)
  197. require.Nil(t, peer.Start())
  198. defer peer.Stop()
  199. // 3. check that the peer connects to seed immediately
  200. assertPeersWithTimeout(t, []*p2p.Switch{peer}, 10*time.Millisecond, 3*time.Second, 1)
  201. }
  202. func TestConnectionSpeedForPeerReceivedFromSeed(t *testing.T) {
  203. // directory to store address books
  204. dir, err := ioutil.TempDir("", "pex_reactor")
  205. require.Nil(t, err)
  206. defer os.RemoveAll(dir) // nolint: errcheck
  207. // 1. create peer
  208. peerSwitch := testCreateDefaultPeer(dir, 1)
  209. require.Nil(t, peerSwitch.Start())
  210. defer peerSwitch.Stop()
  211. // 2. Create seed which knows about the peer
  212. peerAddr := peerSwitch.NetAddress()
  213. seed := testCreateSeed(dir, 2, []*p2p.NetAddress{peerAddr}, []*p2p.NetAddress{peerAddr})
  214. require.Nil(t, seed.Start())
  215. defer seed.Stop()
  216. // 3. create another peer with only seed configured.
  217. secondPeer := testCreatePeerWithSeed(dir, 3, seed)
  218. require.Nil(t, secondPeer.Start())
  219. defer secondPeer.Stop()
  220. // 4. check that the second peer connects to seed immediately
  221. assertPeersWithTimeout(t, []*p2p.Switch{secondPeer}, 10*time.Millisecond, 3*time.Second, 1)
  222. // 5. check that the second peer connects to the first peer immediately
  223. assertPeersWithTimeout(t, []*p2p.Switch{secondPeer}, 10*time.Millisecond, 1*time.Second, 2)
  224. }
  225. func TestPEXReactorSeedMode(t *testing.T) {
  226. // directory to store address books
  227. dir, err := ioutil.TempDir("", "pex_reactor")
  228. require.Nil(t, err)
  229. defer os.RemoveAll(dir) // nolint: errcheck
  230. pexR, book := createReactor(&PEXReactorConfig{SeedMode: true, SeedDisconnectWaitPeriod: 10 * time.Millisecond})
  231. defer teardownReactor(book)
  232. sw := createSwitchAndAddReactors(pexR)
  233. sw.SetAddrBook(book)
  234. err = sw.Start()
  235. require.NoError(t, err)
  236. defer sw.Stop()
  237. assert.Zero(t, sw.Peers().Size())
  238. peerSwitch := testCreateDefaultPeer(dir, 1)
  239. require.NoError(t, peerSwitch.Start())
  240. defer peerSwitch.Stop()
  241. // 1. Test crawlPeers dials the peer
  242. pexR.crawlPeers([]*p2p.NetAddress{peerSwitch.NetAddress()})
  243. assert.Equal(t, 1, sw.Peers().Size())
  244. assert.True(t, sw.Peers().Has(peerSwitch.NodeInfo().ID()))
  245. // 2. attemptDisconnects should not disconnect because of wait period
  246. pexR.attemptDisconnects()
  247. assert.Equal(t, 1, sw.Peers().Size())
  248. time.Sleep(100 * time.Millisecond)
  249. // 3. attemptDisconnects should disconnect after wait period
  250. pexR.attemptDisconnects()
  251. assert.Equal(t, 0, sw.Peers().Size())
  252. }
  253. func TestPEXReactorDialsPeerUpToMaxAttemptsInSeedMode(t *testing.T) {
  254. // directory to store address books
  255. dir, err := ioutil.TempDir("", "pex_reactor")
  256. require.Nil(t, err)
  257. defer os.RemoveAll(dir) // nolint: errcheck
  258. pexR, book := createReactor(&PEXReactorConfig{SeedMode: true})
  259. defer teardownReactor(book)
  260. sw := createSwitchAndAddReactors(pexR)
  261. sw.SetAddrBook(book)
  262. err = sw.Start()
  263. require.NoError(t, err)
  264. defer sw.Stop()
  265. peer := mock.NewPeer(nil)
  266. addr := peer.SocketAddr()
  267. err = book.AddAddress(addr, addr)
  268. require.NoError(t, err)
  269. assert.True(t, book.HasAddress(addr))
  270. // imitate maxAttemptsToDial reached
  271. pexR.attemptsToDial.Store(addr.DialString(), _attemptsToDial{maxAttemptsToDial + 1, time.Now()})
  272. pexR.crawlPeers([]*p2p.NetAddress{addr})
  273. assert.False(t, book.HasAddress(addr))
  274. }
  275. // connect a peer to a seed, wait a bit, then stop it.
  276. // this should give it time to request addrs and for the seed
  277. // to call FlushStop, and allows us to test calling Stop concurrently
  278. // with FlushStop. Before a fix, this non-deterministically reproduced
  279. // https://github.com/tendermint/tendermint/issues/3231.
  280. func TestPEXReactorSeedModeFlushStop(t *testing.T) {
  281. N := 2
  282. switches := make([]*p2p.Switch, N)
  283. // directory to store address books
  284. dir, err := ioutil.TempDir("", "pex_reactor")
  285. require.Nil(t, err)
  286. defer os.RemoveAll(dir) // nolint: errcheck
  287. books := make([]*addrBook, N)
  288. logger := log.TestingLogger()
  289. // create switches
  290. for i := 0; i < N; i++ {
  291. switches[i] = p2p.MakeSwitch(cfg, i, "testing", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch {
  292. books[i] = NewAddrBook(filepath.Join(dir, fmt.Sprintf("addrbook%d.json", i)), false)
  293. books[i].SetLogger(logger.With("pex", i))
  294. sw.SetAddrBook(books[i])
  295. sw.SetLogger(logger.With("pex", i))
  296. config := &PEXReactorConfig{}
  297. if i == 0 {
  298. // first one is a seed node
  299. config = &PEXReactorConfig{SeedMode: true}
  300. }
  301. r := NewPEXReactor(books[i], config)
  302. r.SetLogger(logger.With("pex", i))
  303. r.SetEnsurePeersPeriod(250 * time.Millisecond)
  304. sw.AddReactor("pex", r)
  305. return sw
  306. })
  307. }
  308. for _, sw := range switches {
  309. err := sw.Start() // start switch and reactors
  310. require.Nil(t, err)
  311. }
  312. reactor := switches[0].Reactors()["pex"].(*PEXReactor)
  313. peerID := switches[1].NodeInfo().ID()
  314. err = switches[1].DialPeerWithAddress(switches[0].NetAddress(), false)
  315. assert.NoError(t, err)
  316. // sleep up to a second while waiting for the peer to send us a message.
  317. // this isn't perfect since it's possible the peer sends us a msg and we FlushStop
  318. // before this loop catches it. but non-deterministically it works pretty well.
  319. for i := 0; i < 1000; i++ {
  320. v := reactor.lastReceivedRequests.Get(string(peerID))
  321. if v != nil {
  322. break
  323. }
  324. time.Sleep(time.Millisecond)
  325. }
  326. // by now the FlushStop should have happened. Try stopping the peer.
  327. // it should be safe to do this.
  328. peers := switches[0].Peers().List()
  329. for _, peer := range peers {
  330. peer.Stop()
  331. }
  332. // stop the switches
  333. for _, s := range switches {
  334. s.Stop()
  335. }
  336. }
  337. func TestPEXReactorDoesNotAddPrivatePeersToAddrBook(t *testing.T) {
  338. peer := p2p.CreateRandomPeer(false)
  339. pexR, book := createReactor(&PEXReactorConfig{})
  340. book.AddPrivateIDs([]string{string(peer.NodeInfo().ID())})
  341. defer teardownReactor(book)
  342. // we have to send a request to receive responses
  343. pexR.RequestAddrs(peer)
  344. size := book.Size()
  345. addrs := []*p2p.NetAddress{peer.SocketAddr()}
  346. msg := cdc.MustMarshalBinaryBare(&pexAddrsMessage{Addrs: addrs})
  347. pexR.Receive(PexChannel, peer, msg)
  348. assert.Equal(t, size, book.Size())
  349. pexR.AddPeer(peer)
  350. assert.Equal(t, size, book.Size())
  351. }
  352. func TestPEXReactorDialPeer(t *testing.T) {
  353. pexR, book := createReactor(&PEXReactorConfig{})
  354. defer teardownReactor(book)
  355. sw := createSwitchAndAddReactors(pexR)
  356. sw.SetAddrBook(book)
  357. peer := mock.NewPeer(nil)
  358. addr := peer.SocketAddr()
  359. assert.Equal(t, 0, pexR.AttemptsToDial(addr))
  360. // 1st unsuccessful attempt
  361. pexR.dialPeer(addr)
  362. assert.Equal(t, 1, pexR.AttemptsToDial(addr))
  363. // 2nd unsuccessful attempt
  364. pexR.dialPeer(addr)
  365. // must be skipped because it is too early
  366. assert.Equal(t, 1, pexR.AttemptsToDial(addr))
  367. if !testing.Short() {
  368. time.Sleep(3 * time.Second)
  369. // 3rd attempt
  370. pexR.dialPeer(addr)
  371. assert.Equal(t, 2, pexR.AttemptsToDial(addr))
  372. }
  373. }
  374. func assertPeersWithTimeout(
  375. t *testing.T,
  376. switches []*p2p.Switch,
  377. checkPeriod, timeout time.Duration,
  378. nPeers int,
  379. ) {
  380. var (
  381. ticker = time.NewTicker(checkPeriod)
  382. remaining = timeout
  383. )
  384. for {
  385. select {
  386. case <-ticker.C:
  387. // check peers are connected
  388. allGood := true
  389. for _, s := range switches {
  390. outbound, inbound, _ := s.NumPeers()
  391. if outbound+inbound < nPeers {
  392. allGood = false
  393. break
  394. }
  395. }
  396. remaining -= checkPeriod
  397. if remaining < 0 {
  398. remaining = 0
  399. }
  400. if allGood {
  401. return
  402. }
  403. case <-time.After(remaining):
  404. numPeersStr := ""
  405. for i, s := range switches {
  406. outbound, inbound, _ := s.NumPeers()
  407. numPeersStr += fmt.Sprintf("%d => {outbound: %d, inbound: %d}, ", i, outbound, inbound)
  408. }
  409. t.Errorf(
  410. "expected all switches to be connected to at least %d peer(s) (switches: %s)",
  411. nPeers, numPeersStr,
  412. )
  413. return
  414. }
  415. }
  416. }
  417. // Creates a peer with the provided config
  418. func testCreatePeerWithConfig(dir string, id int, config *PEXReactorConfig) *p2p.Switch {
  419. peer := p2p.MakeSwitch(
  420. cfg,
  421. id,
  422. "127.0.0.1",
  423. "123.123.123",
  424. func(i int, sw *p2p.Switch) *p2p.Switch {
  425. book := NewAddrBook(filepath.Join(dir, fmt.Sprintf("addrbook%d.json", id)), false)
  426. book.SetLogger(log.TestingLogger())
  427. sw.SetAddrBook(book)
  428. sw.SetLogger(log.TestingLogger())
  429. r := NewPEXReactor(
  430. book,
  431. config,
  432. )
  433. r.SetLogger(log.TestingLogger())
  434. sw.AddReactor("pex", r)
  435. return sw
  436. },
  437. )
  438. return peer
  439. }
  440. // Creates a peer with the default config
  441. func testCreateDefaultPeer(dir string, id int) *p2p.Switch {
  442. return testCreatePeerWithConfig(dir, id, &PEXReactorConfig{})
  443. }
  444. // Creates a seed which knows about the provided addresses / source address pairs.
  445. // Starting and stopping the seed is left to the caller
  446. func testCreateSeed(dir string, id int, knownAddrs, srcAddrs []*p2p.NetAddress) *p2p.Switch {
  447. seed := p2p.MakeSwitch(
  448. cfg,
  449. id,
  450. "127.0.0.1",
  451. "123.123.123",
  452. func(i int, sw *p2p.Switch) *p2p.Switch {
  453. book := NewAddrBook(filepath.Join(dir, "addrbookSeed.json"), false)
  454. book.SetLogger(log.TestingLogger())
  455. for j := 0; j < len(knownAddrs); j++ {
  456. book.AddAddress(knownAddrs[j], srcAddrs[j])
  457. book.MarkGood(knownAddrs[j].ID)
  458. }
  459. sw.SetAddrBook(book)
  460. sw.SetLogger(log.TestingLogger())
  461. r := NewPEXReactor(book, &PEXReactorConfig{})
  462. r.SetLogger(log.TestingLogger())
  463. sw.AddReactor("pex", r)
  464. return sw
  465. },
  466. )
  467. return seed
  468. }
  469. // Creates a peer which knows about the provided seed.
  470. // Starting and stopping the peer is left to the caller
  471. func testCreatePeerWithSeed(dir string, id int, seed *p2p.Switch) *p2p.Switch {
  472. conf := &PEXReactorConfig{
  473. Seeds: []string{seed.NetAddress().String()},
  474. }
  475. return testCreatePeerWithConfig(dir, id, conf)
  476. }
  477. func createReactor(conf *PEXReactorConfig) (r *PEXReactor, book *addrBook) {
  478. // directory to store address book
  479. dir, err := ioutil.TempDir("", "pex_reactor")
  480. if err != nil {
  481. panic(err)
  482. }
  483. book = NewAddrBook(filepath.Join(dir, "addrbook.json"), true)
  484. book.SetLogger(log.TestingLogger())
  485. r = NewPEXReactor(book, conf)
  486. r.SetLogger(log.TestingLogger())
  487. return
  488. }
  489. func teardownReactor(book *addrBook) {
  490. err := os.RemoveAll(filepath.Dir(book.FilePath()))
  491. if err != nil {
  492. panic(err)
  493. }
  494. }
  495. func createSwitchAndAddReactors(reactors ...p2p.Reactor) *p2p.Switch {
  496. sw := p2p.MakeSwitch(cfg, 0, "127.0.0.1", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch { return sw })
  497. sw.SetLogger(log.TestingLogger())
  498. for _, r := range reactors {
  499. sw.AddReactor(r.String(), r)
  500. r.SetSwitch(sw)
  501. }
  502. return sw
  503. }