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