dial_test.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  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, 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. // Tests that bootnodes are dialed if no peers are connectd, but not otherwise.
  210. func TestDialStateDynDialBootnode(t *testing.T) {
  211. bootnodes := []*discover.Node{
  212. {ID: uintID(1)},
  213. {ID: uintID(2)},
  214. {ID: uintID(3)},
  215. }
  216. table := fakeTable{
  217. {ID: uintID(4)},
  218. {ID: uintID(5)},
  219. {ID: uintID(6)},
  220. {ID: uintID(7)},
  221. {ID: uintID(8)},
  222. }
  223. runDialTest(t, dialtest{
  224. init: newDialState(nil, bootnodes, table, 5, nil),
  225. rounds: []round{
  226. // 2 dynamic dials attempted, bootnodes pending fallback interval
  227. {
  228. new: []task{
  229. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
  230. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
  231. &discoverTask{},
  232. },
  233. },
  234. // No dials succeed, bootnodes still pending fallback interval
  235. {
  236. done: []task{
  237. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
  238. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
  239. },
  240. },
  241. // No dials succeed, bootnodes still pending fallback interval
  242. {},
  243. // No dials succeed, 2 dynamic dials attempted and 1 bootnode too as fallback interval was reached
  244. {
  245. new: []task{
  246. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
  247. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
  248. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
  249. },
  250. },
  251. // No dials succeed, 2nd bootnode is attempted
  252. {
  253. done: []task{
  254. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
  255. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
  256. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
  257. },
  258. new: []task{
  259. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
  260. },
  261. },
  262. // No dials succeed, 3rd bootnode is attempted
  263. {
  264. done: []task{
  265. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
  266. },
  267. new: []task{
  268. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
  269. },
  270. },
  271. // No dials succeed, 1st bootnode is attempted again, expired random nodes retried
  272. {
  273. done: []task{
  274. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
  275. },
  276. new: []task{
  277. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
  278. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
  279. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
  280. },
  281. },
  282. // Random dial succeeds, no more bootnodes are attempted
  283. {
  284. peers: []*Peer{
  285. {rw: &conn{flags: dynDialedConn, id: uintID(4)}},
  286. },
  287. done: []task{
  288. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
  289. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
  290. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
  291. },
  292. },
  293. },
  294. })
  295. }
  296. func TestDialStateDynDialFromTable(t *testing.T) {
  297. // This table always returns the same random nodes
  298. // in the order given below.
  299. table := fakeTable{
  300. {ID: uintID(1)},
  301. {ID: uintID(2)},
  302. {ID: uintID(3)},
  303. {ID: uintID(4)},
  304. {ID: uintID(5)},
  305. {ID: uintID(6)},
  306. {ID: uintID(7)},
  307. {ID: uintID(8)},
  308. }
  309. runDialTest(t, dialtest{
  310. init: newDialState(nil, nil, table, 10, nil),
  311. rounds: []round{
  312. // 5 out of 8 of the nodes returned by ReadRandomNodes are dialed.
  313. {
  314. new: []task{
  315. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
  316. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
  317. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
  318. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
  319. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
  320. &discoverTask{},
  321. },
  322. },
  323. // Dialing nodes 1,2 succeeds. Dials from the lookup are launched.
  324. {
  325. peers: []*Peer{
  326. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  327. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  328. },
  329. done: []task{
  330. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}},
  331. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}},
  332. &discoverTask{results: []*discover.Node{
  333. {ID: uintID(10)},
  334. {ID: uintID(11)},
  335. {ID: uintID(12)},
  336. }},
  337. },
  338. new: []task{
  339. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}},
  340. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}},
  341. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}},
  342. &discoverTask{},
  343. },
  344. },
  345. // Dialing nodes 3,4,5 fails. The dials from the lookup succeed.
  346. {
  347. peers: []*Peer{
  348. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  349. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  350. {rw: &conn{flags: dynDialedConn, id: uintID(10)}},
  351. {rw: &conn{flags: dynDialedConn, id: uintID(11)}},
  352. {rw: &conn{flags: dynDialedConn, id: uintID(12)}},
  353. },
  354. done: []task{
  355. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}},
  356. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}},
  357. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}},
  358. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}},
  359. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}},
  360. &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}},
  361. },
  362. },
  363. // Waiting for expiry. No waitExpireTask is launched because the
  364. // discovery query is still running.
  365. {
  366. peers: []*Peer{
  367. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  368. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  369. {rw: &conn{flags: dynDialedConn, id: uintID(10)}},
  370. {rw: &conn{flags: dynDialedConn, id: uintID(11)}},
  371. {rw: &conn{flags: dynDialedConn, id: uintID(12)}},
  372. },
  373. },
  374. // Nodes 3,4 are not tried again because only the first two
  375. // returned random nodes (nodes 1,2) are tried and they're
  376. // already connected.
  377. {
  378. peers: []*Peer{
  379. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  380. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  381. {rw: &conn{flags: dynDialedConn, id: uintID(10)}},
  382. {rw: &conn{flags: dynDialedConn, id: uintID(11)}},
  383. {rw: &conn{flags: dynDialedConn, id: uintID(12)}},
  384. },
  385. },
  386. },
  387. })
  388. }
  389. // This test checks that candidates that do not match the netrestrict list are not dialed.
  390. func TestDialStateNetRestrict(t *testing.T) {
  391. // This table always returns the same random nodes
  392. // in the order given below.
  393. table := fakeTable{
  394. {ID: uintID(1), IP: net.ParseIP("127.0.0.1")},
  395. {ID: uintID(2), IP: net.ParseIP("127.0.0.2")},
  396. {ID: uintID(3), IP: net.ParseIP("127.0.0.3")},
  397. {ID: uintID(4), IP: net.ParseIP("127.0.0.4")},
  398. {ID: uintID(5), IP: net.ParseIP("127.0.2.5")},
  399. {ID: uintID(6), IP: net.ParseIP("127.0.2.6")},
  400. {ID: uintID(7), IP: net.ParseIP("127.0.2.7")},
  401. {ID: uintID(8), IP: net.ParseIP("127.0.2.8")},
  402. }
  403. restrict := new(netutil.Netlist)
  404. restrict.Add("127.0.2.0/24")
  405. runDialTest(t, dialtest{
  406. init: newDialState(nil, nil, table, 10, restrict),
  407. rounds: []round{
  408. {
  409. new: []task{
  410. &dialTask{flags: dynDialedConn, dest: table[4]},
  411. &discoverTask{},
  412. },
  413. },
  414. },
  415. })
  416. }
  417. // This test checks that static dials are launched.
  418. func TestDialStateStaticDial(t *testing.T) {
  419. wantStatic := []*discover.Node{
  420. {ID: uintID(1)},
  421. {ID: uintID(2)},
  422. {ID: uintID(3)},
  423. {ID: uintID(4)},
  424. {ID: uintID(5)},
  425. }
  426. runDialTest(t, dialtest{
  427. init: newDialState(wantStatic, nil, fakeTable{}, 0, nil),
  428. rounds: []round{
  429. // Static dials are launched for the nodes that
  430. // aren't yet connected.
  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{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
  438. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
  439. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}},
  440. },
  441. },
  442. // No new tasks are launched in this round because all static
  443. // nodes are either connected or still being dialed.
  444. {
  445. peers: []*Peer{
  446. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  447. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  448. {rw: &conn{flags: staticDialedConn, id: uintID(3)}},
  449. },
  450. done: []task{
  451. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
  452. },
  453. },
  454. // No new dial tasks are launched because all static
  455. // nodes are now connected.
  456. {
  457. peers: []*Peer{
  458. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  459. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  460. {rw: &conn{flags: staticDialedConn, id: uintID(3)}},
  461. {rw: &conn{flags: staticDialedConn, id: uintID(4)}},
  462. {rw: &conn{flags: staticDialedConn, id: uintID(5)}},
  463. },
  464. done: []task{
  465. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
  466. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}},
  467. },
  468. new: []task{
  469. &waitExpireTask{Duration: 14 * time.Second},
  470. },
  471. },
  472. // Wait a round for dial history to expire, no new tasks should spawn.
  473. {
  474. peers: []*Peer{
  475. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  476. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  477. {rw: &conn{flags: staticDialedConn, id: uintID(3)}},
  478. {rw: &conn{flags: staticDialedConn, id: uintID(4)}},
  479. {rw: &conn{flags: staticDialedConn, id: uintID(5)}},
  480. },
  481. },
  482. // If a static node is dropped, it should be immediately redialed,
  483. // irrespective whether it was originally static or dynamic.
  484. {
  485. peers: []*Peer{
  486. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  487. {rw: &conn{flags: staticDialedConn, id: uintID(3)}},
  488. {rw: &conn{flags: staticDialedConn, id: uintID(5)}},
  489. },
  490. new: []task{
  491. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
  492. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}},
  493. },
  494. },
  495. },
  496. })
  497. }
  498. // This test checks that past dials are not retried for some time.
  499. func TestDialStateCache(t *testing.T) {
  500. wantStatic := []*discover.Node{
  501. {ID: uintID(1)},
  502. {ID: uintID(2)},
  503. {ID: uintID(3)},
  504. }
  505. runDialTest(t, dialtest{
  506. init: newDialState(wantStatic, nil, fakeTable{}, 0, nil),
  507. rounds: []round{
  508. // Static dials are launched for the nodes that
  509. // aren't yet connected.
  510. {
  511. peers: nil,
  512. new: []task{
  513. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
  514. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
  515. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
  516. },
  517. },
  518. // No new tasks are launched in this round because all static
  519. // nodes are either connected or still being dialed.
  520. {
  521. peers: []*Peer{
  522. {rw: &conn{flags: staticDialedConn, id: uintID(1)}},
  523. {rw: &conn{flags: staticDialedConn, id: uintID(2)}},
  524. },
  525. done: []task{
  526. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}},
  527. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}},
  528. },
  529. },
  530. // A salvage task is launched to wait for node 3's history
  531. // entry to expire.
  532. {
  533. peers: []*Peer{
  534. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  535. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  536. },
  537. done: []task{
  538. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
  539. },
  540. new: []task{
  541. &waitExpireTask{Duration: 14 * time.Second},
  542. },
  543. },
  544. // Still waiting for node 3's entry to expire in the cache.
  545. {
  546. peers: []*Peer{
  547. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  548. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  549. },
  550. },
  551. // The cache entry for node 3 has expired and is retried.
  552. {
  553. peers: []*Peer{
  554. {rw: &conn{flags: dynDialedConn, id: uintID(1)}},
  555. {rw: &conn{flags: dynDialedConn, id: uintID(2)}},
  556. },
  557. new: []task{
  558. &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}},
  559. },
  560. },
  561. },
  562. })
  563. }
  564. func TestDialResolve(t *testing.T) {
  565. resolved := discover.NewNode(uintID(1), net.IP{127, 0, 55, 234}, 3333, 4444)
  566. table := &resolveMock{answer: resolved}
  567. state := newDialState(nil, nil, table, 0, nil)
  568. // Check that the task is generated with an incomplete ID.
  569. dest := discover.NewNode(uintID(1), nil, 0, 0)
  570. state.addStatic(dest)
  571. tasks := state.newTasks(0, nil, time.Time{})
  572. if !reflect.DeepEqual(tasks, []task{&dialTask{flags: staticDialedConn, dest: dest}}) {
  573. t.Fatalf("expected dial task, got %#v", tasks)
  574. }
  575. // Now run the task, it should resolve the ID once.
  576. config := Config{Dialer: &net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}}
  577. srv := &Server{ntab: table, Config: config}
  578. tasks[0].Do(srv)
  579. if !reflect.DeepEqual(table.resolveCalls, []discover.NodeID{dest.ID}) {
  580. t.Fatalf("wrong resolve calls, got %v", table.resolveCalls)
  581. }
  582. // Report it as done to the dialer, which should update the static node record.
  583. state.taskDone(tasks[0], time.Now())
  584. if state.static[uintID(1)].dest != resolved {
  585. t.Fatalf("state.dest not updated")
  586. }
  587. }
  588. // compares task lists but doesn't care about the order.
  589. func sametasks(a, b []task) bool {
  590. if len(a) != len(b) {
  591. return false
  592. }
  593. next:
  594. for _, ta := range a {
  595. for _, tb := range b {
  596. if reflect.DeepEqual(ta, tb) {
  597. continue next
  598. }
  599. }
  600. return false
  601. }
  602. return true
  603. }
  604. func uintID(i uint32) discover.NodeID {
  605. var id discover.NodeID
  606. binary.BigEndian.PutUint32(id[:], i)
  607. return id
  608. }
  609. // implements discoverTable for TestDialResolve
  610. type resolveMock struct {
  611. resolveCalls []discover.NodeID
  612. answer *discover.Node
  613. }
  614. func (t *resolveMock) Resolve(id discover.NodeID) *discover.Node {
  615. t.resolveCalls = append(t.resolveCalls, id)
  616. return t.answer
  617. }
  618. func (t *resolveMock) Self() *discover.Node { return new(discover.Node) }
  619. func (t *resolveMock) Close() {}
  620. func (t *resolveMock) Bootstrap([]*discover.Node) {}
  621. func (t *resolveMock) Lookup(discover.NodeID) []*discover.Node { return nil }
  622. func (t *resolveMock) ReadRandomNodes(buf []*discover.Node) int { return 0 }