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