github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/p2p/dial_test.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 package p2p 13 14 import ( 15 "encoding/binary" 16 "net" 17 "reflect" 18 "testing" 19 "time" 20 21 "github.com/davecgh/go-spew/spew" 22 "github.com/Sberex/go-sberex/p2p/discover" 23 "github.com/Sberex/go-sberex/p2p/netutil" 24 ) 25 26 func init() { 27 spew.Config.Indent = "\t" 28 } 29 30 type dialtest struct { 31 init *dialstate // state before and after the test. 32 rounds []round 33 } 34 35 type round struct { 36 peers []*Peer // current peer set 37 done []task // tasks that got done this round 38 new []task // the result must match this one 39 } 40 41 func runDialTest(t *testing.T, test dialtest) { 42 var ( 43 vtime time.Time 44 running int 45 ) 46 pm := func(ps []*Peer) map[discover.NodeID]*Peer { 47 m := make(map[discover.NodeID]*Peer) 48 for _, p := range ps { 49 m[p.rw.id] = p 50 } 51 return m 52 } 53 for i, round := range test.rounds { 54 for _, task := range round.done { 55 running-- 56 if running < 0 { 57 panic("running task counter underflow") 58 } 59 test.init.taskDone(task, vtime) 60 } 61 62 new := test.init.newTasks(running, pm(round.peers), vtime) 63 if !sametasks(new, round.new) { 64 t.Errorf("round %d: new tasks mismatch:\ngot %v\nwant %v\nstate: %v\nrunning: %v\n", 65 i, spew.Sdump(new), spew.Sdump(round.new), spew.Sdump(test.init), spew.Sdump(running)) 66 } 67 68 // Time advances by 16 seconds on every round. 69 vtime = vtime.Add(16 * time.Second) 70 running += len(new) 71 } 72 } 73 74 type fakeTable []*discover.Node 75 76 func (t fakeTable) Self() *discover.Node { return new(discover.Node) } 77 func (t fakeTable) Close() {} 78 func (t fakeTable) Lookup(discover.NodeID) []*discover.Node { return nil } 79 func (t fakeTable) Resolve(discover.NodeID) *discover.Node { return nil } 80 func (t fakeTable) ReadRandomNodes(buf []*discover.Node) int { return copy(buf, t) } 81 82 // This test checks that dynamic dials are launched from discovery results. 83 func TestDialStateDynDial(t *testing.T) { 84 runDialTest(t, dialtest{ 85 init: newDialState(nil, nil, fakeTable{}, 5, nil), 86 rounds: []round{ 87 // A discovery query is launched. 88 { 89 peers: []*Peer{ 90 {rw: &conn{flags: staticDialedConn, id: uintID(0)}}, 91 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 92 {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, 93 }, 94 new: []task{&discoverTask{}}, 95 }, 96 // Dynamic dials are launched when it completes. 97 { 98 peers: []*Peer{ 99 {rw: &conn{flags: staticDialedConn, id: uintID(0)}}, 100 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 101 {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, 102 }, 103 done: []task{ 104 &discoverTask{results: []*discover.Node{ 105 {ID: uintID(2)}, // this one is already connected and not dialed. 106 {ID: uintID(3)}, 107 {ID: uintID(4)}, 108 {ID: uintID(5)}, 109 {ID: uintID(6)}, // these are not tried because max dyn dials is 5 110 {ID: uintID(7)}, // ... 111 }}, 112 }, 113 new: []task{ 114 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}}, 115 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, 116 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, 117 }, 118 }, 119 // Some of the dials complete but no new ones are launched yet because 120 // the sum of active dial count and dynamic peer count is == maxDynDials. 121 { 122 peers: []*Peer{ 123 {rw: &conn{flags: staticDialedConn, id: uintID(0)}}, 124 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 125 {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, 126 {rw: &conn{flags: dynDialedConn, id: uintID(3)}}, 127 {rw: &conn{flags: dynDialedConn, id: uintID(4)}}, 128 }, 129 done: []task{ 130 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}}, 131 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, 132 }, 133 }, 134 // No new dial tasks are launched in the this round because 135 // maxDynDials has been reached. 136 { 137 peers: []*Peer{ 138 {rw: &conn{flags: staticDialedConn, id: uintID(0)}}, 139 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 140 {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, 141 {rw: &conn{flags: dynDialedConn, id: uintID(3)}}, 142 {rw: &conn{flags: dynDialedConn, id: uintID(4)}}, 143 {rw: &conn{flags: dynDialedConn, id: uintID(5)}}, 144 }, 145 done: []task{ 146 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, 147 }, 148 new: []task{ 149 &waitExpireTask{Duration: 14 * time.Second}, 150 }, 151 }, 152 // In this round, the peer with id 2 drops off. The query 153 // results from last discovery lookup are reused. 154 { 155 peers: []*Peer{ 156 {rw: &conn{flags: staticDialedConn, id: uintID(0)}}, 157 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 158 {rw: &conn{flags: dynDialedConn, id: uintID(3)}}, 159 {rw: &conn{flags: dynDialedConn, id: uintID(4)}}, 160 {rw: &conn{flags: dynDialedConn, id: uintID(5)}}, 161 }, 162 new: []task{ 163 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(6)}}, 164 }, 165 }, 166 // More peers (3,4) drop off and dial for ID 6 completes. 167 // The last query result from the discovery lookup is reused 168 // and a new one is spawned because more candidates are needed. 169 { 170 peers: []*Peer{ 171 {rw: &conn{flags: staticDialedConn, id: uintID(0)}}, 172 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 173 {rw: &conn{flags: dynDialedConn, id: uintID(5)}}, 174 }, 175 done: []task{ 176 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(6)}}, 177 }, 178 new: []task{ 179 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(7)}}, 180 &discoverTask{}, 181 }, 182 }, 183 // Peer 7 is connected, but there still aren't enough dynamic peers 184 // (4 out of 5). However, a discovery is already running, so ensure 185 // no new is started. 186 { 187 peers: []*Peer{ 188 {rw: &conn{flags: staticDialedConn, id: uintID(0)}}, 189 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 190 {rw: &conn{flags: dynDialedConn, id: uintID(5)}}, 191 {rw: &conn{flags: dynDialedConn, id: uintID(7)}}, 192 }, 193 done: []task{ 194 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(7)}}, 195 }, 196 }, 197 // Finish the running node discovery with an empty set. A new lookup 198 // should be immediately requested. 199 { 200 peers: []*Peer{ 201 {rw: &conn{flags: staticDialedConn, id: uintID(0)}}, 202 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 203 {rw: &conn{flags: dynDialedConn, id: uintID(5)}}, 204 {rw: &conn{flags: dynDialedConn, id: uintID(7)}}, 205 }, 206 done: []task{ 207 &discoverTask{}, 208 }, 209 new: []task{ 210 &discoverTask{}, 211 }, 212 }, 213 }, 214 }) 215 } 216 217 // Tests that bootnodes are dialed if no peers are connectd, but not otherwise. 218 func TestDialStateDynDialBootnode(t *testing.T) { 219 bootnodes := []*discover.Node{ 220 {ID: uintID(1)}, 221 {ID: uintID(2)}, 222 {ID: uintID(3)}, 223 } 224 table := fakeTable{ 225 {ID: uintID(4)}, 226 {ID: uintID(5)}, 227 {ID: uintID(6)}, 228 {ID: uintID(7)}, 229 {ID: uintID(8)}, 230 } 231 runDialTest(t, dialtest{ 232 init: newDialState(nil, bootnodes, table, 5, nil), 233 rounds: []round{ 234 // 2 dynamic dials attempted, bootnodes pending fallback interval 235 { 236 new: []task{ 237 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, 238 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, 239 &discoverTask{}, 240 }, 241 }, 242 // No dials succeed, bootnodes still pending fallback interval 243 { 244 done: []task{ 245 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, 246 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, 247 }, 248 }, 249 // No dials succeed, bootnodes still pending fallback interval 250 {}, 251 // No dials succeed, 2 dynamic dials attempted and 1 bootnode too as fallback interval was reached 252 { 253 new: []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 }, 259 // No dials succeed, 2nd bootnode is attempted 260 { 261 done: []task{ 262 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}}, 263 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, 264 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, 265 }, 266 new: []task{ 267 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}}, 268 }, 269 }, 270 // No dials succeed, 3rd bootnode is attempted 271 { 272 done: []task{ 273 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}}, 274 }, 275 new: []task{ 276 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}}, 277 }, 278 }, 279 // No dials succeed, 1st bootnode is attempted again, expired random nodes retried 280 { 281 done: []task{ 282 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}}, 283 }, 284 new: []task{ 285 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}}, 286 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, 287 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, 288 }, 289 }, 290 // Random dial succeeds, no more bootnodes are attempted 291 { 292 peers: []*Peer{ 293 {rw: &conn{flags: dynDialedConn, id: uintID(4)}}, 294 }, 295 done: []task{ 296 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}}, 297 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, 298 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, 299 }, 300 }, 301 }, 302 }) 303 } 304 305 func TestDialStateDynDialFromTable(t *testing.T) { 306 // This table always returns the same random nodes 307 // in the order given below. 308 table := fakeTable{ 309 {ID: uintID(1)}, 310 {ID: uintID(2)}, 311 {ID: uintID(3)}, 312 {ID: uintID(4)}, 313 {ID: uintID(5)}, 314 {ID: uintID(6)}, 315 {ID: uintID(7)}, 316 {ID: uintID(8)}, 317 } 318 319 runDialTest(t, dialtest{ 320 init: newDialState(nil, nil, table, 10, nil), 321 rounds: []round{ 322 // 5 out of 8 of the nodes returned by ReadRandomNodes are dialed. 323 { 324 new: []task{ 325 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}}, 326 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}}, 327 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}}, 328 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, 329 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, 330 &discoverTask{}, 331 }, 332 }, 333 // Dialing nodes 1,2 succeeds. Dials from the lookup are launched. 334 { 335 peers: []*Peer{ 336 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 337 {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, 338 }, 339 done: []task{ 340 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}}, 341 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(2)}}, 342 &discoverTask{results: []*discover.Node{ 343 {ID: uintID(10)}, 344 {ID: uintID(11)}, 345 {ID: uintID(12)}, 346 }}, 347 }, 348 new: []task{ 349 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}}, 350 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}}, 351 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}}, 352 &discoverTask{}, 353 }, 354 }, 355 // Dialing nodes 3,4,5 fails. The dials from the lookup succeed. 356 { 357 peers: []*Peer{ 358 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 359 {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, 360 {rw: &conn{flags: dynDialedConn, id: uintID(10)}}, 361 {rw: &conn{flags: dynDialedConn, id: uintID(11)}}, 362 {rw: &conn{flags: dynDialedConn, id: uintID(12)}}, 363 }, 364 done: []task{ 365 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(3)}}, 366 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, 367 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, 368 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(10)}}, 369 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(11)}}, 370 &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(12)}}, 371 }, 372 }, 373 // Waiting for expiry. No waitExpireTask is launched because the 374 // discovery query is still running. 375 { 376 peers: []*Peer{ 377 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 378 {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, 379 {rw: &conn{flags: dynDialedConn, id: uintID(10)}}, 380 {rw: &conn{flags: dynDialedConn, id: uintID(11)}}, 381 {rw: &conn{flags: dynDialedConn, id: uintID(12)}}, 382 }, 383 }, 384 // Nodes 3,4 are not tried again because only the first two 385 // returned random nodes (nodes 1,2) are tried and they're 386 // already connected. 387 { 388 peers: []*Peer{ 389 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 390 {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, 391 {rw: &conn{flags: dynDialedConn, id: uintID(10)}}, 392 {rw: &conn{flags: dynDialedConn, id: uintID(11)}}, 393 {rw: &conn{flags: dynDialedConn, id: uintID(12)}}, 394 }, 395 }, 396 }, 397 }) 398 } 399 400 // This test checks that candidates that do not match the netrestrict list are not dialed. 401 func TestDialStateNetRestrict(t *testing.T) { 402 // This table always returns the same random nodes 403 // in the order given below. 404 table := fakeTable{ 405 {ID: uintID(1), IP: net.ParseIP("127.0.0.1")}, 406 {ID: uintID(2), IP: net.ParseIP("127.0.0.2")}, 407 {ID: uintID(3), IP: net.ParseIP("127.0.0.3")}, 408 {ID: uintID(4), IP: net.ParseIP("127.0.0.4")}, 409 {ID: uintID(5), IP: net.ParseIP("127.0.2.5")}, 410 {ID: uintID(6), IP: net.ParseIP("127.0.2.6")}, 411 {ID: uintID(7), IP: net.ParseIP("127.0.2.7")}, 412 {ID: uintID(8), IP: net.ParseIP("127.0.2.8")}, 413 } 414 restrict := new(netutil.Netlist) 415 restrict.Add("127.0.2.0/24") 416 417 runDialTest(t, dialtest{ 418 init: newDialState(nil, nil, table, 10, restrict), 419 rounds: []round{ 420 { 421 new: []task{ 422 &dialTask{flags: dynDialedConn, dest: table[4]}, 423 &discoverTask{}, 424 }, 425 }, 426 }, 427 }) 428 } 429 430 // This test checks that static dials are launched. 431 func TestDialStateStaticDial(t *testing.T) { 432 wantStatic := []*discover.Node{ 433 {ID: uintID(1)}, 434 {ID: uintID(2)}, 435 {ID: uintID(3)}, 436 {ID: uintID(4)}, 437 {ID: uintID(5)}, 438 } 439 440 runDialTest(t, dialtest{ 441 init: newDialState(wantStatic, nil, fakeTable{}, 0, nil), 442 rounds: []round{ 443 // Static dials are launched for the nodes that 444 // aren't yet connected. 445 { 446 peers: []*Peer{ 447 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 448 {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, 449 }, 450 new: []task{ 451 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}}, 452 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}}, 453 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}}, 454 }, 455 }, 456 // No new tasks are launched in this round because all static 457 // nodes are either connected or still being dialed. 458 { 459 peers: []*Peer{ 460 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 461 {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, 462 {rw: &conn{flags: staticDialedConn, id: uintID(3)}}, 463 }, 464 done: []task{ 465 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}}, 466 }, 467 }, 468 // No new dial tasks are launched because all static 469 // nodes are now connected. 470 { 471 peers: []*Peer{ 472 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 473 {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, 474 {rw: &conn{flags: staticDialedConn, id: uintID(3)}}, 475 {rw: &conn{flags: staticDialedConn, id: uintID(4)}}, 476 {rw: &conn{flags: staticDialedConn, id: uintID(5)}}, 477 }, 478 done: []task{ 479 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}}, 480 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(5)}}, 481 }, 482 new: []task{ 483 &waitExpireTask{Duration: 14 * time.Second}, 484 }, 485 }, 486 // Wait a round for dial history to expire, no new tasks should spawn. 487 { 488 peers: []*Peer{ 489 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 490 {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, 491 {rw: &conn{flags: staticDialedConn, id: uintID(3)}}, 492 {rw: &conn{flags: staticDialedConn, id: uintID(4)}}, 493 {rw: &conn{flags: staticDialedConn, id: uintID(5)}}, 494 }, 495 }, 496 // If a static node is dropped, it should be immediately redialed, 497 // irrespective whether it was originally static or dynamic. 498 { 499 peers: []*Peer{ 500 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 501 {rw: &conn{flags: staticDialedConn, id: uintID(3)}}, 502 {rw: &conn{flags: staticDialedConn, id: uintID(5)}}, 503 }, 504 new: []task{ 505 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}}, 506 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(4)}}, 507 }, 508 }, 509 }, 510 }) 511 } 512 513 // This test checks that static peers will be redialed immediately if they were re-added to a static list. 514 func TestDialStaticAfterReset(t *testing.T) { 515 wantStatic := []*discover.Node{ 516 {ID: uintID(1)}, 517 {ID: uintID(2)}, 518 } 519 520 rounds := []round{ 521 // Static dials are launched for the nodes that aren't yet connected. 522 { 523 peers: nil, 524 new: []task{ 525 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}}, 526 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}}, 527 }, 528 }, 529 // No new dial tasks, all peers are connected. 530 { 531 peers: []*Peer{ 532 {rw: &conn{flags: staticDialedConn, id: uintID(1)}}, 533 {rw: &conn{flags: staticDialedConn, id: uintID(2)}}, 534 }, 535 done: []task{ 536 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}}, 537 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}}, 538 }, 539 new: []task{ 540 &waitExpireTask{Duration: 30 * time.Second}, 541 }, 542 }, 543 } 544 dTest := dialtest{ 545 init: newDialState(wantStatic, nil, fakeTable{}, 0, nil), 546 rounds: rounds, 547 } 548 runDialTest(t, dTest) 549 for _, n := range wantStatic { 550 dTest.init.removeStatic(n) 551 dTest.init.addStatic(n) 552 } 553 // without removing peers they will be considered recently dialed 554 runDialTest(t, dTest) 555 } 556 557 // This test checks that past dials are not retried for some time. 558 func TestDialStateCache(t *testing.T) { 559 wantStatic := []*discover.Node{ 560 {ID: uintID(1)}, 561 {ID: uintID(2)}, 562 {ID: uintID(3)}, 563 } 564 565 runDialTest(t, dialtest{ 566 init: newDialState(wantStatic, nil, fakeTable{}, 0, nil), 567 rounds: []round{ 568 // Static dials are launched for the nodes that 569 // aren't yet connected. 570 { 571 peers: nil, 572 new: []task{ 573 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}}, 574 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}}, 575 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}}, 576 }, 577 }, 578 // No new tasks are launched in this round because all static 579 // nodes are either connected or still being dialed. 580 { 581 peers: []*Peer{ 582 {rw: &conn{flags: staticDialedConn, id: uintID(1)}}, 583 {rw: &conn{flags: staticDialedConn, id: uintID(2)}}, 584 }, 585 done: []task{ 586 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(1)}}, 587 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(2)}}, 588 }, 589 }, 590 // A salvage task is launched to wait for node 3's history 591 // entry to expire. 592 { 593 peers: []*Peer{ 594 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 595 {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, 596 }, 597 done: []task{ 598 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}}, 599 }, 600 new: []task{ 601 &waitExpireTask{Duration: 14 * time.Second}, 602 }, 603 }, 604 // Still waiting for node 3's entry to expire in the cache. 605 { 606 peers: []*Peer{ 607 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 608 {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, 609 }, 610 }, 611 // The cache entry for node 3 has expired and is retried. 612 { 613 peers: []*Peer{ 614 {rw: &conn{flags: dynDialedConn, id: uintID(1)}}, 615 {rw: &conn{flags: dynDialedConn, id: uintID(2)}}, 616 }, 617 new: []task{ 618 &dialTask{flags: staticDialedConn, dest: &discover.Node{ID: uintID(3)}}, 619 }, 620 }, 621 }, 622 }) 623 } 624 625 func TestDialResolve(t *testing.T) { 626 resolved := discover.NewNode(uintID(1), net.IP{127, 0, 55, 234}, 3333, 4444) 627 table := &resolveMock{answer: resolved} 628 state := newDialState(nil, nil, table, 0, nil) 629 630 // Check that the task is generated with an incomplete ID. 631 dest := discover.NewNode(uintID(1), nil, 0, 0) 632 state.addStatic(dest) 633 tasks := state.newTasks(0, nil, time.Time{}) 634 if !reflect.DeepEqual(tasks, []task{&dialTask{flags: staticDialedConn, dest: dest}}) { 635 t.Fatalf("expected dial task, got %#v", tasks) 636 } 637 638 // Now run the task, it should resolve the ID once. 639 config := Config{Dialer: TCPDialer{&net.Dialer{Deadline: time.Now().Add(-5 * time.Minute)}}} 640 srv := &Server{ntab: table, Config: config} 641 tasks[0].Do(srv) 642 if !reflect.DeepEqual(table.resolveCalls, []discover.NodeID{dest.ID}) { 643 t.Fatalf("wrong resolve calls, got %v", table.resolveCalls) 644 } 645 646 // Report it as done to the dialer, which should update the static node record. 647 state.taskDone(tasks[0], time.Now()) 648 if state.static[uintID(1)].dest != resolved { 649 t.Fatalf("state.dest not updated") 650 } 651 } 652 653 // compares task lists but doesn't care about the order. 654 func sametasks(a, b []task) bool { 655 if len(a) != len(b) { 656 return false 657 } 658 next: 659 for _, ta := range a { 660 for _, tb := range b { 661 if reflect.DeepEqual(ta, tb) { 662 continue next 663 } 664 } 665 return false 666 } 667 return true 668 } 669 670 func uintID(i uint32) discover.NodeID { 671 var id discover.NodeID 672 binary.BigEndian.PutUint32(id[:], i) 673 return id 674 } 675 676 // implements discoverTable for TestDialResolve 677 type resolveMock struct { 678 resolveCalls []discover.NodeID 679 answer *discover.Node 680 } 681 682 func (t *resolveMock) Resolve(id discover.NodeID) *discover.Node { 683 t.resolveCalls = append(t.resolveCalls, id) 684 return t.answer 685 } 686 687 func (t *resolveMock) Self() *discover.Node { return new(discover.Node) } 688 func (t *resolveMock) Close() {} 689 func (t *resolveMock) Bootstrap([]*discover.Node) {} 690 func (t *resolveMock) Lookup(discover.NodeID) []*discover.Node { return nil } 691 func (t *resolveMock) ReadRandomNodes(buf []*discover.Node) int { return 0 }