github.com/ethereum/go-ethereum@v1.16.1/p2p/discover/v4_lookup_test.go (about) 1 // Copyright 2019 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 discover 18 19 import ( 20 "crypto/ecdsa" 21 "fmt" 22 "net/netip" 23 "slices" 24 "sync" 25 "testing" 26 27 "github.com/ethereum/go-ethereum/crypto" 28 "github.com/ethereum/go-ethereum/p2p/discover/v4wire" 29 "github.com/ethereum/go-ethereum/p2p/enode" 30 "github.com/ethereum/go-ethereum/p2p/enr" 31 ) 32 33 func TestUDPv4_Lookup(t *testing.T) { 34 t.Parallel() 35 test := newUDPTest(t) 36 37 // Lookup on empty table returns no nodes. 38 targetKey, _ := v4wire.DecodePubkey(crypto.S256(), lookupTestnet.target) 39 if results := test.udp.LookupPubkey(targetKey); len(results) > 0 { 40 t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results) 41 } 42 43 // Seed table with initial node. 44 fillTable(test.table, []*enode.Node{lookupTestnet.node(256, 0)}, true) 45 46 // Start the lookup. 47 resultC := make(chan []*enode.Node, 1) 48 go func() { 49 resultC <- test.udp.LookupPubkey(targetKey) 50 test.close() 51 }() 52 53 // Answer lookup packets. 54 serveTestnet(test, lookupTestnet) 55 56 // Verify result nodes. 57 results := <-resultC 58 t.Logf("results:") 59 for _, e := range results { 60 t.Logf(" ld=%d, %x", enode.LogDist(lookupTestnet.target.ID(), e.ID()), e.ID().Bytes()) 61 } 62 if len(results) != bucketSize { 63 t.Errorf("wrong number of results: got %d, want %d", len(results), bucketSize) 64 } 65 checkLookupResults(t, lookupTestnet, results) 66 } 67 68 func TestUDPv4_LookupIterator(t *testing.T) { 69 t.Parallel() 70 test := newUDPTest(t) 71 var wg sync.WaitGroup 72 defer func() { 73 test.close() 74 wg.Wait() 75 }() 76 77 // Seed table with initial nodes. 78 bootnodes := make([]*enode.Node, len(lookupTestnet.dists[256])) 79 for i := range lookupTestnet.dists[256] { 80 bootnodes[i] = lookupTestnet.node(256, i) 81 } 82 fillTable(test.table, bootnodes, true) 83 wg.Add(1) 84 go func() { 85 serveTestnet(test, lookupTestnet) 86 wg.Done() 87 }() 88 89 // Create the iterator and collect the nodes it yields. 90 iter := test.udp.RandomNodes() 91 seen := make(map[enode.ID]*enode.Node) 92 for limit := lookupTestnet.len(); iter.Next() && len(seen) < limit; { 93 seen[iter.Node().ID()] = iter.Node() 94 } 95 iter.Close() 96 97 // Check that all nodes in lookupTestnet were seen by the iterator. 98 results := make([]*enode.Node, 0, len(seen)) 99 for _, n := range seen { 100 results = append(results, n) 101 } 102 sortByID(results) 103 want := lookupTestnet.nodes() 104 if err := checkNodesEqual(results, want); err != nil { 105 t.Fatal(err) 106 } 107 } 108 109 // TestUDPv4_LookupIteratorClose checks that lookupIterator ends when its Close 110 // method is called. 111 func TestUDPv4_LookupIteratorClose(t *testing.T) { 112 t.Parallel() 113 test := newUDPTest(t) 114 var wg sync.WaitGroup 115 defer func() { 116 test.close() 117 wg.Wait() 118 }() 119 120 // Seed table with initial nodes. 121 bootnodes := make([]*enode.Node, len(lookupTestnet.dists[256])) 122 for i := range lookupTestnet.dists[256] { 123 bootnodes[i] = lookupTestnet.node(256, i) 124 } 125 fillTable(test.table, bootnodes, true) 126 127 wg.Add(1) 128 go func() { 129 serveTestnet(test, lookupTestnet) 130 wg.Done() 131 }() 132 133 it := test.udp.RandomNodes() 134 if ok := it.Next(); !ok || it.Node() == nil { 135 t.Fatalf("iterator didn't return any node") 136 } 137 138 it.Close() 139 140 ncalls := 0 141 for ; ncalls < 100 && it.Next(); ncalls++ { 142 if it.Node() == nil { 143 t.Error("iterator returned Node() == nil node after Next() == true") 144 } 145 } 146 t.Logf("iterator returned %d nodes after close", ncalls) 147 if it.Next() { 148 t.Errorf("Next() == true after close and %d more calls", ncalls) 149 } 150 if n := it.Node(); n != nil { 151 t.Errorf("iterator returned non-nil node after close and %d more calls", ncalls) 152 } 153 } 154 155 func serveTestnet(test *udpTest, testnet *preminedTestnet) { 156 for done := false; !done; { 157 done = test.waitPacketOut(func(p v4wire.Packet, to netip.AddrPort, hash []byte) { 158 n, key := testnet.nodeByAddr(to) 159 switch p.(type) { 160 case *v4wire.Ping: 161 test.packetInFrom(nil, key, to, &v4wire.Pong{Expiration: futureExp, ReplyTok: hash}) 162 case *v4wire.Findnode: 163 dist := enode.LogDist(n.ID(), testnet.target.ID()) 164 nodes := testnet.nodesAtDistance(dist - 1) 165 test.packetInFrom(nil, key, to, &v4wire.Neighbors{Expiration: futureExp, Nodes: nodes}) 166 } 167 }) 168 } 169 } 170 171 // checkLookupResults verifies that the results of a lookup are the closest nodes to 172 // the testnet's target. 173 func checkLookupResults(t *testing.T, tn *preminedTestnet, results []*enode.Node) { 174 t.Helper() 175 t.Logf("results:") 176 for _, e := range results { 177 t.Logf(" ld=%d, %x", enode.LogDist(tn.target.ID(), e.ID()), e.ID().Bytes()) 178 } 179 if hasDuplicates(results) { 180 t.Errorf("result set contains duplicate entries") 181 } 182 if !sortedByDistanceTo(tn.target.ID(), results) { 183 t.Errorf("result set not sorted by distance to target") 184 } 185 wantNodes := tn.closest(len(results)) 186 if err := checkNodesEqual(results, wantNodes); err != nil { 187 t.Error(err) 188 } 189 } 190 191 // This is the test network for the Lookup test. 192 // The nodes were obtained by running lookupTestnet.mine with a random NodeID as target. 193 var lookupTestnet = &preminedTestnet{ 194 target: hexEncPubkey("5d485bdcbe9bc89314a10ae9231e429d33853e3a8fa2af39f5f827370a2e4185e344ace5d16237491dad41f278f1d3785210d29ace76cd627b9147ee340b1125"), 195 dists: [257][]*ecdsa.PrivateKey{ 196 251: { 197 hexEncPrivkey("29738ba0c1a4397d6a65f292eee07f02df8e58d41594ba2be3cf84ce0fc58169"), 198 hexEncPrivkey("511b1686e4e58a917f7f848e9bf5539d206a68f5ad6b54b552c2399fe7d174ae"), 199 hexEncPrivkey("d09e5eaeec0fd596236faed210e55ef45112409a5aa7f3276d26646080dcfaeb"), 200 hexEncPrivkey("c1e20dbbf0d530e50573bd0a260b32ec15eb9190032b4633d44834afc8afe578"), 201 hexEncPrivkey("ed5f38f5702d92d306143e5d9154fb21819777da39af325ea359f453d179e80b"), 202 }, 203 252: { 204 hexEncPrivkey("1c9b1cafbec00848d2c174b858219914b42a7d5c9359b1ca03fd650e8239ae94"), 205 hexEncPrivkey("e0e1e8db4a6f13c1ffdd3e96b72fa7012293ced187c9dcdcb9ba2af37a46fa10"), 206 hexEncPrivkey("3d53823e0a0295cb09f3e11d16c1b44d07dd37cec6f739b8df3a590189fe9fb9"), 207 }, 208 253: { 209 hexEncPrivkey("2d0511ae9bf590166597eeab86b6f27b1ab761761eaea8965487b162f8703847"), 210 hexEncPrivkey("6cfbd7b8503073fc3dbdb746a7c672571648d3bd15197ccf7f7fef3d904f53a2"), 211 hexEncPrivkey("a30599b12827b69120633f15b98a7f6bc9fc2e9a0fd6ae2ebb767c0e64d743ab"), 212 hexEncPrivkey("14a98db9b46a831d67eff29f3b85b1b485bb12ae9796aea98d91be3dc78d8a91"), 213 hexEncPrivkey("2369ff1fc1ff8ca7d20b17e2673adc3365c3674377f21c5d9dafaff21fe12e24"), 214 hexEncPrivkey("9ae91101d6b5048607f41ec0f690ef5d09507928aded2410aabd9237aa2727d7"), 215 hexEncPrivkey("05e3c59090a3fd1ae697c09c574a36fcf9bedd0afa8fe3946f21117319ca4973"), 216 hexEncPrivkey("06f31c5ea632658f718a91a1b1b9ae4b7549d7b3bc61cbc2be5f4a439039f3ad"), 217 }, 218 254: { 219 hexEncPrivkey("dec742079ec00ff4ec1284d7905bc3de2366f67a0769431fd16f80fd68c58a7c"), 220 hexEncPrivkey("ff02c8861fa12fbd129d2a95ea663492ef9c1e51de19dcfbbfe1c59894a28d2b"), 221 hexEncPrivkey("4dded9e4eefcbce4262be4fd9e8a773670ab0b5f448f286ec97dfc8cf681444a"), 222 hexEncPrivkey("750d931e2a8baa2c9268cb46b7cd851f4198018bed22f4dceb09dd334a2395f6"), 223 hexEncPrivkey("ce1435a956a98ffec484cd11489c4f165cf1606819ab6b521cee440f0c677e9e"), 224 hexEncPrivkey("996e7f8d1638be92d7328b4770f47e5420fc4bafecb4324fd33b1f5d9f403a75"), 225 hexEncPrivkey("ebdc44e77a6cc0eb622e58cf3bb903c3da4c91ca75b447b0168505d8fc308b9c"), 226 hexEncPrivkey("46bd1eddcf6431bea66fc19ebc45df191c1c7d6ed552dcdc7392885009c322f0"), 227 }, 228 255: { 229 hexEncPrivkey("da8645f90826e57228d9ea72aff84500060ad111a5d62e4af831ed8e4b5acfb8"), 230 hexEncPrivkey("3c944c5d9af51d4c1d43f5d0f3a1a7ef65d5e82744d669b58b5fed242941a566"), 231 hexEncPrivkey("5ebcde76f1d579eebf6e43b0ffe9157e65ffaa391175d5b9aa988f47df3e33da"), 232 hexEncPrivkey("97f78253a7d1d796e4eaabce721febcc4550dd68fb11cc818378ba807a2cb7de"), 233 hexEncPrivkey("a38cd7dc9b4079d1c0406afd0fdb1165c285f2c44f946eca96fc67772c988c7d"), 234 hexEncPrivkey("d64cbb3ffdf712c372b7a22a176308ef8f91861398d5dbaf326fd89c6eaeef1c"), 235 hexEncPrivkey("d269609743ef29d6446e3355ec647e38d919c82a4eb5837e442efd7f4218944f"), 236 hexEncPrivkey("d8f7bcc4a530efde1d143717007179e0d9ace405ddaaf151c4d863753b7fd64c"), 237 }, 238 256: { 239 hexEncPrivkey("8c5b422155d33ea8e9d46f71d1ad3e7b24cb40051413ffa1a81cff613d243ba9"), 240 hexEncPrivkey("937b1af801def4e8f5a3a8bd225a8bcff1db764e41d3e177f2e9376e8dd87233"), 241 hexEncPrivkey("120260dce739b6f71f171da6f65bc361b5fad51db74cf02d3e973347819a6518"), 242 hexEncPrivkey("1fa56cf25d4b46c2bf94e82355aa631717b63190785ac6bae545a88aadc304a9"), 243 hexEncPrivkey("3c38c503c0376f9b4adcbe935d5f4b890391741c764f61b03cd4d0d42deae002"), 244 hexEncPrivkey("3a54af3e9fa162bc8623cdf3e5d9b70bf30ade1d54cc3abea8659aba6cff471f"), 245 hexEncPrivkey("6799a02ea1999aefdcbcc4d3ff9544478be7365a328d0d0f37c26bd95ade0cda"), 246 hexEncPrivkey("e24a7bc9051058f918646b0f6e3d16884b2a55a15553b89bab910d55ebc36116"), 247 }, 248 }, 249 } 250 251 type preminedTestnet struct { 252 target v4wire.Pubkey 253 dists [hashBits + 1][]*ecdsa.PrivateKey 254 } 255 256 func (tn *preminedTestnet) len() int { 257 n := 0 258 for _, keys := range tn.dists { 259 n += len(keys) 260 } 261 return n 262 } 263 264 func (tn *preminedTestnet) nodes() []*enode.Node { 265 result := make([]*enode.Node, 0, tn.len()) 266 for dist, keys := range tn.dists { 267 for index := range keys { 268 result = append(result, tn.node(dist, index)) 269 } 270 } 271 sortByID(result) 272 return result 273 } 274 275 func (tn *preminedTestnet) node(dist, index int) *enode.Node { 276 key := tn.dists[dist][index] 277 rec := new(enr.Record) 278 rec.Set(enr.IP{127, byte(dist >> 8), byte(dist), byte(index)}) 279 rec.Set(enr.UDP(5000)) 280 enode.SignV4(rec, key) 281 n, _ := enode.New(enode.ValidSchemes, rec) 282 return n 283 } 284 285 func (tn *preminedTestnet) nodeByAddr(addr netip.AddrPort) (*enode.Node, *ecdsa.PrivateKey) { 286 ip := addr.Addr().As4() 287 dist := int(ip[1])<<8 + int(ip[2]) 288 index := int(ip[3]) 289 key := tn.dists[dist][index] 290 return tn.node(dist, index), key 291 } 292 293 func (tn *preminedTestnet) nodesAtDistance(dist int) []v4wire.Node { 294 result := make([]v4wire.Node, len(tn.dists[dist])) 295 for i := range result { 296 result[i] = nodeToRPC(tn.node(dist, i)) 297 } 298 return result 299 } 300 301 func (tn *preminedTestnet) neighborsAtDistances(base *enode.Node, distances []uint, elems int) []*enode.Node { 302 var result []*enode.Node 303 for d := range lookupTestnet.dists { 304 for i := range lookupTestnet.dists[d] { 305 n := lookupTestnet.node(d, i) 306 d := enode.LogDist(base.ID(), n.ID()) 307 if slices.Contains(distances, uint(d)) { 308 result = append(result, n) 309 if len(result) >= elems { 310 return result 311 } 312 } 313 } 314 } 315 return result 316 } 317 318 func (tn *preminedTestnet) closest(n int) (nodes []*enode.Node) { 319 for d := range tn.dists { 320 for i := range tn.dists[d] { 321 nodes = append(nodes, tn.node(d, i)) 322 } 323 } 324 slices.SortFunc(nodes, func(a, b *enode.Node) int { 325 return enode.DistCmp(tn.target.ID(), a.ID(), b.ID()) 326 }) 327 return nodes[:n] 328 } 329 330 var _ = (*preminedTestnet).mine // avoid linter warning about mine being dead code. 331 332 // mine generates a testnet struct literal with nodes at 333 // various distances to the network's target. 334 func (tn *preminedTestnet) mine() { 335 // Clear existing slices first (useful when re-mining). 336 for i := range tn.dists { 337 tn.dists[i] = nil 338 } 339 340 targetSha := tn.target.ID() 341 found, need := 0, 40 342 for found < need { 343 k := newkey() 344 ld := enode.LogDist(targetSha, v4wire.EncodePubkey(&k.PublicKey).ID()) 345 if len(tn.dists[ld]) < 8 { 346 tn.dists[ld] = append(tn.dists[ld], k) 347 found++ 348 fmt.Printf("found ID with ld %d (%d/%d)\n", ld, found, need) 349 } 350 } 351 fmt.Printf("&preminedTestnet{\n") 352 fmt.Printf(" target: hexEncPubkey(\"%x\"),\n", tn.target[:]) 353 fmt.Printf(" dists: [%d][]*ecdsa.PrivateKey{\n", len(tn.dists)) 354 for ld, ns := range tn.dists { 355 if len(ns) == 0 { 356 continue 357 } 358 fmt.Printf(" %d: {\n", ld) 359 for _, key := range ns { 360 fmt.Printf(" hexEncPrivkey(\"%x\"),\n", crypto.FromECDSA(key)) 361 } 362 fmt.Printf(" },\n") 363 } 364 fmt.Printf(" },\n") 365 fmt.Printf("}\n") 366 }