dial_test.go 15 KB

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