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.

398 lines
9.9 KiB

8 years ago
8 years ago
8 years ago
p2p: introduce peerConn to simplify peer creation (#1226) * expose AuthEnc in the P2P config if AuthEnc is true, dialed peers must have a node ID in the address and it must match the persistent pubkey from the secret handshake. Refs #1157 * fixes after my own review * fix docs * fix build failure ``` p2p/pex/pex_reactor_test.go:288:88: cannot use seed.NodeInfo().NetAddress() (type *p2p.NetAddress) as type string in array or slice literal ``` * p2p: introduce peerConn to simplify peer creation * Introduce `peerConn` containing the known fields of `peer` * `peer` only created in `sw.addPeer` once handshake is complete and NodeInfo is checked * Eliminates some mutable variables and makes the code flow better * Simplifies the `newXxxPeer` funcs * Use ID instead of PubKey where possible. * SetPubKeyFilter -> SetIDFilter * nodeInfo.Validate takes ID * remove peer.PubKey() * persistent node ids * fixes from review * test: use ip_plus_id.sh more * fix invalid memory panic during fast_sync test ``` 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: panic: runtime error: invalid memory address or nil pointer dereference 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: [signal SIGSEGV: segmentation violation code=0x1 addr=0x20 pc=0x98dd3e] 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: goroutine 3432 [running]: 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: github.com/tendermint/tendermint/p2p.newOutboundPeerConn(0xc423fd1380, 0xc420933e00, 0x1, 0x1239a60, 0 xc420128c40, 0x2, 0x42caf6, 0xc42001f300, 0xc422831d98, 0xc4227951c0, ...) 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/peer.go:123 +0x31e 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: github.com/tendermint/tendermint/p2p.(*Switch).addOutboundPeerWithConfig(0xc4200ad040, 0xc423fd1380, 0 xc420933e00, 0xc423f48801, 0x28, 0x2) 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/switch.go:455 +0x12b 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: github.com/tendermint/tendermint/p2p.(*Switch).DialPeerWithAddress(0xc4200ad040, 0xc423fd1380, 0x1, 0x 0, 0x0) 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/switch.go:371 +0xdc 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: github.com/tendermint/tendermint/p2p.(*Switch).reconnectToPeer(0xc4200ad040, 0x123e000, 0xc42007bb00) 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/switch.go:290 +0x25f 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: created by github.com/tendermint/tendermint/p2p.(*Switch).StopPeerForError 2018-02-21T06:30:05Z box887.localdomain docker/local_testnet_4[14907]: #011/go/src/github.com/tendermint/tendermint/p2p/switch.go:256 +0x1b7 ```
7 years ago
8 years ago
7 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. crypto "github.com/tendermint/go-crypto"
  12. cfg "github.com/tendermint/tendermint/config"
  13. "github.com/tendermint/tendermint/p2p"
  14. "github.com/tendermint/tendermint/p2p/conn"
  15. cmn "github.com/tendermint/tmlibs/common"
  16. "github.com/tendermint/tmlibs/log"
  17. )
  18. var (
  19. config *cfg.P2PConfig
  20. )
  21. func init() {
  22. config = cfg.DefaultP2PConfig()
  23. config.PexReactor = true
  24. }
  25. func TestPEXReactorBasic(t *testing.T) {
  26. r, book := createReactor(&PEXReactorConfig{})
  27. defer teardownReactor(book)
  28. assert.NotNil(t, r)
  29. assert.NotEmpty(t, r.GetChannels())
  30. }
  31. func TestPEXReactorAddRemovePeer(t *testing.T) {
  32. r, book := createReactor(&PEXReactorConfig{})
  33. defer teardownReactor(book)
  34. size := book.Size()
  35. peer := p2p.CreateRandomPeer(false)
  36. r.AddPeer(peer)
  37. assert.Equal(t, size+1, book.Size())
  38. r.RemovePeer(peer, "peer not available")
  39. assert.Equal(t, size+1, book.Size())
  40. outboundPeer := p2p.CreateRandomPeer(true)
  41. r.AddPeer(outboundPeer)
  42. assert.Equal(t, size+1, book.Size(), "outbound peers should not be added to the address book")
  43. r.RemovePeer(outboundPeer, "peer not available")
  44. assert.Equal(t, size+1, book.Size())
  45. }
  46. func TestPEXReactorRunning(t *testing.T) {
  47. N := 3
  48. switches := make([]*p2p.Switch, N)
  49. dir, err := ioutil.TempDir("", "pex_reactor")
  50. require.Nil(t, err)
  51. defer os.RemoveAll(dir) // nolint: errcheck
  52. book := NewAddrBook(filepath.Join(dir, "addrbook.json"), false)
  53. book.SetLogger(log.TestingLogger())
  54. // create switches
  55. for i := 0; i < N; i++ {
  56. switches[i] = p2p.MakeSwitch(config, i, "127.0.0.1", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch {
  57. sw.SetLogger(log.TestingLogger().With("switch", i))
  58. r := NewPEXReactor(book, &PEXReactorConfig{})
  59. r.SetLogger(log.TestingLogger())
  60. r.SetEnsurePeersPeriod(250 * time.Millisecond)
  61. sw.AddReactor("pex", r)
  62. return sw
  63. })
  64. }
  65. // fill the address book and add listeners
  66. for _, s := range switches {
  67. addr := s.NodeInfo().NetAddress()
  68. book.AddAddress(addr, addr)
  69. s.AddListener(p2p.NewDefaultListener("tcp", s.NodeInfo().ListenAddr, true, log.TestingLogger()))
  70. }
  71. // start switches
  72. for _, s := range switches {
  73. err := s.Start() // start switch and reactors
  74. require.Nil(t, err)
  75. }
  76. assertPeersWithTimeout(t, switches, 10*time.Millisecond, 10*time.Second, N-1)
  77. // stop them
  78. for _, s := range switches {
  79. s.Stop()
  80. }
  81. }
  82. func TestPEXReactorReceive(t *testing.T) {
  83. r, book := createReactor(&PEXReactorConfig{})
  84. defer teardownReactor(book)
  85. peer := p2p.CreateRandomPeer(false)
  86. // we have to send a request to receive responses
  87. r.RequestAddrs(peer)
  88. size := book.Size()
  89. addrs := []*p2p.NetAddress{peer.NodeInfo().NetAddress()}
  90. msg := cdc.MustMarshalBinary(&pexAddrsMessage{Addrs: addrs})
  91. r.Receive(PexChannel, peer, msg)
  92. assert.Equal(t, size+1, book.Size())
  93. msg = cdc.MustMarshalBinary(&pexRequestMessage{})
  94. r.Receive(PexChannel, peer, msg) // should not panic.
  95. }
  96. func TestPEXReactorRequestMessageAbuse(t *testing.T) {
  97. r, book := createReactor(&PEXReactorConfig{})
  98. defer teardownReactor(book)
  99. sw := createSwitchAndAddReactors(r)
  100. peer := newMockPeer()
  101. p2p.AddPeerToSwitch(sw, peer)
  102. assert.True(t, sw.Peers().Has(peer.ID()))
  103. id := string(peer.ID())
  104. msg := cdc.MustMarshalBinary(&pexRequestMessage{})
  105. // first time creates the entry
  106. r.Receive(PexChannel, peer, msg)
  107. assert.True(t, r.lastReceivedRequests.Has(id))
  108. assert.True(t, sw.Peers().Has(peer.ID()))
  109. // next time sets the last time value
  110. r.Receive(PexChannel, peer, msg)
  111. assert.True(t, r.lastReceivedRequests.Has(id))
  112. assert.True(t, sw.Peers().Has(peer.ID()))
  113. // third time is too many too soon - peer is removed
  114. r.Receive(PexChannel, peer, msg)
  115. assert.False(t, r.lastReceivedRequests.Has(id))
  116. assert.False(t, sw.Peers().Has(peer.ID()))
  117. }
  118. func TestPEXReactorAddrsMessageAbuse(t *testing.T) {
  119. r, book := createReactor(&PEXReactorConfig{})
  120. defer teardownReactor(book)
  121. sw := createSwitchAndAddReactors(r)
  122. peer := newMockPeer()
  123. p2p.AddPeerToSwitch(sw, peer)
  124. assert.True(t, sw.Peers().Has(peer.ID()))
  125. id := string(peer.ID())
  126. // request addrs from the peer
  127. r.RequestAddrs(peer)
  128. assert.True(t, r.requestsSent.Has(id))
  129. assert.True(t, sw.Peers().Has(peer.ID()))
  130. addrs := []*p2p.NetAddress{peer.NodeInfo().NetAddress()}
  131. msg := cdc.MustMarshalBinary(&pexAddrsMessage{Addrs: addrs})
  132. // receive some addrs. should clear the request
  133. r.Receive(PexChannel, peer, msg)
  134. assert.False(t, r.requestsSent.Has(id))
  135. assert.True(t, sw.Peers().Has(peer.ID()))
  136. // receiving more addrs causes a disconnect
  137. r.Receive(PexChannel, peer, msg)
  138. assert.False(t, sw.Peers().Has(peer.ID()))
  139. }
  140. func TestPEXReactorUsesSeedsIfNeeded(t *testing.T) {
  141. dir, err := ioutil.TempDir("", "pex_reactor")
  142. require.Nil(t, err)
  143. defer os.RemoveAll(dir) // nolint: errcheck
  144. book := NewAddrBook(filepath.Join(dir, "addrbook.json"), false)
  145. book.SetLogger(log.TestingLogger())
  146. // 1. create seed
  147. seed := p2p.MakeSwitch(
  148. config,
  149. 0,
  150. "127.0.0.1",
  151. "123.123.123",
  152. func(i int, sw *p2p.Switch) *p2p.Switch {
  153. sw.SetLogger(log.TestingLogger())
  154. r := NewPEXReactor(book, &PEXReactorConfig{})
  155. r.SetLogger(log.TestingLogger())
  156. sw.AddReactor("pex", r)
  157. return sw
  158. },
  159. )
  160. seed.AddListener(
  161. p2p.NewDefaultListener(
  162. "tcp",
  163. seed.NodeInfo().ListenAddr,
  164. true,
  165. log.TestingLogger(),
  166. ),
  167. )
  168. require.Nil(t, seed.Start())
  169. defer seed.Stop()
  170. // 2. create usual peer with only seed configured.
  171. peer := p2p.MakeSwitch(
  172. config,
  173. 1,
  174. "127.0.0.1",
  175. "123.123.123",
  176. func(i int, sw *p2p.Switch) *p2p.Switch {
  177. sw.SetLogger(log.TestingLogger())
  178. r := NewPEXReactor(
  179. book,
  180. &PEXReactorConfig{
  181. Seeds: []string{seed.NodeInfo().NetAddress().String()},
  182. },
  183. )
  184. r.SetLogger(log.TestingLogger())
  185. sw.AddReactor("pex", r)
  186. return sw
  187. },
  188. )
  189. require.Nil(t, peer.Start())
  190. defer peer.Stop()
  191. // 3. check that the peer connects to seed immediately
  192. assertPeersWithTimeout(t, []*p2p.Switch{peer}, 10*time.Millisecond, 3*time.Second, 1)
  193. }
  194. func TestPEXReactorCrawlStatus(t *testing.T) {
  195. pexR, book := createReactor(&PEXReactorConfig{SeedMode: true})
  196. defer teardownReactor(book)
  197. // Seed/Crawler mode uses data from the Switch
  198. _ = createSwitchAndAddReactors(pexR)
  199. // Create a peer, add it to the peer set and the addrbook.
  200. peer := p2p.CreateRandomPeer(false)
  201. p2p.AddPeerToSwitch(pexR.Switch, peer)
  202. addr1 := peer.NodeInfo().NetAddress()
  203. pexR.book.AddAddress(addr1, addr1)
  204. // Add a non-connected address to the book.
  205. _, addr2 := p2p.CreateRoutableAddr()
  206. pexR.book.AddAddress(addr2, addr1)
  207. // Get some peerInfos to crawl
  208. peerInfos := pexR.getPeersToCrawl()
  209. // Make sure it has the proper number of elements
  210. assert.Equal(t, 2, len(peerInfos))
  211. // TODO: test
  212. }
  213. func TestPEXReactorDialPeer(t *testing.T) {
  214. pexR, book := createReactor(&PEXReactorConfig{})
  215. defer teardownReactor(book)
  216. _ = createSwitchAndAddReactors(pexR)
  217. peer := newMockPeer()
  218. addr := peer.NodeInfo().NetAddress()
  219. assert.Equal(t, 0, pexR.AttemptsToDial(addr))
  220. // 1st unsuccessful attempt
  221. pexR.dialPeer(addr)
  222. assert.Equal(t, 1, pexR.AttemptsToDial(addr))
  223. // 2nd unsuccessful attempt
  224. pexR.dialPeer(addr)
  225. // must be skipped because it is too early
  226. assert.Equal(t, 1, pexR.AttemptsToDial(addr))
  227. }
  228. type mockPeer struct {
  229. *cmn.BaseService
  230. pubKey crypto.PubKey
  231. addr *p2p.NetAddress
  232. outbound, persistent bool
  233. }
  234. func newMockPeer() mockPeer {
  235. _, netAddr := p2p.CreateRoutableAddr()
  236. mp := mockPeer{
  237. addr: netAddr,
  238. pubKey: crypto.GenPrivKeyEd25519().PubKey(),
  239. }
  240. mp.BaseService = cmn.NewBaseService(nil, "MockPeer", mp)
  241. mp.Start()
  242. return mp
  243. }
  244. func (mp mockPeer) ID() p2p.ID { return p2p.PubKeyToID(mp.pubKey) }
  245. func (mp mockPeer) IsOutbound() bool { return mp.outbound }
  246. func (mp mockPeer) IsPersistent() bool { return mp.persistent }
  247. func (mp mockPeer) NodeInfo() p2p.NodeInfo {
  248. return p2p.NodeInfo{
  249. PubKey: mp.pubKey,
  250. ListenAddr: mp.addr.DialString(),
  251. }
  252. }
  253. func (mp mockPeer) Status() conn.ConnectionStatus { return conn.ConnectionStatus{} }
  254. func (mp mockPeer) Send(byte, []byte) bool { return false }
  255. func (mp mockPeer) TrySend(byte, []byte) bool { return false }
  256. func (mp mockPeer) Set(string, interface{}) {}
  257. func (mp mockPeer) Get(string) interface{} { return nil }
  258. func assertPeersWithTimeout(
  259. t *testing.T,
  260. switches []*p2p.Switch,
  261. checkPeriod, timeout time.Duration,
  262. nPeers int,
  263. ) {
  264. var (
  265. ticker = time.NewTicker(checkPeriod)
  266. remaining = timeout
  267. )
  268. for {
  269. select {
  270. case <-ticker.C:
  271. // check peers are connected
  272. allGood := true
  273. for _, s := range switches {
  274. outbound, inbound, _ := s.NumPeers()
  275. if outbound+inbound < nPeers {
  276. allGood = false
  277. }
  278. }
  279. remaining -= checkPeriod
  280. if remaining < 0 {
  281. remaining = 0
  282. }
  283. if allGood {
  284. return
  285. }
  286. case <-time.After(remaining):
  287. numPeersStr := ""
  288. for i, s := range switches {
  289. outbound, inbound, _ := s.NumPeers()
  290. numPeersStr += fmt.Sprintf("%d => {outbound: %d, inbound: %d}, ", i, outbound, inbound)
  291. }
  292. t.Errorf(
  293. "expected all switches to be connected to at least one peer (switches: %s)",
  294. numPeersStr,
  295. )
  296. return
  297. }
  298. }
  299. }
  300. func createReactor(config *PEXReactorConfig) (r *PEXReactor, book *addrBook) {
  301. dir, err := ioutil.TempDir("", "pex_reactor")
  302. if err != nil {
  303. panic(err)
  304. }
  305. book = NewAddrBook(filepath.Join(dir, "addrbook.json"), true)
  306. book.SetLogger(log.TestingLogger())
  307. r = NewPEXReactor(book, &PEXReactorConfig{})
  308. r.SetLogger(log.TestingLogger())
  309. return
  310. }
  311. func teardownReactor(book *addrBook) {
  312. err := os.RemoveAll(filepath.Dir(book.FilePath()))
  313. if err != nil {
  314. panic(err)
  315. }
  316. }
  317. func createSwitchAndAddReactors(reactors ...p2p.Reactor) *p2p.Switch {
  318. sw := p2p.MakeSwitch(config, 0, "127.0.0.1", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch { return sw })
  319. sw.SetLogger(log.TestingLogger())
  320. for _, r := range reactors {
  321. sw.AddReactor(r.String(), r)
  322. r.SetSwitch(sw)
  323. }
  324. return sw
  325. }