github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/p2p/dnsdisc/client_test.go (about) 1 // Copyright 2018 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 dnsdisc 18 19 import ( 20 "context" 21 "crypto/ecdsa" 22 "errors" 23 "math/rand" 24 "reflect" 25 "testing" 26 "time" 27 28 "github.com/davecgh/go-spew/spew" 29 30 "github.com/scroll-tech/go-ethereum/common/mclock" 31 "github.com/scroll-tech/go-ethereum/crypto" 32 "github.com/scroll-tech/go-ethereum/internal/testlog" 33 "github.com/scroll-tech/go-ethereum/log" 34 "github.com/scroll-tech/go-ethereum/p2p/enode" 35 "github.com/scroll-tech/go-ethereum/p2p/enr" 36 ) 37 38 const ( 39 signingKeySeed = 0x111111 40 nodesSeed1 = 0x2945237 41 nodesSeed2 = 0x4567299 42 ) 43 44 func TestClientSyncTree(t *testing.T) { 45 r := mapResolver{ 46 "n": "enrtree-root:v1 e=JWXYDBPXYWG6FX3GMDIBFA6CJ4 l=C7HRFPF3BLGF3YR4DY5KX3SMBE seq=1 sig=o908WmNp7LibOfPsr4btQwatZJ5URBr2ZAuxvK4UWHlsB9sUOTJQaGAlLPVAhM__XJesCHxLISo94z5Z2a463gA", 47 "C7HRFPF3BLGF3YR4DY5KX3SMBE.n": "enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org", 48 "JWXYDBPXYWG6FX3GMDIBFA6CJ4.n": "enrtree-branch:2XS2367YHAXJFGLZHVAWLQD4ZY,H4FHT4B454P6UXFD7JCYQ5PWDY,MHTDO6TMUBRIA2XWG5LUDACK24", 49 "2XS2367YHAXJFGLZHVAWLQD4ZY.n": "enr:-HW4QOFzoVLaFJnNhbgMoDXPnOvcdVuj7pDpqRvh6BRDO68aVi5ZcjB3vzQRZH2IcLBGHzo8uUN3snqmgTiE56CH3AMBgmlkgnY0iXNlY3AyNTZrMaECC2_24YYkYHEgdzxlSNKQEnHhuNAbNlMlWJxrJxbAFvA", 50 "H4FHT4B454P6UXFD7JCYQ5PWDY.n": "enr:-HW4QAggRauloj2SDLtIHN1XBkvhFZ1vtf1raYQp9TBW2RD5EEawDzbtSmlXUfnaHcvwOizhVYLtr7e6vw7NAf6mTuoCgmlkgnY0iXNlY3AyNTZrMaECjrXI8TLNXU0f8cthpAMxEshUyQlK-AM0PW2wfrnacNI", 51 "MHTDO6TMUBRIA2XWG5LUDACK24.n": "enr:-HW4QLAYqmrwllBEnzWWs7I5Ev2IAs7x_dZlbYdRdMUx5EyKHDXp7AV5CkuPGUPdvbv1_Ms1CPfhcGCvSElSosZmyoqAgmlkgnY0iXNlY3AyNTZrMaECriawHKWdDRk2xeZkrOXBQ0dfMFLHY4eENZwdufn1S1o", 52 } 53 var ( 54 wantNodes = testNodes(0x29452, 3) 55 wantLinks = []string{"enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org"} 56 wantSeq = uint(1) 57 ) 58 59 c := NewClient(Config{Resolver: r, Logger: testlog.Logger(t, log.LvlTrace)}) 60 stree, err := c.SyncTree("enrtree://AKPYQIUQIL7PSIACI32J7FGZW56E5FKHEFCCOFHILBIMW3M6LWXS2@n") 61 if err != nil { 62 t.Fatal("sync error:", err) 63 } 64 if !reflect.DeepEqual(sortByID(stree.Nodes()), sortByID(wantNodes)) { 65 t.Errorf("wrong nodes in synced tree:\nhave %v\nwant %v", spew.Sdump(stree.Nodes()), spew.Sdump(wantNodes)) 66 } 67 if !reflect.DeepEqual(stree.Links(), wantLinks) { 68 t.Errorf("wrong links in synced tree: %v", stree.Links()) 69 } 70 if stree.Seq() != wantSeq { 71 t.Errorf("synced tree has wrong seq: %d", stree.Seq()) 72 } 73 } 74 75 // In this test, syncing the tree fails because it contains an invalid ENR entry. 76 func TestClientSyncTreeBadNode(t *testing.T) { 77 // var b strings.Builder 78 // b.WriteString(enrPrefix) 79 // b.WriteString("-----") 80 // badHash := subdomain(&b) 81 // tree, _ := MakeTree(3, nil, []string{"enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org"}) 82 // tree.entries[badHash] = &b 83 // tree.root.eroot = badHash 84 // url, _ := tree.Sign(testKey(signingKeySeed), "n") 85 // fmt.Println(url) 86 // fmt.Printf("%#v\n", tree.ToTXT("n")) 87 88 r := mapResolver{ 89 "n": "enrtree-root:v1 e=INDMVBZEEQ4ESVYAKGIYU74EAA l=C7HRFPF3BLGF3YR4DY5KX3SMBE seq=3 sig=Vl3AmunLur0JZ3sIyJPSH6A3Vvdp4F40jWQeCmkIhmcgwE4VC5U9wpK8C_uL_CMY29fd6FAhspRvq2z_VysTLAA", 90 "C7HRFPF3BLGF3YR4DY5KX3SMBE.n": "enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org", 91 "INDMVBZEEQ4ESVYAKGIYU74EAA.n": "enr:-----", 92 } 93 c := NewClient(Config{Resolver: r, Logger: testlog.Logger(t, log.LvlTrace)}) 94 _, err := c.SyncTree("enrtree://AKPYQIUQIL7PSIACI32J7FGZW56E5FKHEFCCOFHILBIMW3M6LWXS2@n") 95 wantErr := nameError{name: "INDMVBZEEQ4ESVYAKGIYU74EAA.n", err: entryError{typ: "enr", err: errInvalidENR}} 96 if err != wantErr { 97 t.Fatalf("expected sync error %q, got %q", wantErr, err) 98 } 99 } 100 101 // This test checks that randomIterator finds all entries. 102 func TestIterator(t *testing.T) { 103 nodes := testNodes(nodesSeed1, 30) 104 tree, url := makeTestTree("n", nodes, nil) 105 r := mapResolver(tree.ToTXT("n")) 106 c := NewClient(Config{ 107 Resolver: r, 108 Logger: testlog.Logger(t, log.LvlTrace), 109 RateLimit: 500, 110 }) 111 it, err := c.NewIterator(url) 112 if err != nil { 113 t.Fatal(err) 114 } 115 116 checkIterator(t, it, nodes) 117 } 118 119 func TestIteratorCloseWithoutNext(t *testing.T) { 120 tree1, url1 := makeTestTree("t1", nil, nil) 121 c := NewClient(Config{Resolver: newMapResolver(tree1.ToTXT("t1"))}) 122 it, err := c.NewIterator(url1) 123 if err != nil { 124 t.Fatal(err) 125 } 126 127 it.Close() 128 ok := it.Next() 129 if ok { 130 t.Fatal("Next returned true after Close") 131 } 132 } 133 134 // This test checks if closing randomIterator races. 135 func TestIteratorClose(t *testing.T) { 136 nodes := testNodes(nodesSeed1, 500) 137 tree1, url1 := makeTestTree("t1", nodes, nil) 138 c := NewClient(Config{Resolver: newMapResolver(tree1.ToTXT("t1"))}) 139 it, err := c.NewIterator(url1) 140 if err != nil { 141 t.Fatal(err) 142 } 143 144 done := make(chan struct{}) 145 go func() { 146 for it.Next() { 147 _ = it.Node() 148 } 149 close(done) 150 }() 151 152 time.Sleep(50 * time.Millisecond) 153 it.Close() 154 <-done 155 } 156 157 // This test checks that randomIterator traverses linked trees as well as explicitly added trees. 158 func TestIteratorLinks(t *testing.T) { 159 nodes := testNodes(nodesSeed1, 40) 160 tree1, url1 := makeTestTree("t1", nodes[:10], nil) 161 tree2, url2 := makeTestTree("t2", nodes[10:], []string{url1}) 162 c := NewClient(Config{ 163 Resolver: newMapResolver(tree1.ToTXT("t1"), tree2.ToTXT("t2")), 164 Logger: testlog.Logger(t, log.LvlTrace), 165 RateLimit: 500, 166 }) 167 it, err := c.NewIterator(url2) 168 if err != nil { 169 t.Fatal(err) 170 } 171 172 checkIterator(t, it, nodes) 173 } 174 175 // This test verifies that randomIterator re-checks the root of the tree to catch 176 // updates to nodes. 177 func TestIteratorNodeUpdates(t *testing.T) { 178 var ( 179 clock = new(mclock.Simulated) 180 nodes = testNodes(nodesSeed1, 30) 181 resolver = newMapResolver() 182 c = NewClient(Config{ 183 Resolver: resolver, 184 Logger: testlog.Logger(t, log.LvlTrace), 185 RecheckInterval: 20 * time.Minute, 186 RateLimit: 500, 187 }) 188 ) 189 c.clock = clock 190 tree1, url := makeTestTree("n", nodes[:25], nil) 191 it, err := c.NewIterator(url) 192 if err != nil { 193 t.Fatal(err) 194 } 195 196 // Sync the original tree. 197 resolver.add(tree1.ToTXT("n")) 198 checkIterator(t, it, nodes[:25]) 199 200 // Ensure RandomNode returns the new nodes after the tree is updated. 201 updateSomeNodes(nodesSeed1, nodes) 202 tree2, _ := makeTestTree("n", nodes, nil) 203 resolver.clear() 204 resolver.add(tree2.ToTXT("n")) 205 t.Log("tree updated") 206 207 clock.Run(c.cfg.RecheckInterval + 1*time.Second) 208 checkIterator(t, it, nodes) 209 } 210 211 // This test checks that the tree root is rechecked when a couple of leaf 212 // requests have failed. The test is just like TestIteratorNodeUpdates, but 213 // without advancing the clock by recheckInterval after the tree update. 214 func TestIteratorRootRecheckOnFail(t *testing.T) { 215 var ( 216 clock = new(mclock.Simulated) 217 nodes = testNodes(nodesSeed1, 30) 218 resolver = newMapResolver() 219 c = NewClient(Config{ 220 Resolver: resolver, 221 Logger: testlog.Logger(t, log.LvlTrace), 222 RecheckInterval: 20 * time.Minute, 223 RateLimit: 500, 224 // Disabling the cache is required for this test because the client doesn't 225 // notice leaf failures if all records are cached. 226 CacheLimit: 1, 227 }) 228 ) 229 c.clock = clock 230 tree1, url := makeTestTree("n", nodes[:25], nil) 231 it, err := c.NewIterator(url) 232 if err != nil { 233 t.Fatal(err) 234 } 235 236 // Sync the original tree. 237 resolver.add(tree1.ToTXT("n")) 238 checkIterator(t, it, nodes[:25]) 239 240 // Ensure RandomNode returns the new nodes after the tree is updated. 241 updateSomeNodes(nodesSeed1, nodes) 242 tree2, _ := makeTestTree("n", nodes, nil) 243 resolver.clear() 244 resolver.add(tree2.ToTXT("n")) 245 t.Log("tree updated") 246 247 checkIterator(t, it, nodes) 248 } 249 250 // This test checks that the iterator works correctly when the tree is initially empty. 251 func TestIteratorEmptyTree(t *testing.T) { 252 var ( 253 clock = new(mclock.Simulated) 254 nodes = testNodes(nodesSeed1, 1) 255 resolver = newMapResolver() 256 c = NewClient(Config{ 257 Resolver: resolver, 258 Logger: testlog.Logger(t, log.LvlTrace), 259 RecheckInterval: 20 * time.Minute, 260 RateLimit: 500, 261 }) 262 ) 263 c.clock = clock 264 tree1, url := makeTestTree("n", nil, nil) 265 tree2, _ := makeTestTree("n", nodes, nil) 266 resolver.add(tree1.ToTXT("n")) 267 268 // Start the iterator. 269 node := make(chan *enode.Node) 270 it, err := c.NewIterator(url) 271 if err != nil { 272 t.Fatal(err) 273 } 274 go func() { 275 it.Next() 276 node <- it.Node() 277 }() 278 279 // Wait for the client to get stuck in waitForRootUpdates. 280 clock.WaitForTimers(1) 281 282 // Now update the root. 283 resolver.add(tree2.ToTXT("n")) 284 285 // Wait for it to pick up the root change. 286 clock.Run(c.cfg.RecheckInterval) 287 select { 288 case n := <-node: 289 if n.ID() != nodes[0].ID() { 290 t.Fatalf("wrong node returned") 291 } 292 case <-time.After(5 * time.Second): 293 t.Fatal("it.Next() did not unblock within 5s of real time") 294 } 295 } 296 297 // updateSomeNodes applies ENR updates to some of the given nodes. 298 func updateSomeNodes(keySeed int64, nodes []*enode.Node) { 299 keys := testKeys(nodesSeed1, len(nodes)) 300 for i, n := range nodes[:len(nodes)/2] { 301 r := n.Record() 302 r.Set(enr.IP{127, 0, 0, 1}) 303 r.SetSeq(55) 304 enode.SignV4(r, keys[i]) 305 n2, _ := enode.New(enode.ValidSchemes, r) 306 nodes[i] = n2 307 } 308 } 309 310 // This test verifies that randomIterator re-checks the root of the tree to catch 311 // updates to links. 312 func TestIteratorLinkUpdates(t *testing.T) { 313 var ( 314 clock = new(mclock.Simulated) 315 nodes = testNodes(nodesSeed1, 30) 316 resolver = newMapResolver() 317 c = NewClient(Config{ 318 Resolver: resolver, 319 Logger: testlog.Logger(t, log.LvlTrace), 320 RecheckInterval: 20 * time.Minute, 321 RateLimit: 500, 322 }) 323 ) 324 c.clock = clock 325 tree3, url3 := makeTestTree("t3", nodes[20:30], nil) 326 tree2, url2 := makeTestTree("t2", nodes[10:20], nil) 327 tree1, url1 := makeTestTree("t1", nodes[0:10], []string{url2}) 328 resolver.add(tree1.ToTXT("t1")) 329 resolver.add(tree2.ToTXT("t2")) 330 resolver.add(tree3.ToTXT("t3")) 331 332 it, err := c.NewIterator(url1) 333 if err != nil { 334 t.Fatal(err) 335 } 336 337 // Sync tree1 using RandomNode. 338 checkIterator(t, it, nodes[:20]) 339 340 // Add link to tree3, remove link to tree2. 341 tree1, _ = makeTestTree("t1", nodes[:10], []string{url3}) 342 resolver.add(tree1.ToTXT("t1")) 343 t.Log("tree1 updated") 344 345 clock.Run(c.cfg.RecheckInterval + 1*time.Second) 346 347 var wantNodes []*enode.Node 348 wantNodes = append(wantNodes, tree1.Nodes()...) 349 wantNodes = append(wantNodes, tree3.Nodes()...) 350 checkIterator(t, it, wantNodes) 351 352 // Check that linked trees are GCed when they're no longer referenced. 353 knownTrees := it.(*randomIterator).trees 354 if len(knownTrees) != 2 { 355 t.Errorf("client knows %d trees, want 2", len(knownTrees)) 356 } 357 } 358 359 func checkIterator(t *testing.T, it enode.Iterator, wantNodes []*enode.Node) { 360 t.Helper() 361 362 var ( 363 want = make(map[enode.ID]*enode.Node) 364 maxCalls = len(wantNodes) * 3 365 calls = 0 366 ) 367 for _, n := range wantNodes { 368 want[n.ID()] = n 369 } 370 for ; len(want) > 0 && calls < maxCalls; calls++ { 371 if !it.Next() { 372 t.Fatalf("Next returned false (call %d)", calls) 373 } 374 n := it.Node() 375 delete(want, n.ID()) 376 } 377 t.Logf("checkIterator called Next %d times to find %d nodes", calls, len(wantNodes)) 378 for _, n := range want { 379 t.Errorf("iterator didn't discover node %v", n.ID()) 380 } 381 } 382 383 func makeTestTree(domain string, nodes []*enode.Node, links []string) (*Tree, string) { 384 tree, err := MakeTree(1, nodes, links) 385 if err != nil { 386 panic(err) 387 } 388 url, err := tree.Sign(testKey(signingKeySeed), domain) 389 if err != nil { 390 panic(err) 391 } 392 return tree, url 393 } 394 395 // testKeys creates deterministic private keys for testing. 396 func testKeys(seed int64, n int) []*ecdsa.PrivateKey { 397 rand := rand.New(rand.NewSource(seed)) 398 keys := make([]*ecdsa.PrivateKey, n) 399 for i := 0; i < n; i++ { 400 key, err := ecdsa.GenerateKey(crypto.S256(), rand) 401 if err != nil { 402 panic("can't generate key: " + err.Error()) 403 } 404 keys[i] = key 405 } 406 return keys 407 } 408 409 func testKey(seed int64) *ecdsa.PrivateKey { 410 return testKeys(seed, 1)[0] 411 } 412 413 func testNodes(seed int64, n int) []*enode.Node { 414 keys := testKeys(seed, n) 415 nodes := make([]*enode.Node, n) 416 for i, key := range keys { 417 record := new(enr.Record) 418 record.SetSeq(uint64(i)) 419 enode.SignV4(record, key) 420 n, err := enode.New(enode.ValidSchemes, record) 421 if err != nil { 422 panic(err) 423 } 424 nodes[i] = n 425 } 426 return nodes 427 } 428 429 func testNode(seed int64) *enode.Node { 430 return testNodes(seed, 1)[0] 431 } 432 433 type mapResolver map[string]string 434 435 func newMapResolver(maps ...map[string]string) mapResolver { 436 mr := make(mapResolver) 437 for _, m := range maps { 438 mr.add(m) 439 } 440 return mr 441 } 442 443 func (mr mapResolver) clear() { 444 for k := range mr { 445 delete(mr, k) 446 } 447 } 448 449 func (mr mapResolver) add(m map[string]string) { 450 for k, v := range m { 451 mr[k] = v 452 } 453 } 454 455 func (mr mapResolver) LookupTXT(ctx context.Context, name string) ([]string, error) { 456 if record, ok := mr[name]; ok { 457 return []string{record}, nil 458 } 459 return nil, errors.New("not found") 460 }