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