github.com/core-coin/go-core/v2@v2.1.9/p2p/discover/v5_udp_test.go (about) 1 // Copyright 2020 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package discover 18 19 import ( 20 "bytes" 21 "encoding/binary" 22 "fmt" 23 "math/rand" 24 "net" 25 "reflect" 26 "sort" 27 "testing" 28 "time" 29 30 "github.com/core-coin/go-core/v2/crypto" 31 "github.com/core-coin/go-core/v2/internal/testlog" 32 "github.com/core-coin/go-core/v2/log" 33 "github.com/core-coin/go-core/v2/p2p/discover/v5wire" 34 "github.com/core-coin/go-core/v2/p2p/enode" 35 "github.com/core-coin/go-core/v2/p2p/enr" 36 "github.com/core-coin/go-core/v2/rlp" 37 ) 38 39 // Real sockets, real crypto: this test checks end-to-end connectivity for UDPv5. 40 func TestUDPv5_lookupE2E(t *testing.T) { 41 t.Parallel() 42 43 const N = 5 44 var nodes []*UDPv5 45 for i := 0; i < N; i++ { 46 var cfg Config 47 if len(nodes) > 0 { 48 bn := nodes[0].Self() 49 cfg.Bootnodes = []*enode.Node{bn} 50 } 51 node := startLocalhostV5(t, cfg) 52 nodes = append(nodes, node) 53 defer node.Close() 54 } 55 last := nodes[N-1] 56 target := nodes[rand.Intn(N-2)].Self() 57 58 // It is expected that all nodes can be found. 59 expectedResult := make([]*enode.Node, len(nodes)) 60 for i := range nodes { 61 expectedResult[i] = nodes[i].Self() 62 } 63 sort.Slice(expectedResult, func(i, j int) bool { 64 return enode.DistCmp(target.ID(), expectedResult[i].ID(), expectedResult[j].ID()) < 0 65 }) 66 67 // Do the lookup. 68 results := last.Lookup(target.ID()) 69 if err := checkNodesEqual(results, expectedResult); err != nil { 70 t.Fatalf("lookup returned wrong results: %v", err) 71 } 72 } 73 74 func startLocalhostV5(t *testing.T, cfg Config) *UDPv5 { 75 cfg.PrivateKey = newkey() 76 db, _ := enode.OpenDB("") 77 ln := enode.NewLocalNode(db, cfg.PrivateKey) 78 79 // Prefix logs with node ID. 80 lprefix := fmt.Sprintf("(%s)", ln.ID().TerminalString()) 81 lfmt := log.TerminalFormat(false) 82 cfg.Log = testlog.Logger(t, log.LvlTrace) 83 cfg.Log.SetHandler(log.FuncHandler(func(r *log.Record) error { 84 t.Logf("%s %s", lprefix, lfmt.Format(r)) 85 return nil 86 })) 87 88 // Listen. 89 socket, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IP{127, 0, 0, 1}}) 90 if err != nil { 91 t.Fatal(err) 92 } 93 realaddr := socket.LocalAddr().(*net.UDPAddr) 94 ln.SetStaticIP(realaddr.IP) 95 ln.Set(enr.UDP(realaddr.Port)) 96 udp, err := ListenV5(socket, ln, cfg) 97 if err != nil { 98 t.Fatal(err) 99 } 100 return udp 101 } 102 103 // This test checks that incoming PING calls are handled correctly. 104 func TestUDPv5_pingHandling(t *testing.T) { 105 t.Parallel() 106 test := newUDPV5Test(t) 107 defer test.close() 108 109 test.packetIn(&v5wire.Ping{ReqID: []byte("foo")}) 110 test.waitPacketOut(func(p *v5wire.Pong, addr *net.UDPAddr, _ v5wire.Nonce) { 111 if !bytes.Equal(p.ReqID, []byte("foo")) { 112 t.Error("wrong request ID in response:", p.ReqID) 113 } 114 if p.ENRSeq != test.table.self().Seq() { 115 t.Error("wrong ENR sequence number in response:", p.ENRSeq) 116 } 117 }) 118 } 119 120 // This test checks that incoming 'unknown' packets trigger the handshake. 121 func TestUDPv5_unknownPacket(t *testing.T) { 122 t.Parallel() 123 test := newUDPV5Test(t) 124 defer test.close() 125 126 nonce := v5wire.Nonce{1, 2, 3} 127 check := func(p *v5wire.Whoareyou, wantSeq uint64) { 128 t.Helper() 129 if p.Nonce != nonce { 130 t.Error("wrong nonce in WHOAREYOU:", p.Nonce, nonce) 131 } 132 if p.IDNonce == ([16]byte{}) { 133 t.Error("all zero ID nonce") 134 } 135 if p.RecordSeq != wantSeq { 136 t.Errorf("wrong record seq %d in WHOAREYOU, want %d", p.RecordSeq, wantSeq) 137 } 138 } 139 140 // Unknown packet from unknown node. 141 test.packetIn(&v5wire.Unknown{Nonce: nonce}) 142 test.waitPacketOut(func(p *v5wire.Whoareyou, addr *net.UDPAddr, _ v5wire.Nonce) { 143 check(p, 0) 144 }) 145 146 // Make node known. 147 n := test.getNode(test.remotekey, test.remoteaddr).Node() 148 test.table.addSeenNode(wrapNode(n)) 149 150 test.packetIn(&v5wire.Unknown{Nonce: nonce}) 151 test.waitPacketOut(func(p *v5wire.Whoareyou, addr *net.UDPAddr, _ v5wire.Nonce) { 152 check(p, n.Seq()) 153 }) 154 } 155 156 // This test checks that incoming FINDNODE calls are handled correctly. 157 func TestUDPv5_findnodeHandling(t *testing.T) { 158 t.Parallel() 159 test := newUDPV5Test(t) 160 defer test.close() 161 162 // Create test nodes and insert them into the table. 163 nodes253 := nodesAtDistance(test.table.self().ID(), 253, 10) 164 nodes249 := nodesAtDistance(test.table.self().ID(), 249, 4) 165 nodes248 := nodesAtDistance(test.table.self().ID(), 248, 10) 166 fillTable(test.table, wrapNodes(nodes253)) 167 fillTable(test.table, wrapNodes(nodes249)) 168 fillTable(test.table, wrapNodes(nodes248)) 169 170 // Requesting with distance zero should return the node's own record. 171 test.packetIn(&v5wire.Findnode{ReqID: []byte{0}, Distances: []uint{0}}) 172 test.expectNodes([]byte{0}, 1, []*enode.Node{test.udp.Self()}) 173 174 // Requesting with distance > 256 shouldn't crash. 175 test.packetIn(&v5wire.Findnode{ReqID: []byte{1}, Distances: []uint{4234098}}) 176 test.expectNodes([]byte{1}, 1, nil) 177 178 // Requesting with empty distance list shouldn't crash either. 179 test.packetIn(&v5wire.Findnode{ReqID: []byte{2}, Distances: []uint{}}) 180 test.expectNodes([]byte{2}, 1, nil) 181 182 // This request gets no nodes because the corresponding bucket is empty. 183 test.packetIn(&v5wire.Findnode{ReqID: []byte{3}, Distances: []uint{254}}) 184 test.expectNodes([]byte{3}, 1, nil) 185 186 // This request gets all the distance-253 nodes. 187 test.packetIn(&v5wire.Findnode{ReqID: []byte{4}, Distances: []uint{253}}) 188 test.expectNodes([]byte{4}, 4, nodes253) 189 190 // This request gets all the distance-249 nodes and some more at 248 because 191 // the bucket at 249 is not full. 192 test.packetIn(&v5wire.Findnode{ReqID: []byte{5}, Distances: []uint{249, 248}}) 193 var nodes []*enode.Node 194 nodes = append(nodes, nodes249...) 195 nodes = append(nodes, nodes248[:10]...) 196 test.expectNodes([]byte{5}, 5, nodes) 197 } 198 199 func (test *udpV5Test) expectNodes(wantReqID []byte, wantTotal uint8, wantNodes []*enode.Node) { 200 nodeSet := make(map[enode.ID]*enr.Record) 201 for _, n := range wantNodes { 202 nodeSet[n.ID()] = n.Record() 203 } 204 205 for { 206 test.waitPacketOut(func(p *v5wire.Nodes, addr *net.UDPAddr, _ v5wire.Nonce) { 207 if !bytes.Equal(p.ReqID, wantReqID) { 208 test.t.Fatalf("wrong request ID %v in response, want %v", p.ReqID, wantReqID) 209 } 210 if len(p.Nodes) > 3 { 211 test.t.Fatalf("too many nodes in response") 212 } 213 if p.Total != wantTotal { 214 test.t.Fatalf("wrong total response count %d, want %d", p.Total, wantTotal) 215 } 216 for _, record := range p.Nodes { 217 n, _ := enode.New(enode.ValidSchemesForTesting, record) 218 want := nodeSet[n.ID()] 219 if want == nil { 220 test.t.Fatalf("unexpected node in response: %v", n) 221 } 222 if !reflect.DeepEqual(record, want) { 223 test.t.Fatalf("wrong record in response: %v", n) 224 } 225 delete(nodeSet, n.ID()) 226 } 227 }) 228 if len(nodeSet) == 0 { 229 return 230 } 231 } 232 } 233 234 // This test checks that outgoing PING calls work. 235 func TestUDPv5_pingCall(t *testing.T) { 236 t.Parallel() 237 test := newUDPV5Test(t) 238 defer test.close() 239 240 remote := test.getNode(test.remotekey, test.remoteaddr).Node() 241 done := make(chan error, 1) 242 243 // This ping times out. 244 go func() { 245 _, err := test.udp.ping(remote) 246 done <- err 247 }() 248 test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) {}) 249 if err := <-done; err != errTimeout { 250 t.Fatalf("want errTimeout, got %q", err) 251 } 252 253 // This ping works. 254 go func() { 255 _, err := test.udp.ping(remote) 256 done <- err 257 }() 258 test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) { 259 test.packetInFrom(test.remotekey, test.remoteaddr, &v5wire.Pong{ReqID: p.ReqID}) 260 }) 261 if err := <-done; err != nil { 262 t.Fatal(err) 263 } 264 265 // This ping gets a reply from the wrong endpoint. 266 go func() { 267 _, err := test.udp.ping(remote) 268 done <- err 269 }() 270 test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) { 271 wrongAddr := &net.UDPAddr{IP: net.IP{33, 44, 55, 22}, Port: 10101} 272 test.packetInFrom(test.remotekey, wrongAddr, &v5wire.Pong{ReqID: p.ReqID}) 273 }) 274 if err := <-done; err != errTimeout { 275 t.Fatalf("want errTimeout for reply from wrong IP, got %q", err) 276 } 277 } 278 279 // This test checks that outgoing FINDNODE calls work and multiple NODES 280 // replies are aggregated. 281 func TestUDPv5_findnodeCall(t *testing.T) { 282 t.Parallel() 283 test := newUDPV5Test(t) 284 defer test.close() 285 286 // Launch the request: 287 var ( 288 distances = []uint{230} 289 remote = test.getNode(test.remotekey, test.remoteaddr).Node() 290 nodes = nodesAtDistance(remote.ID(), int(distances[0]), 8) 291 done = make(chan error, 1) 292 response []*enode.Node 293 ) 294 go func() { 295 var err error 296 response, err = test.udp.findnode(remote, distances) 297 done <- err 298 }() 299 300 // Serve the responses: 301 test.waitPacketOut(func(p *v5wire.Findnode, addr *net.UDPAddr, _ v5wire.Nonce) { 302 if !reflect.DeepEqual(p.Distances, distances) { 303 t.Fatalf("wrong distances in request: %v", p.Distances) 304 } 305 test.packetIn(&v5wire.Nodes{ 306 ReqID: p.ReqID, 307 Total: 2, 308 Nodes: nodesToRecords(nodes[:4]), 309 }) 310 test.packetIn(&v5wire.Nodes{ 311 ReqID: p.ReqID, 312 Total: 2, 313 Nodes: nodesToRecords(nodes[4:]), 314 }) 315 }) 316 317 // Check results: 318 if err := <-done; err != nil { 319 t.Fatalf("unexpected error: %v", err) 320 } 321 if !reflect.DeepEqual(response, nodes) { 322 t.Fatalf("wrong nodes in response") 323 } 324 325 // TODO: check invalid IPs 326 // TODO: check invalid/unsigned record 327 } 328 329 // This test checks that pending calls are re-sent when a handshake happens. 330 func TestUDPv5_callResend(t *testing.T) { 331 t.Parallel() 332 test := newUDPV5Test(t) 333 defer test.close() 334 335 remote := test.getNode(test.remotekey, test.remoteaddr).Node() 336 done := make(chan error, 2) 337 go func() { 338 _, err := test.udp.ping(remote) 339 done <- err 340 }() 341 go func() { 342 _, err := test.udp.ping(remote) 343 done <- err 344 }() 345 346 // Ping answered by WHOAREYOU. 347 test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, nonce v5wire.Nonce) { 348 test.packetIn(&v5wire.Whoareyou{Nonce: nonce}) 349 }) 350 // Ping should be re-sent. 351 test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) { 352 test.packetIn(&v5wire.Pong{ReqID: p.ReqID}) 353 }) 354 // Answer the other ping. 355 test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, _ v5wire.Nonce) { 356 test.packetIn(&v5wire.Pong{ReqID: p.ReqID}) 357 }) 358 if err := <-done; err != nil { 359 t.Fatalf("unexpected ping error: %v", err) 360 } 361 if err := <-done; err != nil { 362 t.Fatalf("unexpected ping error: %v", err) 363 } 364 } 365 366 // This test ensures we don't allow multiple rounds of WHOAREYOU for a single call. 367 func TestUDPv5_multipleHandshakeRounds(t *testing.T) { 368 t.Parallel() 369 test := newUDPV5Test(t) 370 defer test.close() 371 372 remote := test.getNode(test.remotekey, test.remoteaddr).Node() 373 done := make(chan error, 1) 374 go func() { 375 _, err := test.udp.ping(remote) 376 done <- err 377 }() 378 379 // Ping answered by WHOAREYOU. 380 test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, nonce v5wire.Nonce) { 381 test.packetIn(&v5wire.Whoareyou{Nonce: nonce}) 382 }) 383 // Ping answered by WHOAREYOU again. 384 test.waitPacketOut(func(p *v5wire.Ping, addr *net.UDPAddr, nonce v5wire.Nonce) { 385 test.packetIn(&v5wire.Whoareyou{Nonce: nonce}) 386 }) 387 if err := <-done; err != errTimeout { 388 t.Fatalf("unexpected ping error: %q", err) 389 } 390 } 391 392 // This test checks that calls with n replies may take up to n * respTimeout. 393 func TestUDPv5_callTimeoutReset(t *testing.T) { 394 t.Parallel() 395 test := newUDPV5Test(t) 396 defer test.close() 397 398 // Launch the request: 399 var ( 400 distance = uint(230) 401 remote = test.getNode(test.remotekey, test.remoteaddr).Node() 402 nodes = nodesAtDistance(remote.ID(), int(distance), 8) 403 done = make(chan error, 1) 404 ) 405 go func() { 406 _, err := test.udp.findnode(remote, []uint{distance}) 407 done <- err 408 }() 409 410 // Serve two responses, slowly. 411 test.waitPacketOut(func(p *v5wire.Findnode, addr *net.UDPAddr, _ v5wire.Nonce) { 412 time.Sleep(respTimeout - 50*time.Millisecond) 413 test.packetIn(&v5wire.Nodes{ 414 ReqID: p.ReqID, 415 Total: 2, 416 Nodes: nodesToRecords(nodes[:4]), 417 }) 418 419 time.Sleep(respTimeout - 50*time.Millisecond) 420 test.packetIn(&v5wire.Nodes{ 421 ReqID: p.ReqID, 422 Total: 2, 423 Nodes: nodesToRecords(nodes[4:]), 424 }) 425 }) 426 if err := <-done; err != nil { 427 t.Fatalf("unexpected error: %q", err) 428 } 429 } 430 431 // This test checks that TALKREQ calls the registered handler function. 432 func TestUDPv5_talkHandling(t *testing.T) { 433 t.Parallel() 434 test := newUDPV5Test(t) 435 defer test.close() 436 437 var recvMessage []byte 438 test.udp.RegisterTalkHandler("test", func(message []byte) []byte { 439 recvMessage = message 440 return []byte("test response") 441 }) 442 443 // Successful case: 444 test.packetIn(&v5wire.TalkRequest{ 445 ReqID: []byte("foo"), 446 Protocol: "test", 447 Message: []byte("test request"), 448 }) 449 test.waitPacketOut(func(p *v5wire.TalkResponse, addr *net.UDPAddr, _ v5wire.Nonce) { 450 if !bytes.Equal(p.ReqID, []byte("foo")) { 451 t.Error("wrong request ID in response:", p.ReqID) 452 } 453 if string(p.Message) != "test response" { 454 t.Errorf("wrong talk response message: %q", p.Message) 455 } 456 if string(recvMessage) != "test request" { 457 t.Errorf("wrong message received in handler: %q", recvMessage) 458 } 459 }) 460 461 // Check that empty response is returned for unregistered protocols. 462 recvMessage = nil 463 test.packetIn(&v5wire.TalkRequest{ 464 ReqID: []byte("2"), 465 Protocol: "wrong", 466 Message: []byte("test request"), 467 }) 468 test.waitPacketOut(func(p *v5wire.TalkResponse, addr *net.UDPAddr, _ v5wire.Nonce) { 469 if !bytes.Equal(p.ReqID, []byte("2")) { 470 t.Error("wrong request ID in response:", p.ReqID) 471 } 472 if string(p.Message) != "" { 473 t.Errorf("wrong talk response message: %q", p.Message) 474 } 475 if recvMessage != nil { 476 t.Errorf("handler was called for wrong protocol: %q", recvMessage) 477 } 478 }) 479 } 480 481 // This test checks that outgoing TALKREQ calls work. 482 func TestUDPv5_talkRequest(t *testing.T) { 483 t.Parallel() 484 test := newUDPV5Test(t) 485 defer test.close() 486 487 remote := test.getNode(test.remotekey, test.remoteaddr).Node() 488 done := make(chan error, 1) 489 490 // This request times out. 491 go func() { 492 _, err := test.udp.TalkRequest(remote, "test", []byte("test request")) 493 done <- err 494 }() 495 test.waitPacketOut(func(p *v5wire.TalkRequest, addr *net.UDPAddr, _ v5wire.Nonce) {}) 496 if err := <-done; err != errTimeout { 497 t.Fatalf("want errTimeout, got %q", err) 498 } 499 500 // This request works. 501 go func() { 502 _, err := test.udp.TalkRequest(remote, "test", []byte("test request")) 503 done <- err 504 }() 505 test.waitPacketOut(func(p *v5wire.TalkRequest, addr *net.UDPAddr, _ v5wire.Nonce) { 506 if p.Protocol != "test" { 507 t.Errorf("wrong protocol ID in talk request: %q", p.Protocol) 508 } 509 if string(p.Message) != "test request" { 510 t.Errorf("wrong message talk request: %q", p.Message) 511 } 512 test.packetInFrom(test.remotekey, test.remoteaddr, &v5wire.TalkResponse{ 513 ReqID: p.ReqID, 514 Message: []byte("test response"), 515 }) 516 }) 517 if err := <-done; err != nil { 518 t.Fatal(err) 519 } 520 } 521 522 // This test checks that lookup works. 523 func TestUDPv5_lookup(t *testing.T) { 524 t.Parallel() 525 test := newUDPV5Test(t) 526 527 // Lookup on empty table returns no nodes. 528 if results := test.udp.Lookup(lookupTestnet.target.id()); len(results) > 0 { 529 t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results) 530 } 531 532 // Ensure the tester knows all nodes in lookupTestnet by IP. 533 for d, nn := range lookupTestnet.dists { 534 for i, key := range nn { 535 n := lookupTestnet.node(d, i) 536 test.getNode(key, &net.UDPAddr{IP: n.IP(), Port: n.UDP()}) 537 } 538 } 539 540 // Seed table with initial node. 541 initialNode := lookupTestnet.node(256, 0) 542 fillTable(test.table, []*node{wrapNode(initialNode)}) 543 544 // Start the lookup. 545 resultC := make(chan []*enode.Node, 1) 546 go func() { 547 resultC <- test.udp.Lookup(lookupTestnet.target.id()) 548 test.close() 549 }() 550 551 // Answer lookup packets. 552 asked := make(map[enode.ID]bool) 553 for done := false; !done; { 554 done = test.waitPacketOut(func(p v5wire.Packet, to *net.UDPAddr, _ v5wire.Nonce) { 555 recipient, key := lookupTestnet.nodeByAddr(to) 556 switch p := p.(type) { 557 case *v5wire.Ping: 558 test.packetInFrom(key, to, &v5wire.Pong{ReqID: p.ReqID}) 559 case *v5wire.Findnode: 560 if asked[recipient.ID()] { 561 t.Error("Asked node", recipient.ID(), "twice") 562 } 563 asked[recipient.ID()] = true 564 nodes := lookupTestnet.neighborsAtDistances(recipient, p.Distances, 16) 565 t.Logf("Got FINDNODE for %v, returning %d nodes", p.Distances, len(nodes)) 566 for _, resp := range packNodes(p.ReqID, nodes) { 567 test.packetInFrom(key, to, resp) 568 } 569 } 570 }) 571 } 572 573 // Verify result nodes. 574 results := <-resultC 575 checkLookupResults(t, lookupTestnet, results) 576 } 577 578 // This test checks the local node can be utilised to set key-values. 579 func TestUDPv5_LocalNode(t *testing.T) { 580 t.Parallel() 581 var cfg Config 582 node := startLocalhostV5(t, cfg) 583 defer node.Close() 584 localNd := node.LocalNode() 585 586 // set value in node's local record 587 testVal := [4]byte{'A', 'B', 'C', 'D'} 588 localNd.Set(enr.WithEntry("testing", &testVal)) 589 590 // retrieve the value from self to make sure it matches. 591 outputVal := [4]byte{} 592 if err := node.Self().Load(enr.WithEntry("testing", &outputVal)); err != nil { 593 t.Errorf("Could not load value from record: %v", err) 594 } 595 if testVal != outputVal { 596 t.Errorf("Wanted %#x to be retrieved from the record but instead got %#x", testVal, outputVal) 597 } 598 } 599 600 // udpV5Test is the framework for all tests above. 601 // It runs the UDPv5 transport on a virtual socket and allows testing outgoing packets. 602 type udpV5Test struct { 603 t *testing.T 604 pipe *dgramPipe 605 table *Table 606 db *enode.DB 607 udp *UDPv5 608 localkey, remotekey *crypto.PrivateKey 609 remoteaddr *net.UDPAddr 610 nodesByID map[enode.ID]*enode.LocalNode 611 nodesByIP map[string]*enode.LocalNode 612 } 613 614 // testCodec is the packet encoding used by protocol tests. This codec does not perform encryption. 615 type testCodec struct { 616 test *udpV5Test 617 id enode.ID 618 ctr uint64 619 } 620 621 type testCodecFrame struct { 622 NodeID enode.ID 623 AuthTag v5wire.Nonce 624 Ptype byte 625 Packet rlp.RawValue 626 } 627 628 func (c *testCodec) Encode(toID enode.ID, addr string, p v5wire.Packet, _ *v5wire.Whoareyou) ([]byte, v5wire.Nonce, error) { 629 c.ctr++ 630 var authTag v5wire.Nonce 631 binary.BigEndian.PutUint64(authTag[:], c.ctr) 632 633 penc, _ := rlp.EncodeToBytes(p) 634 frame, err := rlp.EncodeToBytes(testCodecFrame{c.id, authTag, p.Kind(), penc}) 635 return frame, authTag, err 636 } 637 638 func (c *testCodec) Decode(input []byte, addr string) (enode.ID, *enode.Node, v5wire.Packet, error) { 639 frame, p, err := c.decodeFrame(input) 640 if err != nil { 641 return enode.ID{}, nil, nil, err 642 } 643 return frame.NodeID, nil, p, nil 644 } 645 646 func (c *testCodec) decodeFrame(input []byte) (frame testCodecFrame, p v5wire.Packet, err error) { 647 if err = rlp.DecodeBytes(input, &frame); err != nil { 648 return frame, nil, fmt.Errorf("invalid frame: %v", err) 649 } 650 switch frame.Ptype { 651 case v5wire.UnknownPacket: 652 dec := new(v5wire.Unknown) 653 err = rlp.DecodeBytes(frame.Packet, &dec) 654 p = dec 655 case v5wire.WhoareyouPacket: 656 dec := new(v5wire.Whoareyou) 657 err = rlp.DecodeBytes(frame.Packet, &dec) 658 p = dec 659 default: 660 p, err = v5wire.DecodeMessage(frame.Ptype, frame.Packet) 661 } 662 return frame, p, err 663 } 664 665 func newUDPV5Test(t *testing.T) *udpV5Test { 666 test := &udpV5Test{ 667 t: t, 668 pipe: newpipe(), 669 localkey: newkey(), 670 remotekey: newkey(), 671 remoteaddr: &net.UDPAddr{IP: net.IP{10, 0, 1, 99}, Port: 30300}, 672 nodesByID: make(map[enode.ID]*enode.LocalNode), 673 nodesByIP: make(map[string]*enode.LocalNode), 674 } 675 test.db, _ = enode.OpenDB("") 676 ln := enode.NewLocalNode(test.db, test.localkey) 677 ln.SetStaticIP(net.IP{10, 0, 0, 1}) 678 ln.Set(enr.UDP(30300)) 679 test.udp, _ = ListenV5(test.pipe, ln, Config{ 680 PrivateKey: test.localkey, 681 Log: testlog.Logger(t, log.LvlTrace), 682 ValidSchemes: enode.ValidSchemesForTesting, 683 }) 684 test.udp.codec = &testCodec{test: test, id: ln.ID()} 685 test.table = test.udp.tab 686 test.nodesByID[ln.ID()] = ln 687 // Wait for initial refresh so the table doesn't send unexpected findnode. 688 <-test.table.initDone 689 return test 690 } 691 692 // handles a packet as if it had been sent to the transport. 693 func (test *udpV5Test) packetIn(packet v5wire.Packet) { 694 test.t.Helper() 695 test.packetInFrom(test.remotekey, test.remoteaddr, packet) 696 } 697 698 // handles a packet as if it had been sent to the transport by the key/endpoint. 699 func (test *udpV5Test) packetInFrom(key *crypto.PrivateKey, addr *net.UDPAddr, packet v5wire.Packet) { 700 test.t.Helper() 701 702 ln := test.getNode(key, addr) 703 codec := &testCodec{test: test, id: ln.ID()} 704 enc, _, err := codec.Encode(test.udp.Self().ID(), addr.String(), packet, nil) 705 if err != nil { 706 test.t.Errorf("%s encode error: %v", packet.Name(), err) 707 } 708 if test.udp.dispatchReadPacket(addr, enc) { 709 <-test.udp.readNextCh // unblock UDPv5.dispatch 710 } 711 } 712 713 // getNode ensures the test knows about a node at the given endpoint. 714 func (test *udpV5Test) getNode(key *crypto.PrivateKey, addr *net.UDPAddr) *enode.LocalNode { 715 id := encodePubkey(key.PublicKey()).id() 716 ln := test.nodesByID[id] 717 if ln == nil { 718 db, _ := enode.OpenDB("") 719 ln = enode.NewLocalNode(db, key) 720 ln.SetStaticIP(addr.IP) 721 ln.Set(enr.UDP(addr.Port)) 722 test.nodesByID[id] = ln 723 } 724 test.nodesByIP[string(addr.IP)] = ln 725 return ln 726 } 727 728 // waitPacketOut waits for the next output packet and handles it using the given 'validate' 729 // function. The function must be of type func (X, *net.UDPAddr, v5wire.Nonce) where X is 730 // assignable to packetV5. 731 func (test *udpV5Test) waitPacketOut(validate interface{}) (closed bool) { 732 test.t.Helper() 733 734 fn := reflect.ValueOf(validate) 735 exptype := fn.Type().In(0) 736 737 dgram, err := test.pipe.receive() 738 if err == errClosed { 739 return true 740 } 741 if err == errTimeout { 742 test.t.Fatalf("timed out waiting for %v", exptype) 743 return false 744 } 745 ln := test.nodesByIP[string(dgram.to.IP)] 746 if ln == nil { 747 test.t.Fatalf("attempt to send to non-existing node %v", &dgram.to) 748 return false 749 } 750 codec := &testCodec{test: test, id: ln.ID()} 751 frame, p, err := codec.decodeFrame(dgram.data) 752 if err != nil { 753 test.t.Errorf("sent packet decode error: %v", err) 754 return false 755 } 756 if !reflect.TypeOf(p).AssignableTo(exptype) { 757 test.t.Errorf("sent packet type mismatch, got: %v, want: %v", reflect.TypeOf(p), exptype) 758 return false 759 } 760 fn.Call([]reflect.Value{reflect.ValueOf(p), reflect.ValueOf(&dgram.to), reflect.ValueOf(frame.AuthTag)}) 761 return false 762 } 763 764 func (test *udpV5Test) close() { 765 test.t.Helper() 766 767 test.udp.Close() 768 test.db.Close() 769 for id, n := range test.nodesByID { 770 if id != test.udp.Self().ID() { 771 n.Database().Close() 772 } 773 } 774 if len(test.pipe.queue) != 0 { 775 test.t.Fatalf("%d unmatched UDP packets in queue", len(test.pipe.queue)) 776 } 777 }