github.com/flexpool/go-ethereum@v1.9.7/p2p/dnsdisc/sync.go (about) 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 17 package dnsdisc 18 19 import ( 20 "context" 21 "crypto/ecdsa" 22 "math/rand" 23 "time" 24 25 "github.com/ethereum/go-ethereum/common/mclock" 26 "github.com/ethereum/go-ethereum/p2p/enode" 27 ) 28 29 // clientTree is a full tree being synced. 30 type clientTree struct { 31 c *Client 32 loc *linkEntry 33 root *rootEntry 34 lastRootCheck mclock.AbsTime // last revalidation of root 35 enrs *subtreeSync 36 links *subtreeSync 37 linkCache linkCache 38 } 39 40 func newClientTree(c *Client, loc *linkEntry) *clientTree { 41 ct := &clientTree{c: c, loc: loc} 42 ct.linkCache.self = ct 43 return ct 44 } 45 46 func (ct *clientTree) matchPubkey(key *ecdsa.PublicKey) bool { 47 return keysEqual(ct.loc.pubkey, key) 48 } 49 50 func keysEqual(k1, k2 *ecdsa.PublicKey) bool { 51 return k1.Curve == k2.Curve && k1.X.Cmp(k2.X) == 0 && k1.Y.Cmp(k2.Y) == 0 52 } 53 54 // syncAll retrieves all entries of the tree. 55 func (ct *clientTree) syncAll(dest map[string]entry) error { 56 if err := ct.updateRoot(); err != nil { 57 return err 58 } 59 if err := ct.links.resolveAll(dest); err != nil { 60 return err 61 } 62 if err := ct.enrs.resolveAll(dest); err != nil { 63 return err 64 } 65 return nil 66 } 67 68 // syncRandom retrieves a single entry of the tree. The Node return value 69 // is non-nil if the entry was a node. 70 func (ct *clientTree) syncRandom(ctx context.Context) (*enode.Node, error) { 71 if ct.rootUpdateDue() { 72 if err := ct.updateRoot(); err != nil { 73 return nil, err 74 } 75 } 76 // Link tree sync has priority, run it to completion before syncing ENRs. 77 if !ct.links.done() { 78 err := ct.syncNextLink(ctx) 79 return nil, err 80 } 81 82 // Sync next random entry in ENR tree. Once every node has been visited, we simply 83 // start over. This is fine because entries are cached. 84 if ct.enrs.done() { 85 ct.enrs = newSubtreeSync(ct.c, ct.loc, ct.root.eroot, false) 86 } 87 return ct.syncNextRandomENR(ctx) 88 } 89 90 func (ct *clientTree) syncNextLink(ctx context.Context) error { 91 hash := ct.links.missing[0] 92 e, err := ct.links.resolveNext(ctx, hash) 93 if err != nil { 94 return err 95 } 96 ct.links.missing = ct.links.missing[1:] 97 98 if le, ok := e.(*linkEntry); ok { 99 lt, err := ct.c.ensureTree(le) 100 if err != nil { 101 return err 102 } 103 ct.linkCache.add(lt) 104 } 105 return nil 106 } 107 108 func (ct *clientTree) syncNextRandomENR(ctx context.Context) (*enode.Node, error) { 109 index := rand.Intn(len(ct.enrs.missing)) 110 hash := ct.enrs.missing[index] 111 e, err := ct.enrs.resolveNext(ctx, hash) 112 if err != nil { 113 return nil, err 114 } 115 ct.enrs.missing = removeHash(ct.enrs.missing, index) 116 if ee, ok := e.(*enrEntry); ok { 117 return ee.node, nil 118 } 119 return nil, nil 120 } 121 122 func (ct *clientTree) String() string { 123 return ct.loc.String() 124 } 125 126 // removeHash removes the element at index from h. 127 func removeHash(h []string, index int) []string { 128 if len(h) == 1 { 129 return nil 130 } 131 last := len(h) - 1 132 if index < last { 133 h[index] = h[last] 134 h[last] = "" 135 } 136 return h[:last] 137 } 138 139 // updateRoot ensures that the given tree has an up-to-date root. 140 func (ct *clientTree) updateRoot() error { 141 ct.lastRootCheck = ct.c.clock.Now() 142 ctx, cancel := context.WithTimeout(context.Background(), ct.c.cfg.Timeout) 143 defer cancel() 144 root, err := ct.c.resolveRoot(ctx, ct.loc) 145 if err != nil { 146 return err 147 } 148 ct.root = &root 149 150 // Invalidate subtrees if changed. 151 if ct.links == nil || root.lroot != ct.links.root { 152 ct.links = newSubtreeSync(ct.c, ct.loc, root.lroot, true) 153 ct.linkCache.reset() 154 } 155 if ct.enrs == nil || root.eroot != ct.enrs.root { 156 ct.enrs = newSubtreeSync(ct.c, ct.loc, root.eroot, false) 157 } 158 return nil 159 } 160 161 // rootUpdateDue returns true when a root update is needed. 162 func (ct *clientTree) rootUpdateDue() bool { 163 return ct.root == nil || time.Duration(ct.c.clock.Now()-ct.lastRootCheck) > ct.c.cfg.RecheckInterval 164 } 165 166 // subtreeSync is the sync of an ENR or link subtree. 167 type subtreeSync struct { 168 c *Client 169 loc *linkEntry 170 root string 171 missing []string // missing tree node hashes 172 link bool // true if this sync is for the link tree 173 } 174 175 func newSubtreeSync(c *Client, loc *linkEntry, root string, link bool) *subtreeSync { 176 return &subtreeSync{c, loc, root, []string{root}, link} 177 } 178 179 func (ts *subtreeSync) done() bool { 180 return len(ts.missing) == 0 181 } 182 183 func (ts *subtreeSync) resolveAll(dest map[string]entry) error { 184 for !ts.done() { 185 hash := ts.missing[0] 186 ctx, cancel := context.WithTimeout(context.Background(), ts.c.cfg.Timeout) 187 e, err := ts.resolveNext(ctx, hash) 188 cancel() 189 if err != nil { 190 return err 191 } 192 dest[hash] = e 193 ts.missing = ts.missing[1:] 194 } 195 return nil 196 } 197 198 func (ts *subtreeSync) resolveNext(ctx context.Context, hash string) (entry, error) { 199 e, err := ts.c.resolveEntry(ctx, ts.loc.domain, hash) 200 if err != nil { 201 return nil, err 202 } 203 switch e := e.(type) { 204 case *enrEntry: 205 if ts.link { 206 return nil, errENRInLinkTree 207 } 208 case *linkEntry: 209 if !ts.link { 210 return nil, errLinkInENRTree 211 } 212 case *branchEntry: 213 ts.missing = append(ts.missing, e.children...) 214 } 215 return e, nil 216 } 217 218 // linkCache tracks the links of a tree. 219 type linkCache struct { 220 self *clientTree 221 directM map[*clientTree]struct{} // direct links 222 allM map[*clientTree]struct{} // direct & transitive links 223 } 224 225 // reset clears the cache. 226 func (lc *linkCache) reset() { 227 lc.directM = nil 228 lc.allM = nil 229 } 230 231 // add adds a direct link to the cache. 232 func (lc *linkCache) add(ct *clientTree) { 233 if lc.directM == nil { 234 lc.directM = make(map[*clientTree]struct{}) 235 } 236 if _, ok := lc.directM[ct]; !ok { 237 lc.invalidate() 238 } 239 lc.directM[ct] = struct{}{} 240 } 241 242 // invalidate resets the cache of transitive links. 243 func (lc *linkCache) invalidate() { 244 lc.allM = nil 245 } 246 247 // valid returns true when the cache of transitive links is up-to-date. 248 func (lc *linkCache) valid() bool { 249 // Re-check validity of child caches to catch updates. 250 for ct := range lc.allM { 251 if ct != lc.self && !ct.linkCache.valid() { 252 lc.allM = nil 253 break 254 } 255 } 256 return lc.allM != nil 257 } 258 259 // all returns all trees reachable through the cache. 260 func (lc *linkCache) all() map[*clientTree]struct{} { 261 if lc.valid() { 262 return lc.allM 263 } 264 // Remake lc.allM it by taking the union of all() across children. 265 m := make(map[*clientTree]struct{}) 266 if lc.self != nil { 267 m[lc.self] = struct{}{} 268 } 269 for ct := range lc.directM { 270 m[ct] = struct{}{} 271 for lt := range ct.linkCache.all() { 272 m[lt] = struct{}{} 273 } 274 } 275 lc.allM = m 276 return m 277 }