dial_test.go 18 KB

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