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