client_test.go 13 KB

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