github.com/ethereum/go-ethereum@v1.16.1/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 "context" 21 "errors" 22 "fmt" 23 "math/rand" 24 "net" 25 "net/netip" 26 "reflect" 27 "sync" 28 "testing" 29 "time" 30 31 "github.com/ethereum/go-ethereum/common/mclock" 32 "github.com/ethereum/go-ethereum/internal/testlog" 33 "github.com/ethereum/go-ethereum/log" 34 "github.com/ethereum/go-ethereum/p2p/enode" 35 "github.com/ethereum/go-ethereum/p2p/netutil" 36 ) 37 38 // This test checks that dynamic dials are launched from discovery results. 39 func TestDialSchedDynDial(t *testing.T) { 40 t.Parallel() 41 42 config := dialConfig{ 43 maxActiveDials: 5, 44 maxDialPeers: 4, 45 } 46 runDialTest(t, config, []dialTestRound{ 47 // 3 out of 4 peers are connected, leaving 2 dial slots. 48 // 9 nodes are discovered, but only 2 are dialed. 49 { 50 peersAdded: []*conn{ 51 {flags: staticDialedConn, node: newNode(uintID(0x00), "")}, 52 {flags: dynDialedConn, node: newNode(uintID(0x01), "")}, 53 {flags: dynDialedConn, node: newNode(uintID(0x02), "")}, 54 }, 55 discovered: []*enode.Node{ 56 newNode(uintID(0x00), "127.0.0.1:30303"), // not dialed because already connected as static peer 57 newNode(uintID(0x02), "127.0.0.1:30303"), // ... 58 newNode(uintID(0x03), "127.0.0.1:30303"), 59 newNode(uintID(0x04), "127.0.0.1:30303"), 60 newNode(uintID(0x05), "127.0.0.1:30303"), // not dialed because there are only two slots 61 newNode(uintID(0x06), "127.0.0.1:30303"), // ... 62 newNode(uintID(0x07), "127.0.0.1:30303"), // ... 63 newNode(uintID(0x08), "127.0.0.1:30303"), // ... 64 }, 65 wantNewDials: []*enode.Node{ 66 newNode(uintID(0x03), "127.0.0.1:30303"), 67 newNode(uintID(0x04), "127.0.0.1:30303"), 68 }, 69 }, 70 71 // One dial completes, freeing one dial slot. 72 { 73 failed: []enode.ID{ 74 uintID(0x04), 75 }, 76 wantNewDials: []*enode.Node{ 77 newNode(uintID(0x05), "127.0.0.1:30303"), 78 }, 79 }, 80 81 // Dial to 0x03 completes, filling the last remaining peer slot. 82 { 83 succeeded: []enode.ID{ 84 uintID(0x03), 85 }, 86 failed: []enode.ID{ 87 uintID(0x05), 88 }, 89 discovered: []*enode.Node{ 90 newNode(uintID(0x09), "127.0.0.1:30303"), // not dialed because there are no free slots 91 }, 92 }, 93 94 // 3 peers drop off, creating 6 dial slots. Check that 5 of those slots 95 // (i.e. up to maxActiveDialTasks) are used. 96 { 97 peersRemoved: []enode.ID{ 98 uintID(0x00), 99 uintID(0x01), 100 uintID(0x02), 101 }, 102 discovered: []*enode.Node{ 103 newNode(uintID(0x0a), "127.0.0.1:30303"), 104 newNode(uintID(0x0b), "127.0.0.1:30303"), 105 newNode(uintID(0x0c), "127.0.0.1:30303"), 106 newNode(uintID(0x0d), "127.0.0.1:30303"), 107 newNode(uintID(0x0f), "127.0.0.1:30303"), 108 }, 109 wantNewDials: []*enode.Node{ 110 newNode(uintID(0x06), "127.0.0.1:30303"), 111 newNode(uintID(0x07), "127.0.0.1:30303"), 112 newNode(uintID(0x08), "127.0.0.1:30303"), 113 newNode(uintID(0x09), "127.0.0.1:30303"), 114 newNode(uintID(0x0a), "127.0.0.1:30303"), 115 }, 116 }, 117 }) 118 } 119 120 // This test checks that candidates that do not match the netrestrict list are not dialed. 121 func TestDialSchedNetRestrict(t *testing.T) { 122 t.Parallel() 123 124 nodes := []*enode.Node{ 125 newNode(uintID(0x01), "127.0.0.1:30303"), 126 newNode(uintID(0x02), "127.0.0.2:30303"), 127 newNode(uintID(0x03), "127.0.0.3:30303"), 128 newNode(uintID(0x04), "127.0.0.4:30303"), 129 newNode(uintID(0x05), "127.0.2.5:30303"), 130 newNode(uintID(0x06), "127.0.2.6:30303"), 131 newNode(uintID(0x07), "127.0.2.7:30303"), 132 newNode(uintID(0x08), "127.0.2.8:30303"), 133 } 134 config := dialConfig{ 135 netRestrict: new(netutil.Netlist), 136 maxActiveDials: 10, 137 maxDialPeers: 10, 138 } 139 config.netRestrict.Add("127.0.2.0/24") 140 runDialTest(t, config, []dialTestRound{ 141 { 142 discovered: nodes, 143 wantNewDials: nodes[4:8], 144 }, 145 { 146 succeeded: []enode.ID{ 147 nodes[4].ID(), 148 nodes[5].ID(), 149 nodes[6].ID(), 150 nodes[7].ID(), 151 }, 152 }, 153 }) 154 } 155 156 // This test checks that static dials work and obey the limits. 157 func TestDialSchedStaticDial(t *testing.T) { 158 t.Parallel() 159 160 config := dialConfig{ 161 maxActiveDials: 5, 162 maxDialPeers: 4, 163 } 164 runDialTest(t, config, []dialTestRound{ 165 // Static dials are launched for the nodes that 166 // aren't yet connected. 167 { 168 peersAdded: []*conn{ 169 {flags: dynDialedConn, node: newNode(uintID(0x01), "127.0.0.1:30303")}, 170 {flags: dynDialedConn, node: newNode(uintID(0x02), "127.0.0.2:30303")}, 171 }, 172 update: func(d *dialScheduler) { 173 // These two are not dialed because they're already connected 174 // as dynamic peers. 175 d.addStatic(newNode(uintID(0x01), "127.0.0.1:30303")) 176 d.addStatic(newNode(uintID(0x02), "127.0.0.2:30303")) 177 // These nodes will be dialed: 178 d.addStatic(newNode(uintID(0x03), "127.0.0.3:30303")) 179 d.addStatic(newNode(uintID(0x04), "127.0.0.4:30303")) 180 d.addStatic(newNode(uintID(0x05), "127.0.0.5:30303")) 181 d.addStatic(newNode(uintID(0x06), "127.0.0.6:30303")) 182 d.addStatic(newNode(uintID(0x07), "127.0.0.7:30303")) 183 d.addStatic(newNode(uintID(0x08), "127.0.0.8:30303")) 184 d.addStatic(newNode(uintID(0x09), "127.0.0.9:30303")) 185 }, 186 wantNewDials: []*enode.Node{ 187 newNode(uintID(0x03), "127.0.0.3:30303"), 188 newNode(uintID(0x04), "127.0.0.4:30303"), 189 newNode(uintID(0x05), "127.0.0.5:30303"), 190 newNode(uintID(0x06), "127.0.0.6:30303"), 191 }, 192 }, 193 // Dial to 0x03 completes, filling a peer slot. One slot remains, 194 // two dials are launched to attempt to fill it. 195 { 196 succeeded: []enode.ID{ 197 uintID(0x03), 198 }, 199 failed: []enode.ID{ 200 uintID(0x04), 201 uintID(0x05), 202 uintID(0x06), 203 }, 204 wantResolves: map[enode.ID]*enode.Node{ 205 uintID(0x04): nil, 206 uintID(0x05): nil, 207 uintID(0x06): nil, 208 }, 209 wantNewDials: []*enode.Node{ 210 newNode(uintID(0x08), "127.0.0.8:30303"), 211 newNode(uintID(0x09), "127.0.0.9:30303"), 212 }, 213 }, 214 // Peer 0x01 drops and 0x07 connects as inbound peer. 215 // Only 0x01 is dialed. 216 { 217 peersAdded: []*conn{ 218 {flags: inboundConn, node: newNode(uintID(0x07), "127.0.0.7:30303")}, 219 }, 220 peersRemoved: []enode.ID{ 221 uintID(0x01), 222 }, 223 wantNewDials: []*enode.Node{ 224 newNode(uintID(0x01), "127.0.0.1:30303"), 225 }, 226 }, 227 }) 228 } 229 230 // This test checks that removing static nodes stops connecting to them. 231 func TestDialSchedRemoveStatic(t *testing.T) { 232 t.Parallel() 233 234 config := dialConfig{ 235 maxActiveDials: 1, 236 maxDialPeers: 1, 237 } 238 runDialTest(t, config, []dialTestRound{ 239 // Add static nodes. 240 { 241 update: func(d *dialScheduler) { 242 d.addStatic(newNode(uintID(0x01), "127.0.0.1:30303")) 243 d.addStatic(newNode(uintID(0x02), "127.0.0.2:30303")) 244 d.addStatic(newNode(uintID(0x03), "127.0.0.3:30303")) 245 }, 246 wantNewDials: []*enode.Node{ 247 newNode(uintID(0x01), "127.0.0.1:30303"), 248 }, 249 }, 250 // Dial to 0x01 fails. 251 { 252 failed: []enode.ID{ 253 uintID(0x01), 254 }, 255 wantResolves: map[enode.ID]*enode.Node{ 256 uintID(0x01): nil, 257 }, 258 wantNewDials: []*enode.Node{ 259 newNode(uintID(0x02), "127.0.0.2:30303"), 260 }, 261 }, 262 // All static nodes are removed. 0x01 is in history, 0x02 is being 263 // dialed, 0x03 is in staticPool. 264 { 265 update: func(d *dialScheduler) { 266 d.removeStatic(newNode(uintID(0x01), "127.0.0.1:30303")) 267 d.removeStatic(newNode(uintID(0x02), "127.0.0.2:30303")) 268 d.removeStatic(newNode(uintID(0x03), "127.0.0.3:30303")) 269 }, 270 failed: []enode.ID{ 271 uintID(0x02), 272 }, 273 wantResolves: map[enode.ID]*enode.Node{ 274 uintID(0x02): nil, 275 }, 276 }, 277 // Since all static nodes are removed, they should not be dialed again. 278 {}, {}, {}, 279 }) 280 } 281 282 // This test checks that static dials are selected at random. 283 func TestDialSchedManyStaticNodes(t *testing.T) { 284 t.Parallel() 285 286 config := dialConfig{maxDialPeers: 2} 287 runDialTest(t, config, []dialTestRound{ 288 { 289 peersAdded: []*conn{ 290 {flags: dynDialedConn, node: newNode(uintID(0xFFFE), "")}, 291 {flags: dynDialedConn, node: newNode(uintID(0xFFFF), "")}, 292 }, 293 update: func(d *dialScheduler) { 294 for id := uint16(0); id < 2000; id++ { 295 n := newNode(uintID(id), "127.0.0.1:30303") 296 d.addStatic(n) 297 } 298 }, 299 }, 300 { 301 peersRemoved: []enode.ID{ 302 uintID(0xFFFE), 303 uintID(0xFFFF), 304 }, 305 wantNewDials: []*enode.Node{ 306 newNode(uintID(0x0085), "127.0.0.1:30303"), 307 newNode(uintID(0x02dc), "127.0.0.1:30303"), 308 newNode(uintID(0x0285), "127.0.0.1:30303"), 309 newNode(uintID(0x00cb), "127.0.0.1:30303"), 310 }, 311 }, 312 }) 313 } 314 315 // This test checks that past dials are not retried for some time. 316 func TestDialSchedHistory(t *testing.T) { 317 t.Parallel() 318 319 config := dialConfig{ 320 maxActiveDials: 3, 321 maxDialPeers: 3, 322 } 323 runDialTest(t, config, []dialTestRound{ 324 { 325 update: func(d *dialScheduler) { 326 d.addStatic(newNode(uintID(0x01), "127.0.0.1:30303")) 327 d.addStatic(newNode(uintID(0x02), "127.0.0.2:30303")) 328 d.addStatic(newNode(uintID(0x03), "127.0.0.3:30303")) 329 }, 330 wantNewDials: []*enode.Node{ 331 newNode(uintID(0x01), "127.0.0.1:30303"), 332 newNode(uintID(0x02), "127.0.0.2:30303"), 333 newNode(uintID(0x03), "127.0.0.3:30303"), 334 }, 335 }, 336 // No new tasks are launched in this round because all static 337 // nodes are either connected or still being dialed. 338 { 339 succeeded: []enode.ID{ 340 uintID(0x01), 341 uintID(0x02), 342 }, 343 failed: []enode.ID{ 344 uintID(0x03), 345 }, 346 wantResolves: map[enode.ID]*enode.Node{ 347 uintID(0x03): nil, 348 }, 349 }, 350 // Nothing happens in this round because we're waiting for 351 // node 0x3's history entry to expire. 352 {}, 353 // The cache entry for node 0x03 has expired and is retried. 354 { 355 wantNewDials: []*enode.Node{ 356 newNode(uintID(0x03), "127.0.0.3:30303"), 357 }, 358 }, 359 }) 360 } 361 362 func TestDialSchedResolve(t *testing.T) { 363 t.Parallel() 364 365 config := dialConfig{ 366 maxActiveDials: 1, 367 maxDialPeers: 1, 368 } 369 node := newNode(uintID(0x01), "") 370 resolved := newNode(uintID(0x01), "127.0.0.1:30303") 371 resolved2 := newNode(uintID(0x01), "127.0.0.55:30303") 372 runDialTest(t, config, []dialTestRound{ 373 { 374 update: func(d *dialScheduler) { 375 d.addStatic(node) 376 }, 377 wantResolves: map[enode.ID]*enode.Node{ 378 uintID(0x01): resolved, 379 }, 380 wantNewDials: []*enode.Node{ 381 resolved, 382 }, 383 }, 384 { 385 failed: []enode.ID{ 386 uintID(0x01), 387 }, 388 wantResolves: map[enode.ID]*enode.Node{ 389 uintID(0x01): resolved2, 390 }, 391 wantNewDials: []*enode.Node{ 392 resolved2, 393 }, 394 }, 395 }) 396 } 397 398 func TestDialSchedDNSHostname(t *testing.T) { 399 t.Parallel() 400 401 config := dialConfig{ 402 maxActiveDials: 1, 403 maxDialPeers: 1, 404 } 405 node := newNode(uintID(0x01), ":30303").WithHostname("node-hostname") 406 resolved := newNode(uintID(0x01), "1.2.3.4:30303").WithHostname("node-hostname") 407 runDialTest(t, config, []dialTestRound{ 408 { 409 update: func(d *dialScheduler) { 410 d.dnsLookupFunc = func(ctx context.Context, network string, name string) ([]netip.Addr, error) { 411 if name != "node-hostname" { 412 t.Error("wrong hostname in DNS lookup:", name) 413 } 414 result := []netip.Addr{netip.MustParseAddr("1.2.3.4")} 415 return result, nil 416 } 417 d.addStatic(node) 418 }, 419 wantNewDials: []*enode.Node{ 420 resolved, 421 }, 422 }, 423 }) 424 } 425 426 // ------- 427 // Code below here is the framework for the tests above. 428 429 type dialTestRound struct { 430 peersAdded []*conn 431 peersRemoved []enode.ID 432 update func(*dialScheduler) // called at beginning of round 433 discovered []*enode.Node // newly discovered nodes 434 succeeded []enode.ID // dials which succeed this round 435 failed []enode.ID // dials which fail this round 436 wantResolves map[enode.ID]*enode.Node 437 wantNewDials []*enode.Node // dials that should be launched in this round 438 } 439 440 func runDialTest(t *testing.T, config dialConfig, rounds []dialTestRound) { 441 var ( 442 clock = new(mclock.Simulated) 443 iterator = newDialTestIterator() 444 dialer = newDialTestDialer() 445 resolver = new(dialTestResolver) 446 peers = make(map[enode.ID]*conn) 447 setupCh = make(chan *conn) 448 ) 449 450 // Override config. 451 config.clock = clock 452 config.dialer = dialer 453 config.resolver = resolver 454 config.log = testlog.Logger(t, log.LvlTrace) 455 config.rand = rand.New(rand.NewSource(0x1111)) 456 457 // Set up the dialer. The setup function below runs on the dialTask 458 // goroutine and adds the peer. 459 var dialsched *dialScheduler 460 setup := func(fd net.Conn, f connFlag, node *enode.Node) error { 461 conn := &conn{flags: f, node: node} 462 dialsched.peerAdded(conn) 463 setupCh <- conn 464 return nil 465 } 466 dialsched = newDialScheduler(config, iterator, setup) 467 defer dialsched.stop() 468 469 for i, round := range rounds { 470 // Apply peer set updates. 471 for _, c := range round.peersAdded { 472 if peers[c.node.ID()] != nil { 473 t.Fatalf("round %d: peer %v already connected", i, c.node.ID()) 474 } 475 dialsched.peerAdded(c) 476 peers[c.node.ID()] = c 477 } 478 for _, id := range round.peersRemoved { 479 c := peers[id] 480 if c == nil { 481 t.Fatalf("round %d: can't remove non-existent peer %v", i, id) 482 } 483 dialsched.peerRemoved(c) 484 } 485 486 // Init round. 487 t.Logf("round %d (%d peers)", i, len(peers)) 488 resolver.setAnswers(round.wantResolves) 489 if round.update != nil { 490 round.update(dialsched) 491 } 492 iterator.addNodes(round.discovered) 493 494 // Unblock dialTask goroutines. 495 if err := dialer.completeDials(round.succeeded, nil); err != nil { 496 t.Fatalf("round %d: %v", i, err) 497 } 498 for range round.succeeded { 499 conn := <-setupCh 500 peers[conn.node.ID()] = conn 501 } 502 if err := dialer.completeDials(round.failed, errors.New("oops")); err != nil { 503 t.Fatalf("round %d: %v", i, err) 504 } 505 506 // Wait for new tasks. 507 if err := dialer.waitForDials(round.wantNewDials); err != nil { 508 t.Fatalf("round %d: %v", i, err) 509 } 510 if !resolver.checkCalls() { 511 t.Fatalf("unexpected calls to Resolve: %v", resolver.calls) 512 } 513 514 clock.Run(16 * time.Second) 515 } 516 } 517 518 // dialTestIterator is the input iterator for dialer tests. This works a bit like a channel 519 // with infinite buffer: nodes are added to the buffer with addNodes, which unblocks Next 520 // and returns them from the iterator. 521 type dialTestIterator struct { 522 cur *enode.Node 523 524 mu sync.Mutex 525 buf []*enode.Node 526 cond *sync.Cond 527 closed bool 528 } 529 530 func newDialTestIterator() *dialTestIterator { 531 it := &dialTestIterator{} 532 it.cond = sync.NewCond(&it.mu) 533 return it 534 } 535 536 // addNodes adds nodes to the iterator buffer and unblocks Next. 537 func (it *dialTestIterator) addNodes(nodes []*enode.Node) { 538 it.mu.Lock() 539 defer it.mu.Unlock() 540 541 it.buf = append(it.buf, nodes...) 542 it.cond.Signal() 543 } 544 545 // Node returns the current node. 546 func (it *dialTestIterator) Node() *enode.Node { 547 return it.cur 548 } 549 550 // Next moves to the next node. 551 func (it *dialTestIterator) Next() bool { 552 it.mu.Lock() 553 defer it.mu.Unlock() 554 555 it.cur = nil 556 for len(it.buf) == 0 && !it.closed { 557 it.cond.Wait() 558 } 559 if it.closed { 560 return false 561 } 562 it.cur = it.buf[0] 563 copy(it.buf[:], it.buf[1:]) 564 it.buf = it.buf[:len(it.buf)-1] 565 return true 566 } 567 568 // Close ends the iterator, unblocking Next. 569 func (it *dialTestIterator) Close() { 570 it.mu.Lock() 571 defer it.mu.Unlock() 572 573 it.closed = true 574 it.buf = nil 575 it.cond.Signal() 576 } 577 578 // dialTestDialer is the NodeDialer used by runDialTest. 579 type dialTestDialer struct { 580 init chan *dialTestReq 581 blocked map[enode.ID]*dialTestReq 582 } 583 584 type dialTestReq struct { 585 n *enode.Node 586 unblock chan error 587 } 588 589 func newDialTestDialer() *dialTestDialer { 590 return &dialTestDialer{ 591 init: make(chan *dialTestReq), 592 blocked: make(map[enode.ID]*dialTestReq), 593 } 594 } 595 596 // Dial implements NodeDialer. 597 func (d *dialTestDialer) Dial(ctx context.Context, n *enode.Node) (net.Conn, error) { 598 req := &dialTestReq{n: n, unblock: make(chan error, 1)} 599 select { 600 case d.init <- req: 601 select { 602 case err := <-req.unblock: 603 pipe, _ := net.Pipe() 604 return pipe, err 605 case <-ctx.Done(): 606 return nil, ctx.Err() 607 } 608 case <-ctx.Done(): 609 return nil, ctx.Err() 610 } 611 } 612 613 // waitForDials waits for calls to Dial with the given nodes as argument. 614 // Those calls will be held blocking until completeDials is called with the same nodes. 615 func (d *dialTestDialer) waitForDials(nodes []*enode.Node) error { 616 waitset := make(map[enode.ID]*enode.Node, len(nodes)) 617 for _, n := range nodes { 618 waitset[n.ID()] = n 619 } 620 timeout := time.NewTimer(1 * time.Second) 621 defer timeout.Stop() 622 623 for len(waitset) > 0 { 624 select { 625 case req := <-d.init: 626 want, ok := waitset[req.n.ID()] 627 if !ok { 628 return fmt.Errorf("attempt to dial unexpected node %v", req.n.ID()) 629 } 630 if !reflect.DeepEqual(req.n, want) { 631 return fmt.Errorf("ENR of dialed node %v does not match test", req.n.ID()) 632 } 633 delete(waitset, req.n.ID()) 634 d.blocked[req.n.ID()] = req 635 case <-timeout.C: 636 var waitlist []enode.ID 637 for id := range waitset { 638 waitlist = append(waitlist, id) 639 } 640 return fmt.Errorf("timed out waiting for dials to %v", waitlist) 641 } 642 } 643 644 return d.checkUnexpectedDial() 645 } 646 647 func (d *dialTestDialer) checkUnexpectedDial() error { 648 select { 649 case req := <-d.init: 650 return fmt.Errorf("attempt to dial unexpected node %v", req.n.ID()) 651 case <-time.After(150 * time.Millisecond): 652 return nil 653 } 654 } 655 656 // completeDials unblocks calls to Dial for the given nodes. 657 func (d *dialTestDialer) completeDials(ids []enode.ID, err error) error { 658 for _, id := range ids { 659 req := d.blocked[id] 660 if req == nil { 661 return fmt.Errorf("can't complete dial to %v", id) 662 } 663 req.unblock <- err 664 } 665 return nil 666 } 667 668 // dialTestResolver tracks calls to resolve. 669 type dialTestResolver struct { 670 mu sync.Mutex 671 calls []enode.ID 672 answers map[enode.ID]*enode.Node 673 } 674 675 func (t *dialTestResolver) setAnswers(m map[enode.ID]*enode.Node) { 676 t.mu.Lock() 677 defer t.mu.Unlock() 678 679 t.answers = m 680 t.calls = nil 681 } 682 683 func (t *dialTestResolver) checkCalls() bool { 684 t.mu.Lock() 685 defer t.mu.Unlock() 686 687 for _, id := range t.calls { 688 if _, ok := t.answers[id]; !ok { 689 return false 690 } 691 } 692 return true 693 } 694 695 func (t *dialTestResolver) Resolve(n *enode.Node) *enode.Node { 696 t.mu.Lock() 697 defer t.mu.Unlock() 698 699 t.calls = append(t.calls, n.ID()) 700 return t.answers[n.ID()] 701 }