sync.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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. "math/rand"
  21. "time"
  22. "github.com/ethereum/go-ethereum/common/mclock"
  23. "github.com/ethereum/go-ethereum/p2p/enode"
  24. )
  25. // clientTree is a full tree being synced.
  26. type clientTree struct {
  27. c *Client
  28. loc *linkEntry
  29. root *rootEntry
  30. lastRootCheck mclock.AbsTime // last revalidation of root
  31. enrs *subtreeSync
  32. links *subtreeSync
  33. linkCache linkCache
  34. }
  35. func newClientTree(c *Client, loc *linkEntry) *clientTree {
  36. ct := &clientTree{c: c, loc: loc}
  37. ct.linkCache.self = ct
  38. return ct
  39. }
  40. func (ct *clientTree) matchPubkey(key *ecdsa.PublicKey) bool {
  41. return keysEqual(ct.loc.pubkey, key)
  42. }
  43. func keysEqual(k1, k2 *ecdsa.PublicKey) bool {
  44. return k1.Curve == k2.Curve && k1.X.Cmp(k2.X) == 0 && k1.Y.Cmp(k2.Y) == 0
  45. }
  46. // syncAll retrieves all entries of the tree.
  47. func (ct *clientTree) syncAll(dest map[string]entry) error {
  48. if err := ct.updateRoot(); err != nil {
  49. return err
  50. }
  51. if err := ct.links.resolveAll(dest); err != nil {
  52. return err
  53. }
  54. if err := ct.enrs.resolveAll(dest); err != nil {
  55. return err
  56. }
  57. return nil
  58. }
  59. // syncRandom retrieves a single entry of the tree. The Node return value
  60. // is non-nil if the entry was a node.
  61. func (ct *clientTree) syncRandom(ctx context.Context) (*enode.Node, error) {
  62. if ct.rootUpdateDue() {
  63. if err := ct.updateRoot(); err != nil {
  64. return nil, err
  65. }
  66. }
  67. // Link tree sync has priority, run it to completion before syncing ENRs.
  68. if !ct.links.done() {
  69. err := ct.syncNextLink(ctx)
  70. return nil, err
  71. }
  72. // Sync next random entry in ENR tree. Once every node has been visited, we simply
  73. // start over. This is fine because entries are cached.
  74. if ct.enrs.done() {
  75. ct.enrs = newSubtreeSync(ct.c, ct.loc, ct.root.eroot, false)
  76. }
  77. return ct.syncNextRandomENR(ctx)
  78. }
  79. func (ct *clientTree) syncNextLink(ctx context.Context) error {
  80. hash := ct.links.missing[0]
  81. e, err := ct.links.resolveNext(ctx, hash)
  82. if err != nil {
  83. return err
  84. }
  85. ct.links.missing = ct.links.missing[1:]
  86. if le, ok := e.(*linkEntry); ok {
  87. lt, err := ct.c.ensureTree(le)
  88. if err != nil {
  89. return err
  90. }
  91. ct.linkCache.add(lt)
  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.url()
  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.linkCache.reset()
  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 *subtreeEntry:
  190. ts.missing = append(ts.missing, e.children...)
  191. }
  192. return e, nil
  193. }
  194. // linkCache tracks the links of a tree.
  195. type linkCache struct {
  196. self *clientTree
  197. directM map[*clientTree]struct{} // direct links
  198. allM map[*clientTree]struct{} // direct & transitive links
  199. }
  200. // reset clears the cache.
  201. func (lc *linkCache) reset() {
  202. lc.directM = nil
  203. lc.allM = nil
  204. }
  205. // add adds a direct link to the cache.
  206. func (lc *linkCache) add(ct *clientTree) {
  207. if lc.directM == nil {
  208. lc.directM = make(map[*clientTree]struct{})
  209. }
  210. if _, ok := lc.directM[ct]; !ok {
  211. lc.invalidate()
  212. }
  213. lc.directM[ct] = struct{}{}
  214. }
  215. // invalidate resets the cache of transitive links.
  216. func (lc *linkCache) invalidate() {
  217. lc.allM = nil
  218. }
  219. // valid returns true when the cache of transitive links is up-to-date.
  220. func (lc *linkCache) valid() bool {
  221. // Re-check validity of child caches to catch updates.
  222. for ct := range lc.allM {
  223. if ct != lc.self && !ct.linkCache.valid() {
  224. lc.allM = nil
  225. break
  226. }
  227. }
  228. return lc.allM != nil
  229. }
  230. // all returns all trees reachable through the cache.
  231. func (lc *linkCache) all() map[*clientTree]struct{} {
  232. if lc.valid() {
  233. return lc.allM
  234. }
  235. // Remake lc.allM it by taking the union of all() across children.
  236. m := make(map[*clientTree]struct{})
  237. if lc.self != nil {
  238. m[lc.self] = struct{}{}
  239. }
  240. for ct := range lc.directM {
  241. m[ct] = struct{}{}
  242. for lt := range ct.linkCache.all() {
  243. m[lt] = struct{}{}
  244. }
  245. }
  246. lc.allM = m
  247. return m
  248. }