sync.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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. "math/rand"
  20. "time"
  21. "github.com/ethereum/go-ethereum/common/mclock"
  22. "github.com/ethereum/go-ethereum/p2p/enode"
  23. )
  24. // clientTree is a full tree being synced.
  25. type clientTree struct {
  26. c *Client
  27. loc *linkEntry // link to this tree
  28. lastRootCheck mclock.AbsTime // last revalidation of root
  29. root *rootEntry
  30. enrs *subtreeSync
  31. links *subtreeSync
  32. lc *linkCache // tracks all links between all trees
  33. curLinks map[string]struct{} // links contained in this tree
  34. linkGCRoot string // root on which last link GC has run
  35. }
  36. func newClientTree(c *Client, lc *linkCache, loc *linkEntry) *clientTree {
  37. return &clientTree{c: c, lc: lc, loc: loc}
  38. }
  39. // syncAll retrieves all entries of the tree.
  40. func (ct *clientTree) syncAll(dest map[string]entry) error {
  41. if err := ct.updateRoot(); err != nil {
  42. return err
  43. }
  44. if err := ct.links.resolveAll(dest); err != nil {
  45. return err
  46. }
  47. if err := ct.enrs.resolveAll(dest); err != nil {
  48. return err
  49. }
  50. return nil
  51. }
  52. // syncRandom retrieves a single entry of the tree. The Node return value
  53. // is non-nil if the entry was a node.
  54. func (ct *clientTree) syncRandom(ctx context.Context) (*enode.Node, error) {
  55. if ct.rootUpdateDue() {
  56. if err := ct.updateRoot(); err != nil {
  57. return nil, err
  58. }
  59. }
  60. // Link tree sync has priority, run it to completion before syncing ENRs.
  61. if !ct.links.done() {
  62. err := ct.syncNextLink(ctx)
  63. return nil, err
  64. }
  65. ct.gcLinks()
  66. // Sync next random entry in ENR tree. Once every node has been visited, we simply
  67. // start over. This is fine because entries are cached.
  68. if ct.enrs.done() {
  69. ct.enrs = newSubtreeSync(ct.c, ct.loc, ct.root.eroot, false)
  70. }
  71. return ct.syncNextRandomENR(ctx)
  72. }
  73. // gcLinks removes outdated links from the global link cache. GC runs once
  74. // when the link sync finishes.
  75. func (ct *clientTree) gcLinks() {
  76. if !ct.links.done() || ct.root.lroot == ct.linkGCRoot {
  77. return
  78. }
  79. ct.lc.resetLinks(ct.loc.str, ct.curLinks)
  80. ct.linkGCRoot = ct.root.lroot
  81. }
  82. func (ct *clientTree) syncNextLink(ctx context.Context) error {
  83. hash := ct.links.missing[0]
  84. e, err := ct.links.resolveNext(ctx, hash)
  85. if err != nil {
  86. return err
  87. }
  88. ct.links.missing = ct.links.missing[1:]
  89. if dest, ok := e.(*linkEntry); ok {
  90. ct.lc.addLink(ct.loc.str, dest.str)
  91. ct.curLinks[dest.str] = struct{}{}
  92. }
  93. return nil
  94. }
  95. func (ct *clientTree) syncNextRandomENR(ctx context.Context) (*enode.Node, error) {
  96. index := rand.Intn(len(ct.enrs.missing))
  97. hash := ct.enrs.missing[index]
  98. e, err := ct.enrs.resolveNext(ctx, hash)
  99. if err != nil {
  100. return nil, err
  101. }
  102. ct.enrs.missing = removeHash(ct.enrs.missing, index)
  103. if ee, ok := e.(*enrEntry); ok {
  104. return ee.node, nil
  105. }
  106. return nil, nil
  107. }
  108. func (ct *clientTree) String() string {
  109. return ct.loc.String()
  110. }
  111. // removeHash removes the element at index from h.
  112. func removeHash(h []string, index int) []string {
  113. if len(h) == 1 {
  114. return nil
  115. }
  116. last := len(h) - 1
  117. if index < last {
  118. h[index] = h[last]
  119. h[last] = ""
  120. }
  121. return h[:last]
  122. }
  123. // updateRoot ensures that the given tree has an up-to-date root.
  124. func (ct *clientTree) updateRoot() error {
  125. ct.lastRootCheck = ct.c.clock.Now()
  126. ctx, cancel := context.WithTimeout(context.Background(), ct.c.cfg.Timeout)
  127. defer cancel()
  128. root, err := ct.c.resolveRoot(ctx, ct.loc)
  129. if err != nil {
  130. return err
  131. }
  132. ct.root = &root
  133. // Invalidate subtrees if changed.
  134. if ct.links == nil || root.lroot != ct.links.root {
  135. ct.links = newSubtreeSync(ct.c, ct.loc, root.lroot, true)
  136. ct.curLinks = make(map[string]struct{})
  137. }
  138. if ct.enrs == nil || root.eroot != ct.enrs.root {
  139. ct.enrs = newSubtreeSync(ct.c, ct.loc, root.eroot, false)
  140. }
  141. return nil
  142. }
  143. // rootUpdateDue returns true when a root update is needed.
  144. func (ct *clientTree) rootUpdateDue() bool {
  145. return ct.root == nil || time.Duration(ct.c.clock.Now()-ct.lastRootCheck) > ct.c.cfg.RecheckInterval
  146. }
  147. // subtreeSync is the sync of an ENR or link subtree.
  148. type subtreeSync struct {
  149. c *Client
  150. loc *linkEntry
  151. root string
  152. missing []string // missing tree node hashes
  153. link bool // true if this sync is for the link tree
  154. }
  155. func newSubtreeSync(c *Client, loc *linkEntry, root string, link bool) *subtreeSync {
  156. return &subtreeSync{c, loc, root, []string{root}, link}
  157. }
  158. func (ts *subtreeSync) done() bool {
  159. return len(ts.missing) == 0
  160. }
  161. func (ts *subtreeSync) resolveAll(dest map[string]entry) error {
  162. for !ts.done() {
  163. hash := ts.missing[0]
  164. ctx, cancel := context.WithTimeout(context.Background(), ts.c.cfg.Timeout)
  165. e, err := ts.resolveNext(ctx, hash)
  166. cancel()
  167. if err != nil {
  168. return err
  169. }
  170. dest[hash] = e
  171. ts.missing = ts.missing[1:]
  172. }
  173. return nil
  174. }
  175. func (ts *subtreeSync) resolveNext(ctx context.Context, hash string) (entry, error) {
  176. e, err := ts.c.resolveEntry(ctx, ts.loc.domain, hash)
  177. if err != nil {
  178. return nil, err
  179. }
  180. switch e := e.(type) {
  181. case *enrEntry:
  182. if ts.link {
  183. return nil, errENRInLinkTree
  184. }
  185. case *linkEntry:
  186. if !ts.link {
  187. return nil, errLinkInENRTree
  188. }
  189. case *branchEntry:
  190. ts.missing = append(ts.missing, e.children...)
  191. }
  192. return e, nil
  193. }
  194. // linkCache tracks links between trees.
  195. type linkCache struct {
  196. backrefs map[string]map[string]struct{}
  197. changed bool
  198. }
  199. func (lc *linkCache) isReferenced(r string) bool {
  200. return len(lc.backrefs[r]) != 0
  201. }
  202. func (lc *linkCache) addLink(from, to string) {
  203. if _, ok := lc.backrefs[to][from]; ok {
  204. return
  205. }
  206. if lc.backrefs == nil {
  207. lc.backrefs = make(map[string]map[string]struct{})
  208. }
  209. if _, ok := lc.backrefs[to]; !ok {
  210. lc.backrefs[to] = make(map[string]struct{})
  211. }
  212. lc.backrefs[to][from] = struct{}{}
  213. lc.changed = true
  214. }
  215. // resetLinks clears all links of the given tree.
  216. func (lc *linkCache) resetLinks(from string, keep map[string]struct{}) {
  217. stk := []string{from}
  218. for len(stk) > 0 {
  219. item := stk[len(stk)-1]
  220. stk = stk[:len(stk)-1]
  221. for r, refs := range lc.backrefs {
  222. if _, ok := keep[r]; ok {
  223. continue
  224. }
  225. if _, ok := refs[item]; !ok {
  226. continue
  227. }
  228. lc.changed = true
  229. delete(refs, item)
  230. if len(refs) == 0 {
  231. delete(lc.backrefs, r)
  232. stk = append(stk, r)
  233. }
  234. }
  235. }
  236. }