github.com/klaytn/klaytn@v1.12.1/networks/p2p/discover/udp_test.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2015 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from p2p/discover/udp_test.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package discover 22 23 import ( 24 "bytes" 25 "crypto/ecdsa" 26 "encoding/binary" 27 "encoding/hex" 28 "errors" 29 "fmt" 30 "io" 31 "math/rand" 32 "net" 33 "path/filepath" 34 "reflect" 35 "runtime" 36 "sync" 37 "testing" 38 "time" 39 40 "github.com/davecgh/go-spew/spew" 41 "github.com/klaytn/klaytn/common" 42 "github.com/klaytn/klaytn/crypto" 43 "github.com/klaytn/klaytn/rlp" 44 ) 45 46 func init() { 47 spew.Config.DisableMethods = true 48 } 49 50 // shared test variables 51 var ( 52 futureExp = uint64(time.Now().Add(10 * time.Hour).Unix()) 53 testTarget = NodeID{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1} 54 testRemote = rpcEndpoint{IP: net.ParseIP("1.1.1.1").To4(), UDP: 1, TCP: 2} 55 testLocalAnnounced = rpcEndpoint{IP: net.ParseIP("2.2.2.2").To4(), UDP: 3, TCP: 4} 56 testLocal = rpcEndpoint{IP: net.ParseIP("3.3.3.3").To4(), UDP: 5, TCP: 6} 57 ) 58 59 type udpTest struct { 60 t *testing.T 61 pipe *dgramPipe 62 table *Table 63 udp *udp 64 sent [][]byte 65 localkey, remotekey *ecdsa.PrivateKey 66 remoteaddr *net.UDPAddr 67 } 68 69 func newUDPTest(t *testing.T) *udpTest { 70 test := &udpTest{ 71 t: t, 72 pipe: newpipe(), 73 localkey: newkey(), 74 remotekey: newkey(), 75 remoteaddr: &net.UDPAddr{IP: net.IP{10, 0, 1, 99}, Port: 30303}, 76 } 77 conf := Config{ 78 Conn: test.pipe, 79 PrivateKey: test.localkey, 80 Id: PubkeyID(&test.localkey.PublicKey), 81 Addr: test.pipe.LocalAddr().(*net.UDPAddr), 82 } 83 discv, udp, _ := newUDP(&conf) 84 tab := discv.(*Table) 85 tab.addStorage(NodeTypeUnknown, &KademliaStorage{targetType: NodeTypeUnknown}) 86 test.table, test.udp = tab, udp 87 // Wait for initial refresh so the table doesn't send unexpected findnode. 88 <-test.table.initDone 89 return test 90 } 91 92 // handles a packet as if it had been sent to the transport. 93 func (test *udpTest) packetIn(wantError error, ptype byte, data packet) error { 94 enc, _, err := encodePacket(test.remotekey, ptype, data) 95 if err != nil { 96 return test.errorf("packet (%d) encode error: %v", ptype, err) 97 } 98 test.sent = append(test.sent, enc) 99 if err = test.udp.handlePacket(test.remoteaddr, enc); err != wantError { 100 return test.errorf("error mismatch: got %q, want %q", err, wantError) 101 } 102 return nil 103 } 104 105 // waits for a packet to be sent by the transport. 106 // validate should have type func(*udpTest, X) error, where X is a packet type. 107 func (test *udpTest) waitPacketOut(validate interface{}) ([]byte, error) { 108 dgram := test.pipe.waitPacketOut() 109 p, _, hash, err := decodePacket(dgram) 110 if err != nil { 111 return hash, test.errorf("sent packet decode error: %v", err) 112 } 113 fn := reflect.ValueOf(validate) 114 exptype := fn.Type().In(0) 115 if reflect.TypeOf(p) != exptype { 116 return hash, test.errorf("sent packet type mismatch, got: %v, want: %v", reflect.TypeOf(p), exptype) 117 } 118 fn.Call([]reflect.Value{reflect.ValueOf(p)}) 119 return hash, nil 120 } 121 122 func (test *udpTest) errorf(format string, args ...interface{}) error { 123 _, file, line, ok := runtime.Caller(2) // errorf + waitPacketOut 124 if ok { 125 file = filepath.Base(file) 126 } else { 127 file = "???" 128 line = 1 129 } 130 err := fmt.Errorf(format, args...) 131 fmt.Printf("\t%s:%d: %v\n", file, line, err) 132 test.t.Fail() 133 return err 134 } 135 136 func TestUDP_packetErrors(t *testing.T) { 137 test := newUDPTest(t) 138 defer test.table.Close() 139 140 test.packetIn(errExpired, pingPacket, &ping{From: testRemote, To: testLocalAnnounced, Version: Version}) 141 test.packetIn(errUnsolicitedReply, pongPacket, &pong{ReplyTok: []byte{}, Expiration: futureExp}) 142 test.packetIn(errUnknownNode, findnodePacket, &findnode{Expiration: futureExp}) 143 test.packetIn(errUnsolicitedReply, neighborsPacket, &neighbors{Expiration: futureExp}) 144 } 145 146 func TestUDP_pingTimeout(t *testing.T) { 147 t.Parallel() 148 test := newUDPTest(t) 149 defer test.table.Close() 150 151 toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222} 152 toid := NodeID{1, 2, 3, 4} 153 if err := test.udp.ping(toid, toaddr); err != errTimeout { 154 t.Error("expected timeout error, got", err) 155 } 156 } 157 158 func TestUDP_responseTimeouts(t *testing.T) { 159 t.Parallel() 160 test := newUDPTest(t) 161 defer test.table.Close() 162 163 rand.Seed(time.Now().UnixNano()) 164 randomDuration := func(max time.Duration) time.Duration { 165 return time.Duration(rand.Int63n(int64(max))) 166 } 167 168 var ( 169 nReqs = 200 170 nTimeouts = 0 // number of requests with ptype > 128 171 nilErr = make(chan error, nReqs) // for requests that get a reply 172 timeoutErr = make(chan error, nReqs) // for requests that time out 173 ) 174 for i := 0; i < nReqs; i++ { 175 // Create a matcher for a random request in udp.loop. Requests 176 // with ptype <= 128 will not get a reply and should time out. 177 // For all other requests, a reply is scheduled to arrive 178 // within the timeout window. 179 p := &pending{ 180 ptype: byte(rand.Intn(255)), 181 callback: func(interface{}) bool { return true }, 182 } 183 binary.BigEndian.PutUint64(p.from[:], uint64(i)) 184 if p.ptype <= 128 { 185 p.errc = timeoutErr 186 test.udp.addpending <- p 187 nTimeouts++ 188 } else { 189 p.errc = nilErr 190 test.udp.addpending <- p 191 time.AfterFunc(randomDuration(60*time.Millisecond), func() { 192 if !test.udp.handleReply(p.from, p.ptype, nil) { 193 t.Logf("not matched: %v", p) 194 } 195 }) 196 } 197 time.Sleep(randomDuration(30 * time.Millisecond)) 198 } 199 200 // Check that all timeouts were delivered and that the rest got nil errors. 201 // The replies must be delivered. 202 var ( 203 recvDeadline = time.After(20 * time.Second) 204 nTimeoutsRecv, nNil = 0, 0 205 ) 206 for i := 0; i < nReqs; i++ { 207 select { 208 case err := <-timeoutErr: 209 if err != errTimeout { 210 t.Fatalf("got non-timeout error on timeoutErr %d: %v", i, err) 211 } 212 nTimeoutsRecv++ 213 case err := <-nilErr: 214 if err != nil { 215 t.Fatalf("got non-nil error on nilErr %d: %v", i, err) 216 } 217 nNil++ 218 case <-recvDeadline: 219 t.Fatalf("exceeded recv deadline") 220 } 221 } 222 if nTimeoutsRecv != nTimeouts { 223 t.Errorf("wrong number of timeout errors received: got %d, want %d", nTimeoutsRecv, nTimeouts) 224 } 225 if nNil != nReqs-nTimeouts { 226 t.Errorf("wrong number of successful replies: got %d, want %d", nNil, nReqs-nTimeouts) 227 } 228 } 229 230 func TestUDP_findnodeTimeout(t *testing.T) { 231 t.Parallel() 232 test := newUDPTest(t) 233 defer test.table.Close() 234 235 toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222} 236 toid := NodeID{1, 2, 3, 4} 237 target := NodeID{4, 5, 6, 7} 238 result, err := test.udp.findnode(toid, toaddr, target, NodeTypeUnknown, 100) 239 if err != errTimeout { 240 t.Error("expected timeout error, got", err) 241 } 242 if len(result) > 0 { 243 t.Error("expected empty result, got", result) 244 } 245 } 246 247 func TestUDP_findnode(t *testing.T) { 248 test := newUDPTest(t) 249 defer test.table.Close() 250 251 // put a few nodes into the table. their exact 252 // distribution shouldn't matter much, although we need to 253 // take care not to overflow any bucket. 254 targetHash := crypto.Keccak256Hash(testTarget[:]) 255 nodes := &nodesByDistance{target: targetHash} 256 for i := 0; i < bucketSize; i++ { 257 nodes.push(nodeAtDistance(test.table.self.sha, i+2, NodeTypeUnknown), bucketSize) 258 } 259 test.table.stuff(nodes.entries, NodeTypeUnknown) 260 261 // ensure there's a bond with the test node, 262 // findnode won't be accepted otherwise. 263 test.table.db.updateBondTime(PubkeyID(&test.remotekey.PublicKey), time.Now()) 264 265 // check that closest neighbors are returned. 266 test.packetIn(nil, findnodePacket, &findnode{Target: testTarget, Expiration: futureExp}) 267 expected := test.table.closest(targetHash, NodeTypeUnknown, bucketSize) 268 269 waitNeighbors := func(want []*Node) { 270 test.waitPacketOut(func(p *neighbors) { 271 if len(p.Nodes) != len(want) { 272 t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), bucketSize) 273 } 274 for i := range p.Nodes { 275 if p.Nodes[i].ID != want[i].ID { 276 t.Errorf("result mismatch at %d:\n got: %v\n want: %v", i, p.Nodes[i], expected.entries[i]) 277 } 278 } 279 }) 280 } 281 waitNeighbors(expected.entries[:maxNeighbors]) 282 waitNeighbors(expected.entries[maxNeighbors:]) 283 } 284 285 func TestUDP_findnodeMultiReply(t *testing.T) { 286 test := newUDPTest(t) 287 defer test.table.Close() 288 289 // queue a pending findnode request 290 resultc, errc := make(chan []*Node), make(chan error) 291 go func() { 292 rid := PubkeyID(&test.remotekey.PublicKey) 293 ns, err := test.udp.findnode(rid, test.remoteaddr, testTarget, NodeTypeUnknown, 100) 294 if err != nil && len(ns) == 0 { 295 errc <- err 296 } else { 297 resultc <- ns 298 } 299 }() 300 301 // wait for the findnode to be sent. 302 // after it is sent, the transport is waiting for a reply 303 test.waitPacketOut(func(p *findnode) { 304 if p.Target != testTarget { 305 t.Errorf("wrong target: got %v, want %v", p.Target, testTarget) 306 } 307 }) 308 309 // send the reply as two packets. 310 list := []*Node{ 311 MustParseNode("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304"), 312 MustParseNode("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.17:30303"), 313 MustParseNode("kni://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.18:30301?discport=17"), 314 MustParseNode("kni://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.19:30303"), 315 } 316 rpclist := make([]rpcNode, len(list)) 317 for i := range list { 318 rpclist[i] = nodeToRPC(list[i]) 319 } 320 test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: rpclist[:2]}) 321 test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: rpclist[2:]}) 322 323 // check that the sent neighbors are all returned by findnode 324 select { 325 case result := <-resultc: 326 want := append(list[:2], list[3:]...) 327 if !reflect.DeepEqual(result, want) { 328 t.Errorf("neighbors mismatch:\n got: %v\n want: %v", result, want) 329 } 330 case err := <-errc: 331 t.Errorf("findnode error: %v", err) 332 case <-time.After(5 * time.Second): 333 t.Error("findnode did not return within 5 seconds") 334 } 335 } 336 337 func TestUDP_successfulPing(t *testing.T) { 338 test := newUDPTest(t) 339 added := make(chan *Node, 1) 340 test.table.nodeAddedHook = func(n *Node) { added <- n } 341 defer test.table.Close() 342 343 // The remote side sends a ping packet to initiate the exchange. 344 go test.packetIn(nil, pingPacket, &ping{From: testRemote, To: testLocalAnnounced, Version: Version, Expiration: futureExp}) 345 346 // the ping is replied to. 347 test.waitPacketOut(func(p *pong) { 348 pinghash := test.sent[0][:macSize] 349 if !bytes.Equal(p.ReplyTok, pinghash) { 350 t.Errorf("got pong.ReplyTok %x, want %x", p.ReplyTok, pinghash) 351 } 352 wantTo := rpcEndpoint{ 353 // The mirrored UDP address is the UDP packet sender 354 IP: test.remoteaddr.IP, UDP: uint16(test.remoteaddr.Port), 355 // The mirrored TCP port is the one from the ping packet 356 TCP: testRemote.TCP, 357 } 358 if !reflect.DeepEqual(p.To, wantTo) { 359 t.Errorf("got pong.To %v, want %v", p.To, wantTo) 360 } 361 }) 362 363 // remote is unknown, the table pings back. 364 hash, _ := test.waitPacketOut(func(p *ping) error { 365 if !reflect.DeepEqual(p.From, test.udp.ourEndpoint) { 366 t.Errorf("got ping.From %v, want %v", p.From, test.udp.ourEndpoint) 367 } 368 wantTo := rpcEndpoint{ 369 // The mirrored UDP address is the UDP packet sender. 370 IP: test.remoteaddr.IP, UDP: uint16(test.remoteaddr.Port), 371 TCP: 0, 372 } 373 if !reflect.DeepEqual(p.To, wantTo) { 374 t.Errorf("got ping.To %v, want %v", p.To, wantTo) 375 } 376 return nil 377 }) 378 test.packetIn(nil, pongPacket, &pong{ReplyTok: hash, Expiration: futureExp}) 379 380 // the node should be added to the table shortly after getting the 381 // pong packet. 382 select { 383 case n := <-added: 384 rid := PubkeyID(&test.remotekey.PublicKey) 385 if n.ID != rid { 386 t.Errorf("node has wrong ID: got %v, want %v", n.ID, rid) 387 } 388 if !n.IP.Equal(test.remoteaddr.IP) { 389 t.Errorf("node has wrong IP: got %v, want: %v", n.IP, test.remoteaddr.IP) 390 } 391 if int(n.UDP) != test.remoteaddr.Port { 392 t.Errorf("node has wrong UDP port: got %v, want: %v", n.UDP, test.remoteaddr.Port) 393 } 394 if n.TCP != testRemote.TCP { 395 t.Errorf("node has wrong TCP port: got %v, want: %v", n.TCP, testRemote.TCP) 396 } 397 case <-time.After(2 * time.Second): 398 t.Errorf("node was not added within 2 seconds") 399 } 400 } 401 402 var testPackets = []struct { 403 input string 404 wantPacket interface{} 405 }{ 406 { 407 input: "c1dc2bc5e0986febc55ec7f6f3970eeb59e0ef3f7fad695ad7241b6ba6ea3da3bee8c5a0eabdcdd5d9dc7507176f033a4a9d45e20578014be411e00a4283343a32c19d70afa2592dc18cd6c56dc7bad46f94fffd086a72e9580e34c14527745e0101ed8004cc847f000001820cfa8215a880d890000000000000000000000000000000018208ae820d05808443b9a355", 408 wantPacket: &ping{ 409 Version: 4, 410 From: rpcEndpoint{net.ParseIP("127.0.0.1").To4(), 3322, 5544, NodeTypeUnknown}, 411 To: rpcEndpoint{net.ParseIP("::1"), 2222, 3333, NodeTypeUnknown}, 412 Expiration: 1136239445, 413 Rest: []rlp.RawValue{}, 414 }, 415 }, 416 { 417 input: "940bcb4948a6bb037a25b24b59b569aa71fccd3a0d3da6ac7b2d99bf70b74dc0c480efb7ebc6e558bd440d811a49f869dad84ec13f97f2e40e4a52f33de7c3441fec3083a28ce9b7d49052b9de070e7668047a6c06574a10bab526629c7ccd650101ef8004cc847f000001820cfa8215a880d890000000000000000000000000000000018208ae820d05808443b9a3550102", 418 wantPacket: &ping{ 419 Version: 4, 420 From: rpcEndpoint{net.ParseIP("127.0.0.1").To4(), 3322, 5544, NodeTypeUnknown}, 421 To: rpcEndpoint{net.ParseIP("::1"), 2222, 3333, NodeTypeUnknown}, 422 Expiration: 1136239445, 423 Rest: []rlp.RawValue{{0x01}, {0x02}}, 424 }, 425 }, 426 { 427 input: "d170464a76f677c22287104e7245bf5d0275cf293f13dec6d6d1e59e00b51ff6cf00cbcc58cd19605edbedac50ec50a9193f73f1fdd749442bb7e609df8aadea46922f3f125b0a85b828949b19b6ea29a9c3e8cb998d54f07b60fb340499f02d0001f8418082022bd89020010db83c4d001500000000abcdef12820cfa8215a880d89020010db885a308d313198a2e037073488208ae82823a808443b9a355c50102030405", 428 wantPacket: &ping{ 429 Version: 555, 430 From: rpcEndpoint{net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), 3322, 5544, NodeTypeUnknown}, 431 To: rpcEndpoint{net.ParseIP("2001:db8:85a3:8d3:1319:8a2e:370:7348"), 2222, 33338, NodeTypeUnknown}, 432 Expiration: 1136239445, 433 Rest: []rlp.RawValue{{0xC5, 0x01, 0x02, 0x03, 0x04, 0x05}}, 434 }, 435 }, 436 { 437 input: "b4214320298d39982ddf34ed1329961ab817f4e1678ca91356827427d6153ab31c65e46cba61350888523789b50bc3c5c0c97fa13bfc8debffd21eea2baca2ef3fee180ab983276ab90c8b928db9a1e970fad9efe2528bfa0482377a8bd778be0002f847d89020010db885a308d313198a2e037073488208ae82823a80a0fbc914b16819237dcd8801d7e53f69e9719adecb3cc0e790c57e91ca4461c9548443b9a355c6010203c2040506", 438 wantPacket: &pong{ 439 To: rpcEndpoint{net.ParseIP("2001:db8:85a3:8d3:1319:8a2e:370:7348"), 2222, 33338, NodeTypeUnknown}, 440 ReplyTok: common.Hex2Bytes("fbc914b16819237dcd8801d7e53f69e9719adecb3cc0e790c57e91ca4461c954"), 441 Expiration: 1136239445, 442 Rest: []rlp.RawValue{{0xC6, 0x01, 0x02, 0x03, 0xC2, 0x04, 0x05}, {0x06}}, 443 }, 444 }, 445 { 446 input: "07f78dd1ffff1a0c0885bd52c54979f714b50f1a7bede368762a7caf44bf0e858e48fb1445201ad19a52f33a73ed2ff33ae3770520e3cbeaca07d62e042d80645d9726ce03088683dd1165f23a1c9151975f010fd17fa519704a9beab755c3240103f84fb840ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f808443b9a35582999983999999", 447 wantPacket: &findnode{ 448 Target: MustHexID("ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f"), 449 Expiration: 1136239445, 450 Rest: []rlp.RawValue{{0x82, 0x99, 0x99}, {0x83, 0x99, 0x99, 0x99}}, 451 }, 452 }, 453 { 454 input: "ae3f2a4bdb2a6e2629c333b62d617c76b977edd85dfafa5cb6bed1206e1ca9cd826d16ebff4f68318bb7032e9fa19758b3b35026be584c186ba05ceb2574fce94bee11f71abebd820fdc0248c6dd66bb5947cd6ca09d9103cd05fd3aa2b31e920004f9016080f90154f84e846321163782115c82115db8403155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf3280f84a84010203040101b840312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db80f85a9020010db83c4d001500000000abcdef12820d05820d05b84038643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac80f85a9020010db885a308d313198a2e037073488203e78203e8b8408dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73808443b9a355010203", 455 wantPacket: &neighbors{ 456 Nodes: []rpcNode{ 457 { 458 ID: MustHexID("3155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32"), 459 IP: net.ParseIP("99.33.22.55").To4(), 460 UDP: 4444, 461 TCP: 4445, 462 }, 463 { 464 ID: MustHexID("312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069db"), 465 IP: net.ParseIP("1.2.3.4").To4(), 466 UDP: 1, 467 TCP: 1, 468 }, 469 { 470 ID: MustHexID("38643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aac"), 471 IP: net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), 472 UDP: 3333, 473 TCP: 3333, 474 }, 475 { 476 ID: MustHexID("8dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73"), 477 IP: net.ParseIP("2001:db8:85a3:8d3:1319:8a2e:370:7348"), 478 UDP: 999, 479 TCP: 1000, 480 }, 481 }, 482 Expiration: 1136239445, 483 Rest: []rlp.RawValue{{0x01}, {0x02}, {0x03}}, 484 }, 485 }, 486 } 487 488 func TestForwardCompatibility(t *testing.T) { 489 testkey, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 490 wantNodeID := PubkeyID(&testkey.PublicKey) 491 492 for _, test := range testPackets { 493 input, err := hex.DecodeString(test.input) 494 if err != nil { 495 t.Fatalf("invalid hex: %s", test.input) 496 } 497 packet, nodeid, _, err := decodePacket(input) 498 if err != nil { 499 t.Errorf("did not accept packet %s\n%v", test.input, err) 500 continue 501 } 502 if !reflect.DeepEqual(packet, test.wantPacket) { 503 t.Errorf("got %s\nwant %s", spew.Sdump(packet), spew.Sdump(test.wantPacket)) 504 } 505 if nodeid != wantNodeID { 506 t.Errorf("got id %v\nwant id %v", nodeid, wantNodeID) 507 } 508 } 509 } 510 511 // dgramPipe is a fake UDP socket. It queues all sent datagrams. 512 type dgramPipe struct { 513 mu *sync.Mutex 514 cond *sync.Cond 515 closing chan struct{} 516 closed bool 517 queue [][]byte 518 } 519 520 func newpipe() *dgramPipe { 521 mu := new(sync.Mutex) 522 return &dgramPipe{ 523 closing: make(chan struct{}), 524 cond: &sync.Cond{L: mu}, 525 mu: mu, 526 } 527 } 528 529 // WriteToUDP queues a datagram. 530 func (c *dgramPipe) WriteToUDP(b []byte, to *net.UDPAddr) (n int, err error) { 531 msg := make([]byte, len(b)) 532 copy(msg, b) 533 c.mu.Lock() 534 defer c.mu.Unlock() 535 if c.closed { 536 return 0, errors.New("closed") 537 } 538 c.queue = append(c.queue, msg) 539 c.cond.Signal() 540 return len(b), nil 541 } 542 543 // ReadFromUDP just hangs until the pipe is closed. 544 func (c *dgramPipe) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) { 545 <-c.closing 546 return 0, nil, io.EOF 547 } 548 549 func (c *dgramPipe) Close() error { 550 c.mu.Lock() 551 defer c.mu.Unlock() 552 if !c.closed { 553 close(c.closing) 554 c.closed = true 555 } 556 return nil 557 } 558 559 func (c *dgramPipe) LocalAddr() net.Addr { 560 return &net.UDPAddr{IP: testLocal.IP, Port: int(testLocal.UDP)} 561 } 562 563 func (c *dgramPipe) waitPacketOut() []byte { 564 c.mu.Lock() 565 defer c.mu.Unlock() 566 for len(c.queue) == 0 { 567 c.cond.Wait() 568 } 569 p := c.queue[0] 570 copy(c.queue, c.queue[1:]) 571 c.queue = c.queue[:len(c.queue)-1] 572 return p 573 }