dial_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. // Copyright 2015 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package p2p
  17. import (
  18. "encoding/binary"
  19. "net"
  20. "reflect"
  21. "testing"
  22. "time"
  23. "github.com/davecgh/go-spew/spew"
  24. "github.com/ethereum/go-ethereum/p2p/discover"
  25. )
  26. func init() {
  27. spew.Config.Indent = "\t"
  28. }
  29. type dialtest struct {
  30. init *dialstate // state before and after the test.
  31. rounds []round
  32. }
  33. type round struct {
  34. peers []*Peer // current peer set
  35. done []task // tasks that got done this round
  36. new []task // the result must match this one
  37. }
  38. func runDialTest(t *testing.T, test dialtest) {
  39. var (
  40. vtime time.Time
  41. running int
  42. )
  43. pm := func(ps []*Peer) map[discover.NodeID]*Peer {
  44. m := make(map[discover.NodeID]*Peer)
  45. for _, p := range ps {
  46. m[p.rw.id] = p
  47. }
  48. return m
  49. }
  50. for i, round := range test.rounds {
  51. for _, task := range round.done {
  52. running--
  53. if running < 0 {
  54. panic("running task counter underflow")
  55. }
  56. test.init.taskDone(task, vtime)
  57. }
  58. new := test.init.newTasks(running, pm(round.peers), vtime)
  59. if !sametasks(new, round.new) {
  60. t.Errorf("round %d: new tasks mismatch:\ngot %v\nwant %v\nstate: %v\nrunning: %v\n",
  61. i, spew.Sdump(new), spew.Sdump(round.new), spew.Sdump(test.init), spew.Sdump(running))
  62. }
  63. // Time advances by 16 seconds on every round.
  64. vtime = vtime.Add(16 * time.Second)
  65. running += len(new)
  66. }
  67. }
  68. type fakeTable []*discover.Node
  69. func (t fakeTable) Self() *discover.Node { return new(discover.Node) }
  70. func (t fakeTable) Close() {}
  71. func (t fakeTable) Lookup(discover.NodeID) []*discover.Node { return nil }
  72. func (t fakeTable) Resolve(discover.NodeID) *discover.Node { return nil }
  73. func (t fakeTable) ReadRandomNodes(buf []*discover.Node) int { return copy(buf, t) }
  74. // This test checks that dynamic dials are launched from discovery results.
  75. func TestDialStateDynDial(t *testing.T) {
  76. runDialTest(t, dialtest{
  77. init: newDialState(nil, fakeTable{}, 5),
  78. rounds: []round{
  79. // A discovery query is launched.
  80. {
  81. peers: []*Peer{
  82. {rw: &conn{flags: staticDialedConn, id: uintID(0)}},
  83. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  84. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  85. },
  86. new: []task{&discoverTask{}},
  87. },
  88. // Dynamic dials are launched when it completes.
  89. {
  90. peers: []*Peer{
  91. {rw: &conn{flags: staticDialedConn, id: uintID(0)}},
  92. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  93. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  94. },
  95. done: []task{
  96. &discoverTask{results: []*discover.Node{
  97. {ID: uintID(2)}, // this one is already connected and not dialed.
  98. {ID: uintID(3)},
  99. {ID: uintID(4)},
  100. {ID: uintID(5)},
  101. {ID: uintID(6)}, // these are not tried because max dyn dials is 5
  102. {ID: uintID(7)}, // ...
  103. }},
  104. },
  105. new: []task{
  106. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
  107. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
  108. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
  109. },
  110. },
  111. // Some of the dials complete but no new ones are launched yet because
  112. // the sum of active dial count and dynamic peer count is == maxDynDials.
  113. {
  114. peers: []*Peer{
  115. {rw: &conn{flags: staticDialedConn, id: uintID(0)}},
  116. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  117. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  118. {rw: &conn{flags: dynDialedConn, id: uintID(3)}},
  119. {rw: &conn{flags: dynDialedConn, id: uintID(4)}},
  120. },
  121. done: []task{
  122. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
  123. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
  124. },
  125. },
  126. // No new dial tasks are launched in the this round because
  127. // maxDynDials has been reached.
  128. {
  129. peers: []*Peer{
  130. {rw: &conn{flags: staticDialedConn, id: uintID(0)}},
  131. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  132. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  133. {rw: &conn{flags: dynDialedConn, id: uintID(3)}},
  134. {rw: &conn{flags: dynDialedConn, id: uintID(4)}},
  135. {rw: &conn{flags: dynDialedConn, id: uintID(5)}},
  136. },
  137. done: []task{
  138. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
  139. },
  140. new: []task{
  141. &waitExpireTask{Duration: 14 * time.Second},
  142. },
  143. },
  144. // In this round, the peer with id 2 drops off. The query
  145. // results from last discovery lookup are reused.
  146. {
  147. peers: []*Peer{
  148. {rw: &conn{flags: staticDialedConn, id: uintID(0)}},
  149. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  150. {rw: &conn{flags: dynDialedConn, id: uintID(3)}},
  151. {rw: &conn{flags: dynDialedConn, id: uintID(4)}},
  152. {rw: &conn{flags: dynDialedConn, id: uintID(5)}},
  153. },
  154. new: []task{
  155. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(6)}},
  156. },
  157. },
  158. // More peers (3,4) drop off and dial for ID 6 completes.
  159. // The last query result from the discovery lookup is reused
  160. // and a new one is spawned because more candidates are needed.
  161. {
  162. peers: []*Peer{
  163. {rw: &conn{flags: staticDialedConn, id: uintID(0)}},
  164. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  165. {rw: &conn{flags: dynDialedConn, id: uintID(5)}},
  166. },
  167. done: []task{
  168. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(6)}},
  169. },
  170. new: []task{
  171. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(7)}},
  172. &discoverTask{},
  173. },
  174. },
  175. // Peer 7 is connected, but there still aren't enough dynamic peers
  176. // (4 out of 5). However, a discovery is already running, so ensure
  177. // no new is started.
  178. {
  179. peers: []*Peer{
  180. {rw: &conn{flags: staticDialedConn, id: uintID(0)}},
  181. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  182. {rw: &conn{flags: dynDialedConn, id: uintID(5)}},
  183. {rw: &conn{flags: dynDialedConn, id: uintID(7)}},
  184. },
  185. done: []task{
  186. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(7)}},
  187. },
  188. },
  189. // Finish the running node discovery with an empty set. A new lookup
  190. // should be immediately requested.
  191. {
  192. peers: []*Peer{
  193. {rw: &conn{flags: staticDialedConn, id: uintID(0)}},
  194. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  195. {rw: &conn{flags: dynDialedConn, id: uintID(5)}},
  196. {rw: &conn{flags: dynDialedConn, id: uintID(7)}},
  197. },
  198. done: []task{
  199. &discoverTask{},
  200. },
  201. new: []task{
  202. &discoverTask{},
  203. },
  204. },
  205. },
  206. })
  207. }
  208. func TestDialStateDynDialFromTable(t *testing.T) {
  209. // This table always returns the same random nodes
  210. // in the order given below.
  211. table := fakeTable{
  212. {ID: uintID(1)},
  213. {ID: uintID(2)},
  214. {ID: uintID(3)},
  215. {ID: uintID(4)},
  216. {ID: uintID(5)},
  217. {ID: uintID(6)},
  218. {ID: uintID(7)},
  219. {ID: uintID(8)},
  220. }
  221. runDialTest(t, dialtest{
  222. init: newDialState(nil, table, 10),
  223. rounds: []round{
  224. // 5 out of 8 of the nodes returned by ReadRandomNodes are dialed.
  225. {
  226. new: []task{
  227. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
  228. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
  229. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
  230. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
  231. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
  232. &discoverTask{},
  233. },
  234. },
  235. // Dialing nodes 1,2 succeeds. Dials from the lookup are launched.
  236. {
  237. peers: []*Peer{
  238. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  239. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  240. },
  241. done: []task{
  242. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
  243. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
  244. &discoverTask{results: []*discover.Node{
  245. {ID: uintID(10)},
  246. {ID: uintID(11)},
  247. {ID: uintID(12)},
  248. }},
  249. },
  250. new: []task{
  251. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}},
  252. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}},
  253. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}},
  254. &discoverTask{},
  255. },
  256. },
  257. // Dialing nodes 3,4,5 fails. The dials from the lookup succeed.
  258. {
  259. peers: []*Peer{
  260. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  261. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  262. {rw: &conn{flags: dynDialedConn, id: uintID(10)}},
  263. {rw: &conn{flags: dynDialedConn, id: uintID(11)}},
  264. {rw: &conn{flags: dynDialedConn, id: uintID(12)}},
  265. },
  266. done: []task{
  267. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
  268. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
  269. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
  270. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}},
  271. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}},
  272. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}},
  273. },
  274. },
  275. // Waiting for expiry. No waitExpireTask is launched because the
  276. // discovery query is still running.
  277. {
  278. peers: []*Peer{
  279. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  280. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  281. {rw: &conn{flags: dynDialedConn, id: uintID(10)}},
  282. {rw: &conn{flags: dynDialedConn, id: uintID(11)}},
  283. {rw: &conn{flags: dynDialedConn, id: uintID(12)}},
  284. },
  285. },
  286. // Nodes 3,4 are not tried again because only the first two
  287. // returned random nodes (nodes 1,2) are tried and they're
  288. // already connected.
  289. {
  290. peers: []*Peer{
  291. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  292. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  293. {rw: &conn{flags: dynDialedConn, id: uintID(10)}},
  294. {rw: &conn{flags: dynDialedConn, id: uintID(11)}},
  295. {rw: &conn{flags: dynDialedConn, id: uintID(12)}},
  296. },
  297. },
  298. },
  299. })
  300. }
  301. // This test checks that static dials are launched.
  302. func TestDialStateStaticDial(t *testing.T) {
  303. wantStatic := []*discover.Node{
  304. {ID: uintID(1)},
  305. {ID: uintID(2)},
  306. {ID: uintID(3)},
  307. {ID: uintID(4)},
  308. {ID: uintID(5)},
  309. }
  310. runDialTest(t, dialtest{
  311. init: newDialState(wantStatic, fakeTable{}, 0),
  312. rounds: []round{
  313. // Static dials are launched for the nodes that
  314. // aren't yet connected.
  315. {
  316. peers: []*Peer{
  317. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  318. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  319. },
  320. new: []task{
  321. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
  322. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
  323. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}},
  324. },
  325. },
  326. // No new tasks are launched in this round because all static
  327. // nodes are either connected or still being dialed.
  328. {
  329. peers: []*Peer{
  330. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  331. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  332. {rw: &conn{flags: staticDialedConn, id: uintID(3)}},
  333. },
  334. done: []task{
  335. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
  336. },
  337. },
  338. // No new dial tasks are launched because all static
  339. // nodes are now connected.
  340. {
  341. peers: []*Peer{
  342. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  343. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  344. {rw: &conn{flags: staticDialedConn, id: uintID(3)}},
  345. {rw: &conn{flags: staticDialedConn, id: uintID(4)}},
  346. {rw: &conn{flags: staticDialedConn, id: uintID(5)}},
  347. },
  348. done: []task{
  349. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
  350. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}},
  351. },
  352. new: []task{
  353. &waitExpireTask{Duration: 14 * time.Second},
  354. },
  355. },
  356. // Wait a round for dial history to expire, no new tasks should spawn.
  357. {
  358. peers: []*Peer{
  359. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  360. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  361. {rw: &conn{flags: staticDialedConn, id: uintID(3)}},
  362. {rw: &conn{flags: staticDialedConn, id: uintID(4)}},
  363. {rw: &conn{flags: staticDialedConn, id: uintID(5)}},
  364. },
  365. },
  366. // If a static node is dropped, it should be immediately redialed,
  367. // irrespective whether it was originally static or dynamic.
  368. {
  369. peers: []*Peer{
  370. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  371. {rw: &conn{flags: staticDialedConn, id: uintID(3)}},
  372. {rw: &conn{flags: staticDialedConn, id: uintID(5)}},
  373. },
  374. new: []task{
  375. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
  376. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
  377. },
  378. },
  379. },
  380. })
  381. }
  382. // This test checks that past dials are not retried for some time.
  383. func TestDialStateCache(t *testing.T) {
  384. wantStatic := []*discover.Node{
  385. {ID: uintID(1)},
  386. {ID: uintID(2)},
  387. {ID: uintID(3)},
  388. }
  389. runDialTest(t, dialtest{
  390. init: newDialState(wantStatic, fakeTable{}, 0),
  391. rounds: []round{
  392. // Static dials are launched for the nodes that
  393. // aren't yet connected.
  394. {
  395. peers: nil,
  396. new: []task{
  397. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
  398. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
  399. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
  400. },
  401. },
  402. // No new tasks are launched in this round because all static
  403. // nodes are either connected or still being dialed.
  404. {
  405. peers: []*Peer{
  406. {rw: &conn{flags: staticDialedConn, id: uintID(1)}},
  407. {rw: &conn{flags: staticDialedConn, id: uintID(2)}},
  408. },
  409. done: []task{
  410. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
  411. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
  412. },
  413. },
  414. // A salvage task is launched to wait for node 3's history
  415. // entry to expire.
  416. {
  417. peers: []*Peer{
  418. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  419. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  420. },
  421. done: []task{
  422. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
  423. },
  424. new: []task{
  425. &waitExpireTask{Duration: 14 * time.Second},
  426. },
  427. },
  428. // Still waiting for node 3's entry to expire in the cache.
  429. {
  430. peers: []*Peer{
  431. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  432. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  433. },
  434. },
  435. // The cache entry for node 3 has expired and is retried.
  436. {
  437. peers: []*Peer{
  438. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  439. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  440. },
  441. new: []task{
  442. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
  443. },
  444. },
  445. },
  446. })
  447. }
  448. func TestDialResolve(t *testing.T) {
  449. resolved := discover.NewNode(uintID(1), net.IP{127, 0, 55, 234}, 3333, 4444)
  450. table := &resolveMock{answer: resolved}
  451. state := newDialState(nil, table, 0)
  452. // Check that the task is generated with an incomplete ID.
  453. dest := discover.NewNode(uintID(1), nil, 0, 0)
  454. state.addStatic(dest)
  455. tasks := state.newTasks(0, nil, time.Time{})
  456. if !reflect.DeepEqual(tasks, []task{&dialTask{flags: staticDialedConn, dest: dest}}) {
  457. t.Fatalf("expected dial task, got %#v", tasks)
  458. }
  459. // Now run the task, it should resolve the ID once.
  460. srv := &Server{ntab: table, Dialer: &net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}}
  461. tasks[0].Do(srv)
  462. if !reflect.DeepEqual(table.resolveCalls, []discover.NodeID{dest.ID}) {
  463. t.Fatalf("wrong resolve calls, got %v", table.resolveCalls)
  464. }
  465. // Report it as done to the dialer, which should update the static node record.
  466. state.taskDone(tasks[0], time.Now())
  467. if state.static[uintID(1)].dest != resolved {
  468. t.Fatalf("state.dest not updated")
  469. }
  470. }
  471. // compares task lists but doesn't care about the order.
  472. func sametasks(a, b []task) bool {
  473. if len(a) != len(b) {
  474. return false
  475. }
  476. next:
  477. for _, ta := range a {
  478. for _, tb := range b {
  479. if reflect.DeepEqual(ta, tb) {
  480. continue next
  481. }
  482. }
  483. return false
  484. }
  485. return true
  486. }
  487. func uintID(i uint32) discover.NodeID {
  488. var id discover.NodeID
  489. binary.BigEndian.PutUint32(id[:], i)
  490. return id
  491. }
  492. // implements discoverTable for TestDialResolve
  493. type resolveMock struct {
  494. resolveCalls []discover.NodeID
  495. answer *discover.Node
  496. }
  497. func (t *resolveMock) Resolve(id discover.NodeID) *discover.Node {
  498. t.resolveCalls = append(t.resolveCalls, id)
  499. return t.answer
  500. }
  501. func (t *resolveMock) Self() *discover.Node { return new(discover.Node) }
  502. func (t *resolveMock) Close() {}
  503. func (t *resolveMock) Bootstrap([]*discover.Node) {}
  504. func (t *resolveMock) Lookup(discover.NodeID) []*discover.Node { return nil }
  505. func (t *resolveMock) ReadRandomNodes(buf []*discover.Node) int { return 0 }