github.com/core-coin/go-core/v2@v2.1.9/les/fetcher.go (about) 1 // Copyright 2016 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package les 18 19 import ( 20 "math/big" 21 "math/rand" 22 "sync" 23 "time" 24 25 "github.com/core-coin/go-core/v2/xcbdb" 26 27 "github.com/core-coin/go-core/v2/common" 28 "github.com/core-coin/go-core/v2/consensus" 29 "github.com/core-coin/go-core/v2/core" 30 "github.com/core-coin/go-core/v2/core/rawdb" 31 "github.com/core-coin/go-core/v2/core/types" 32 "github.com/core-coin/go-core/v2/light" 33 "github.com/core-coin/go-core/v2/log" 34 "github.com/core-coin/go-core/v2/p2p/enode" 35 "github.com/core-coin/go-core/v2/xcb/fetcher" 36 ) 37 38 const ( 39 blockDelayTimeout = 10 * time.Second // Timeout for retrieving the headers from the peer 40 gatherSlack = 100 * time.Millisecond // Interval used to collate almost-expired requests 41 cachedAnnosThreshold = 64 // The maximum queued announcements 42 ) 43 44 // announce represents an new block announcement from the les server. 45 type announce struct { 46 data *announceData 47 trust bool 48 peerid enode.ID 49 } 50 51 // request represents a record when the header request is sent. 52 type request struct { 53 reqid uint64 54 peerid enode.ID 55 sendAt time.Time 56 hash common.Hash 57 } 58 59 // response represents a response packet from network as well as a channel 60 // to return all un-requested data. 61 type response struct { 62 reqid uint64 63 headers []*types.Header 64 peerid enode.ID 65 remain chan []*types.Header 66 } 67 68 // fetcherPeer holds the fetcher-specific information for each active peer 69 type fetcherPeer struct { 70 latest *announceData // The latest announcement sent from the peer 71 72 // These following two fields can track the latest announces 73 // from the peer with limited size for caching. We hold the 74 // assumption that all enqueued announces are td-monotonic. 75 announces map[common.Hash]*announce // Announcement map 76 announcesList []common.Hash // FIFO announces list 77 } 78 79 // addAnno enqueues an new trusted announcement. If the queued announces overflow, 80 // evict from the oldest. 81 func (fp *fetcherPeer) addAnno(anno *announce) { 82 // Short circuit if the anno already exists. In normal case it should 83 // never happen since only monotonic anno is accepted. But the adversary 84 // may feed us fake announces with higher td but same hash. In this case, 85 // ignore the anno anyway. 86 hash := anno.data.Hash 87 if _, exist := fp.announces[hash]; exist { 88 return 89 } 90 fp.announces[hash] = anno 91 fp.announcesList = append(fp.announcesList, hash) 92 93 // Evict oldest if the announces are oversized. 94 if len(fp.announcesList)-cachedAnnosThreshold > 0 { 95 for i := 0; i < len(fp.announcesList)-cachedAnnosThreshold; i++ { 96 delete(fp.announces, fp.announcesList[i]) 97 } 98 copy(fp.announcesList, fp.announcesList[len(fp.announcesList)-cachedAnnosThreshold:]) 99 fp.announcesList = fp.announcesList[:cachedAnnosThreshold] 100 } 101 } 102 103 // forwardAnno removes all announces from the map with a number lower than 104 // the provided threshold. 105 func (fp *fetcherPeer) forwardAnno(td *big.Int) []*announce { 106 var ( 107 cutset int 108 evicted []*announce 109 ) 110 for ; cutset < len(fp.announcesList); cutset++ { 111 anno := fp.announces[fp.announcesList[cutset]] 112 if anno == nil { 113 continue // In theory it should never ever happen 114 } 115 if anno.data.Td.Cmp(td) > 0 { 116 break 117 } 118 evicted = append(evicted, anno) 119 delete(fp.announces, anno.data.Hash) 120 } 121 if cutset > 0 { 122 copy(fp.announcesList, fp.announcesList[cutset:]) 123 fp.announcesList = fp.announcesList[:len(fp.announcesList)-cutset] 124 } 125 return evicted 126 } 127 128 // lightFetcher implements retrieval of newly announced headers. It reuses 129 // the xcb.BlockFetcher as the underlying fetcher but adding more additional 130 // rules: e.g. evict "timeout" peers. 131 type lightFetcher struct { 132 // Various handlers 133 ulc *ulc 134 chaindb xcbdb.Database 135 reqDist *requestDistributor 136 peerset *serverPeerSet // The global peerset of light client which shared by all components 137 chain *light.LightChain // The local light chain which maintains the canonical header chain. 138 fetcher *fetcher.BlockFetcher // The underlying fetcher which takes care block header retrieval. 139 140 // Peerset maintained by fetcher 141 plock sync.RWMutex 142 peers map[enode.ID]*fetcherPeer 143 144 // Various channels 145 announceCh chan *announce 146 requestCh chan *request 147 deliverCh chan *response 148 syncDone chan *types.Header 149 150 closeCh chan struct{} 151 wg sync.WaitGroup 152 153 // Callback 154 synchronise func(peer *serverPeer) 155 156 // Test fields or hooks 157 noAnnounce bool 158 newHeadHook func(*types.Header) 159 newAnnounce func(*serverPeer, *announceData) 160 } 161 162 // newLightFetcher creates a light fetcher instance. 163 func newLightFetcher(chain *light.LightChain, engine consensus.Engine, peers *serverPeerSet, ulc *ulc, chaindb xcbdb.Database, reqDist *requestDistributor, syncFn func(p *serverPeer)) *lightFetcher { 164 // Construct the fetcher by offering all necessary APIs 165 validator := func(header *types.Header) error { 166 // Disable seal verification explicitly if we are running in ulc mode. 167 return engine.VerifyHeader(chain, header, ulc == nil) 168 } 169 heighter := func() uint64 { return chain.CurrentHeader().Number.Uint64() } 170 dropper := func(id string) { peers.unregister(id) } 171 inserter := func(headers []*types.Header) (int, error) { 172 // Disable PoW checking explicitly if we are running in ulc mode. 173 checkFreq := 1 174 if ulc != nil { 175 checkFreq = 0 176 } 177 return chain.InsertHeaderChain(headers, checkFreq) 178 } 179 f := &lightFetcher{ 180 ulc: ulc, 181 peerset: peers, 182 chaindb: chaindb, 183 chain: chain, 184 reqDist: reqDist, 185 fetcher: fetcher.NewBlockFetcher(true, chain.GetHeaderByHash, nil, validator, nil, heighter, inserter, nil, dropper), 186 peers: make(map[enode.ID]*fetcherPeer), 187 synchronise: syncFn, 188 announceCh: make(chan *announce), 189 requestCh: make(chan *request), 190 deliverCh: make(chan *response), 191 syncDone: make(chan *types.Header), 192 closeCh: make(chan struct{}), 193 } 194 peers.subscribe(f) 195 return f 196 } 197 198 func (f *lightFetcher) start() { 199 f.wg.Add(1) 200 f.fetcher.Start() 201 go f.mainloop() 202 } 203 204 func (f *lightFetcher) stop() { 205 close(f.closeCh) 206 f.fetcher.Stop() 207 f.wg.Wait() 208 } 209 210 // registerPeer adds an new peer to the fetcher's peer set 211 func (f *lightFetcher) registerPeer(p *serverPeer) { 212 f.plock.Lock() 213 defer f.plock.Unlock() 214 215 f.peers[p.ID()] = &fetcherPeer{announces: make(map[common.Hash]*announce)} 216 } 217 218 // unregisterPeer removes the specified peer from the fetcher's peer set 219 func (f *lightFetcher) unregisterPeer(p *serverPeer) { 220 f.plock.Lock() 221 defer f.plock.Unlock() 222 223 delete(f.peers, p.ID()) 224 } 225 226 // peer returns the peer from the fetcher peerset. 227 func (f *lightFetcher) peer(id enode.ID) *fetcherPeer { 228 f.plock.RLock() 229 defer f.plock.RUnlock() 230 231 return f.peers[id] 232 } 233 234 // forEachPeer iterates the fetcher peerset, abort the iteration if the 235 // callback returns false. 236 func (f *lightFetcher) forEachPeer(check func(id enode.ID, p *fetcherPeer) bool) { 237 f.plock.RLock() 238 defer f.plock.RUnlock() 239 240 for id, peer := range f.peers { 241 if !check(id, peer) { 242 return 243 } 244 } 245 } 246 247 // mainloop is the main event loop of the light fetcher, which is responsible for 248 // 249 // - announcement maintenance(ulc) 250 // If we are running in ultra light client mode, then all announcements from 251 // the trusted servers are maintained. If the same announcements from trusted 252 // servers reach the threshold, then the relevant header is requested for retrieval. 253 // 254 // - block header retrieval 255 // Whenever we receive announce with higher td compared with local chain, the 256 // request will be made for header retrieval. 257 // 258 // - re-sync trigger 259 // If the local chain lags too much, then the fetcher will enter "synnchronise" 260 // mode to retrieve missing headers in batch. 261 func (f *lightFetcher) mainloop() { 262 defer f.wg.Done() 263 264 var ( 265 syncInterval = uint64(1) // Interval used to trigger a light resync. 266 syncing bool // Indicator whether the client is syncing 267 268 ulc = f.ulc != nil 269 headCh = make(chan core.ChainHeadEvent, 100) 270 fetching = make(map[uint64]*request) 271 requestTimer = time.NewTimer(0) 272 273 // Local status 274 localHead = f.chain.CurrentHeader() 275 localTd = f.chain.GetTd(localHead.Hash(), localHead.Number.Uint64()) 276 ) 277 sub := f.chain.SubscribeChainHeadEvent(headCh) 278 defer sub.Unsubscribe() 279 280 // reset updates the local status with given header. 281 reset := func(header *types.Header) { 282 localHead = header 283 localTd = f.chain.GetTd(header.Hash(), header.Number.Uint64()) 284 } 285 // trustedHeader returns an indicator whether the header is regarded as 286 // trusted. If we are running in the ulc mode, only when we receive enough 287 // same announcement from trusted server, the header will be trusted. 288 trustedHeader := func(hash common.Hash, number uint64) (bool, []enode.ID) { 289 var ( 290 agreed []enode.ID 291 trusted bool 292 ) 293 f.forEachPeer(func(id enode.ID, p *fetcherPeer) bool { 294 if anno := p.announces[hash]; anno != nil && anno.trust && anno.data.Number == number { 295 agreed = append(agreed, id) 296 if 100*len(agreed)/len(f.ulc.keys) >= f.ulc.fraction { 297 trusted = true 298 return false // abort iteration 299 } 300 } 301 return true 302 }) 303 return trusted, agreed 304 } 305 for { 306 select { 307 case anno := <-f.announceCh: 308 peerid, data := anno.peerid, anno.data 309 log.Debug("Received new announce", "peer", peerid, "number", data.Number, "hash", data.Hash, "reorg", data.ReorgDepth) 310 311 peer := f.peer(peerid) 312 if peer == nil { 313 log.Debug("Receive announce from unknown peer", "peer", peerid) 314 continue 315 } 316 // Announced tds should be strictly monotonic, drop the peer if 317 // the announce is out-of-order. 318 if peer.latest != nil && data.Td.Cmp(peer.latest.Td) <= 0 { 319 f.peerset.unregister(peerid.String()) 320 log.Debug("Non-monotonic td", "peer", peerid, "current", data.Td, "previous", peer.latest.Td) 321 continue 322 } 323 peer.latest = data 324 325 // Filter out any stale announce, the local chain is ahead of announce 326 if localTd != nil && data.Td.Cmp(localTd) <= 0 { 327 continue 328 } 329 peer.addAnno(anno) 330 331 // If we are not syncing, try to trigger a single retrieval or re-sync 332 if !ulc && !syncing { 333 // Two scenarios lead to re-sync: 334 // - reorg happens 335 // - local chain lags 336 // We can't retrieve the parent of the announce by single retrieval 337 // in both cases, so resync is necessary. 338 if data.Number > localHead.Number.Uint64()+syncInterval || data.ReorgDepth > 0 { 339 syncing = true 340 go f.startSync(peerid) 341 log.Debug("Trigger light sync", "peer", peerid, "local", localHead.Number, "localhash", localHead.Hash(), "remote", data.Number, "remotehash", data.Hash) 342 continue 343 } 344 f.fetcher.Notify(peerid.String(), data.Hash, data.Number, time.Now(), f.requestHeaderByHash(peerid), nil) 345 log.Debug("Trigger header retrieval", "peer", peerid, "number", data.Number, "hash", data.Hash) 346 } 347 // Keep collecting announces from trusted server even we are syncing. 348 if ulc && anno.trust { 349 // Notify underlying fetcher to retrieve header or trigger a resync if 350 // we have receive enough announcements from trusted server. 351 trusted, agreed := trustedHeader(data.Hash, data.Number) 352 if trusted && !syncing { 353 if data.Number > localHead.Number.Uint64()+syncInterval || data.ReorgDepth > 0 { 354 syncing = true 355 go f.startSync(peerid) 356 log.Debug("Trigger trusted light sync", "local", localHead.Number, "localhash", localHead.Hash(), "remote", data.Number, "remotehash", data.Hash) 357 continue 358 } 359 p := agreed[rand.Intn(len(agreed))] 360 f.fetcher.Notify(p.String(), data.Hash, data.Number, time.Now(), f.requestHeaderByHash(p), nil) 361 log.Debug("Trigger trusted header retrieval", "number", data.Number, "hash", data.Hash) 362 } 363 } 364 365 case req := <-f.requestCh: 366 fetching[req.reqid] = req // Tracking all in-flight requests for response latency statistic. 367 if len(fetching) == 1 { 368 f.rescheduleTimer(fetching, requestTimer) 369 } 370 371 case <-requestTimer.C: 372 for reqid, request := range fetching { 373 if time.Since(request.sendAt) > blockDelayTimeout-gatherSlack { 374 delete(fetching, reqid) 375 f.peerset.unregister(request.peerid.String()) 376 log.Debug("Request timeout", "peer", request.peerid, "reqid", reqid) 377 } 378 } 379 f.rescheduleTimer(fetching, requestTimer) 380 381 case resp := <-f.deliverCh: 382 if req := fetching[resp.reqid]; req != nil { 383 delete(fetching, resp.reqid) 384 f.rescheduleTimer(fetching, requestTimer) 385 386 // The underlying fetcher does not check the consistency of request and response. 387 // The adversary can send the fake announces with invalid hash and number but always 388 // delivery some mismatched header. So it can't be punished by the underlying fetcher. 389 // We have to add two more rules here to detect. 390 if len(resp.headers) != 1 { 391 f.peerset.unregister(req.peerid.String()) 392 log.Debug("Deliver more than requested", "peer", req.peerid, "reqid", req.reqid) 393 continue 394 } 395 if resp.headers[0].Hash() != req.hash { 396 f.peerset.unregister(req.peerid.String()) 397 log.Debug("Deliver invalid header", "peer", req.peerid, "reqid", req.reqid) 398 continue 399 } 400 resp.remain <- f.fetcher.FilterHeaders(resp.peerid.String(), resp.headers, time.Now()) 401 } else { 402 // Discard the entire packet no matter it's a timeout response or unexpected one. 403 resp.remain <- resp.headers 404 } 405 406 case ev := <-headCh: 407 // Short circuit if we are still syncing. 408 if syncing { 409 continue 410 } 411 reset(ev.Block.Header()) 412 413 // Clean stale announcements from les-servers. 414 var droplist []enode.ID 415 f.forEachPeer(func(id enode.ID, p *fetcherPeer) bool { 416 removed := p.forwardAnno(localTd) 417 for _, anno := range removed { 418 if header := f.chain.GetHeaderByHash(anno.data.Hash); header != nil { 419 if header.Number.Uint64() != anno.data.Number { 420 droplist = append(droplist, id) 421 break 422 } 423 // In theory td should exists. 424 td := f.chain.GetTd(anno.data.Hash, anno.data.Number) 425 if td != nil && td.Cmp(anno.data.Td) != 0 { 426 droplist = append(droplist, id) 427 break 428 } 429 } 430 } 431 return true 432 }) 433 for _, id := range droplist { 434 f.peerset.unregister(id.String()) 435 log.Debug("Kicked out peer for invalid announcement") 436 } 437 if f.newHeadHook != nil { 438 f.newHeadHook(localHead) 439 } 440 441 case origin := <-f.syncDone: 442 syncing = false // Reset the status 443 444 // Rewind all untrusted headers for ulc mode. 445 if ulc { 446 head := f.chain.CurrentHeader() 447 ancestor := rawdb.FindCommonAncestor(f.chaindb, origin, head) 448 var untrusted []common.Hash 449 for head.Number.Cmp(ancestor.Number) > 0 { 450 hash, number := head.Hash(), head.Number.Uint64() 451 if trusted, _ := trustedHeader(hash, number); trusted { 452 break 453 } 454 untrusted = append(untrusted, hash) 455 head = f.chain.GetHeader(head.ParentHash, number-1) 456 } 457 if len(untrusted) > 0 { 458 for i, j := 0, len(untrusted)-1; i < j; i, j = i+1, j-1 { 459 untrusted[i], untrusted[j] = untrusted[j], untrusted[i] 460 } 461 f.chain.Rollback(untrusted) 462 } 463 } 464 // Reset local status. 465 reset(f.chain.CurrentHeader()) 466 if f.newHeadHook != nil { 467 f.newHeadHook(localHead) 468 } 469 log.Debug("light sync finished", "number", localHead.Number, "hash", localHead.Hash()) 470 471 case <-f.closeCh: 472 return 473 } 474 } 475 } 476 477 // announce processes a new announcement message received from a peer. 478 func (f *lightFetcher) announce(p *serverPeer, head *announceData) { 479 if f.newAnnounce != nil { 480 f.newAnnounce(p, head) 481 } 482 if f.noAnnounce { 483 return 484 } 485 select { 486 case f.announceCh <- &announce{peerid: p.ID(), trust: p.trusted, data: head}: 487 case <-f.closeCh: 488 return 489 } 490 } 491 492 // trackRequest sends a reqID to main loop for in-flight request tracking. 493 func (f *lightFetcher) trackRequest(peerid enode.ID, reqid uint64, hash common.Hash) { 494 select { 495 case f.requestCh <- &request{reqid: reqid, peerid: peerid, sendAt: time.Now(), hash: hash}: 496 case <-f.closeCh: 497 } 498 } 499 500 // requestHeaderByHash constructs a header retrieval request and sends it to 501 // local request distributor. 502 // 503 // Note, we rely on the underlying xcb/fetcher to retrieve and validate the 504 // response, so that we have to obey the rule of xcb/fetcher which only accepts 505 // the response from given peer. 506 func (f *lightFetcher) requestHeaderByHash(peerid enode.ID) func(common.Hash) error { 507 return func(hash common.Hash) error { 508 req := &distReq{ 509 getCost: func(dp distPeer) uint64 { return dp.(*serverPeer).getRequestCost(GetBlockHeadersMsg, 1) }, 510 canSend: func(dp distPeer) bool { return dp.(*serverPeer).ID() == peerid }, 511 request: func(dp distPeer) func() { 512 peer, id := dp.(*serverPeer), genReqID() 513 cost := peer.getRequestCost(GetBlockHeadersMsg, 1) 514 peer.fcServer.QueuedRequest(id, cost) 515 516 return func() { 517 f.trackRequest(peer.ID(), id, hash) 518 peer.requestHeadersByHash(id, hash, 1, 0, false) 519 } 520 }, 521 } 522 f.reqDist.queue(req) 523 return nil 524 } 525 } 526 527 // requestResync invokes synchronisation callback to start syncing. 528 func (f *lightFetcher) startSync(id enode.ID) { 529 defer func(header *types.Header) { 530 f.syncDone <- header 531 }(f.chain.CurrentHeader()) 532 533 peer := f.peerset.peer(id.String()) 534 if peer == nil || peer.onlyAnnounce { 535 return 536 } 537 f.synchronise(peer) 538 } 539 540 // deliverHeaders delivers header download request responses for processing 541 func (f *lightFetcher) deliverHeaders(peer *serverPeer, reqid uint64, headers []*types.Header) []*types.Header { 542 remain := make(chan []*types.Header, 1) 543 select { 544 case f.deliverCh <- &response{reqid: reqid, headers: headers, peerid: peer.ID(), remain: remain}: 545 case <-f.closeCh: 546 return nil 547 } 548 return <-remain 549 } 550 551 // rescheduleTimer resets the specified timeout timer to the next request timeout. 552 func (f *lightFetcher) rescheduleTimer(requests map[uint64]*request, timer *time.Timer) { 553 // Short circuit if no inflight requests 554 if len(requests) == 0 { 555 timer.Stop() 556 return 557 } 558 // Otherwise find the earliest expiring request 559 earliest := time.Now() 560 for _, req := range requests { 561 if earliest.After(req.sendAt) { 562 earliest = req.sendAt 563 } 564 } 565 timer.Reset(blockDelayTimeout - time.Since(earliest)) 566 }