github.com/theQRL/go-zond@v0.1.1/zond/downloader/beaconsync.go (about) 1 // Copyright 2022 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 downloader 18 19 import ( 20 "fmt" 21 "sync" 22 "time" 23 24 "github.com/theQRL/go-zond/common" 25 "github.com/theQRL/go-zond/core/rawdb" 26 "github.com/theQRL/go-zond/core/types" 27 "github.com/theQRL/go-zond/log" 28 ) 29 30 // beaconBackfiller is the chain and state backfilling that can be commenced once 31 // the skeleton syncer has successfully reverse downloaded all the headers up to 32 // the genesis block or an existing header in the database. Its operation is fully 33 // directed by the skeleton sync's head/tail events. 34 type beaconBackfiller struct { 35 downloader *Downloader // Downloader to direct via this callback implementation 36 syncMode SyncMode // Sync mode to use for backfilling the skeleton chains 37 success func() // Callback to run on successful sync cycle completion 38 filling bool // Flag whether the downloader is backfilling or not 39 filled *types.Header // Last header filled by the last terminated sync loop 40 started chan struct{} // Notification channel whether the downloader inited 41 lock sync.Mutex // Mutex protecting the sync lock 42 } 43 44 // newBeaconBackfiller is a helper method to create the backfiller. 45 func newBeaconBackfiller(dl *Downloader, success func()) backfiller { 46 return &beaconBackfiller{ 47 downloader: dl, 48 success: success, 49 } 50 } 51 52 // suspend cancels any background downloader threads and returns the last header 53 // that has been successfully backfilled. 54 func (b *beaconBackfiller) suspend() *types.Header { 55 // If no filling is running, don't waste cycles 56 b.lock.Lock() 57 filling := b.filling 58 filled := b.filled 59 started := b.started 60 b.lock.Unlock() 61 62 if !filling { 63 return filled // Return the filled header on the previous sync completion 64 } 65 // A previous filling should be running, though it may happen that it hasn't 66 // yet started (being done on a new goroutine). Many concurrent beacon head 67 // announcements can lead to sync start/stop thrashing. In that case we need 68 // to wait for initialization before we can safely cancel it. It is safe to 69 // read this channel multiple times, it gets closed on startup. 70 <-started 71 72 // Now that we're sure the downloader successfully started up, we can cancel 73 // it safely without running the risk of data races. 74 b.downloader.Cancel() 75 76 // Sync cycle was just terminated, retrieve and return the last filled header. 77 // Can't use `filled` as that contains a stale value from before cancellation. 78 return b.downloader.blockchain.CurrentSnapBlock() 79 } 80 81 // resume starts the downloader threads for backfilling state and chain data. 82 func (b *beaconBackfiller) resume() { 83 b.lock.Lock() 84 if b.filling { 85 // If a previous filling cycle is still running, just ignore this start 86 // request. // TODO(karalabe): We should make this channel driven 87 b.lock.Unlock() 88 return 89 } 90 b.filling = true 91 b.filled = nil 92 b.started = make(chan struct{}) 93 mode := b.syncMode 94 b.lock.Unlock() 95 96 // Start the backfilling on its own thread since the downloader does not have 97 // its own lifecycle runloop. 98 go func() { 99 // Set the backfiller to non-filling when download completes 100 defer func() { 101 b.lock.Lock() 102 b.filling = false 103 b.filled = b.downloader.blockchain.CurrentSnapBlock() 104 b.lock.Unlock() 105 }() 106 // If the downloader fails, report an error as in beacon chain mode there 107 // should be no errors as long as the chain we're syncing to is valid. 108 if err := b.downloader.synchronise("", common.Hash{}, nil, nil, mode, true, b.started); err != nil { 109 log.Error("Beacon backfilling failed", "err", err) 110 return 111 } 112 // Synchronization succeeded. Since this happens async, notify the outer 113 // context to disable snap syncing and enable transaction propagation. 114 if b.success != nil { 115 b.success() 116 } 117 }() 118 } 119 120 // setMode updates the sync mode from the current one to the requested one. If 121 // there's an active sync in progress, it will be cancelled and restarted. 122 func (b *beaconBackfiller) setMode(mode SyncMode) { 123 // Update the old sync mode and track if it was changed 124 b.lock.Lock() 125 updated := b.syncMode != mode 126 filling := b.filling 127 b.syncMode = mode 128 b.lock.Unlock() 129 130 // If the sync mode was changed mid-sync, restart. This should never ever 131 // really happen, we just handle it to detect programming errors. 132 if !updated || !filling { 133 return 134 } 135 log.Error("Downloader sync mode changed mid-run", "old", mode.String(), "new", mode.String()) 136 b.suspend() 137 b.resume() 138 } 139 140 // SetBadBlockCallback sets the callback to run when a bad block is hit by the 141 // block processor. This method is not thread safe and should be set only once 142 // on startup before system events are fired. 143 func (d *Downloader) SetBadBlockCallback(onBadBlock badBlockFn) { 144 d.badBlock = onBadBlock 145 } 146 147 // BeaconSync is the post-merge version of the chain synchronization, where the 148 // chain is not downloaded from genesis onward, rather from trusted head announces 149 // backwards. 150 // 151 // Internally backfilling and state sync is done the same way, but the header 152 // retrieval and scheduling is replaced. 153 func (d *Downloader) BeaconSync(mode SyncMode, head *types.Header, final *types.Header) error { 154 return d.beaconSync(mode, head, final, true) 155 } 156 157 // BeaconExtend is an optimistic version of BeaconSync, where an attempt is made 158 // to extend the current beacon chain with a new header, but in case of a mismatch, 159 // the old sync will not be terminated and reorged, rather the new head is dropped. 160 // 161 // This is useful if a beacon client is feeding us large chunks of payloads to run, 162 // but is not setting the head after each. 163 func (d *Downloader) BeaconExtend(mode SyncMode, head *types.Header) error { 164 return d.beaconSync(mode, head, nil, false) 165 } 166 167 // beaconSync is the post-merge version of the chain synchronization, where the 168 // chain is not downloaded from genesis onward, rather from trusted head announces 169 // backwards. 170 // 171 // Internally backfilling and state sync is done the same way, but the header 172 // retrieval and scheduling is replaced. 173 func (d *Downloader) beaconSync(mode SyncMode, head *types.Header, final *types.Header, force bool) error { 174 // When the downloader starts a sync cycle, it needs to be aware of the sync 175 // mode to use (full, snap). To keep the skeleton chain oblivious, inject the 176 // mode into the backfiller directly. 177 // 178 // Super crazy dangerous type cast. Should be fine (TM), we're only using a 179 // different backfiller implementation for skeleton tests. 180 d.skeleton.filler.(*beaconBackfiller).setMode(mode) 181 182 // Signal the skeleton sync to switch to a new head, however it wants 183 if err := d.skeleton.Sync(head, final, force); err != nil { 184 return err 185 } 186 return nil 187 } 188 189 // findBeaconAncestor tries to locate the common ancestor link of the local chain 190 // and the beacon chain just requested. In the general case when our node was in 191 // sync and on the correct chain, checking the top N links should already get us 192 // a match. In the rare scenario when we ended up on a long reorganisation (i.e. 193 // none of the head links match), we do a binary search to find the ancestor. 194 func (d *Downloader) findBeaconAncestor() (uint64, error) { 195 // Figure out the current local head position 196 var chainHead *types.Header 197 198 switch d.getMode() { 199 case FullSync: 200 chainHead = d.blockchain.CurrentBlock() 201 case SnapSync: 202 chainHead = d.blockchain.CurrentSnapBlock() 203 default: 204 chainHead = d.lightchain.CurrentHeader() 205 } 206 number := chainHead.Number.Uint64() 207 208 // Retrieve the skeleton bounds and ensure they are linked to the local chain 209 beaconHead, beaconTail, _, err := d.skeleton.Bounds() 210 if err != nil { 211 // This is a programming error. The chain backfiller was called with an 212 // invalid beacon sync state. Ideally we would panic here, but erroring 213 // gives us at least a remote chance to recover. It's still a big fault! 214 log.Error("Failed to retrieve beacon bounds", "err", err) 215 return 0, err 216 } 217 var linked bool 218 switch d.getMode() { 219 case FullSync: 220 linked = d.blockchain.HasBlock(beaconTail.ParentHash, beaconTail.Number.Uint64()-1) 221 case SnapSync: 222 linked = d.blockchain.HasFastBlock(beaconTail.ParentHash, beaconTail.Number.Uint64()-1) 223 default: 224 linked = d.blockchain.HasHeader(beaconTail.ParentHash, beaconTail.Number.Uint64()-1) 225 } 226 if !linked { 227 // This is a programming error. The chain backfiller was called with a 228 // tail that's not linked to the local chain. Whilst this should never 229 // happen, there might be some weirdnesses if beacon sync backfilling 230 // races with the user (or beacon client) calling setHead. Whilst panic 231 // would be the ideal thing to do, it is safer long term to attempt a 232 // recovery and fix any noticed issue after the fact. 233 log.Error("Beacon sync linkup unavailable", "number", beaconTail.Number.Uint64()-1, "hash", beaconTail.ParentHash) 234 return 0, fmt.Errorf("beacon linkup unavailable locally: %d [%x]", beaconTail.Number.Uint64()-1, beaconTail.ParentHash) 235 } 236 // Binary search to find the ancestor 237 start, end := beaconTail.Number.Uint64()-1, number 238 if number := beaconHead.Number.Uint64(); end > number { 239 // This shouldn't really happen in a healthy network, but if the consensus 240 // clients feeds us a shorter chain as the canonical, we should not attempt 241 // to access non-existent skeleton items. 242 log.Warn("Beacon head lower than local chain", "beacon", number, "local", end) 243 end = number 244 } 245 for start+1 < end { 246 // Split our chain interval in two, and request the hash to cross check 247 check := (start + end) / 2 248 249 h := d.skeleton.Header(check) 250 n := h.Number.Uint64() 251 252 var known bool 253 switch d.getMode() { 254 case FullSync: 255 known = d.blockchain.HasBlock(h.Hash(), n) 256 case SnapSync: 257 known = d.blockchain.HasFastBlock(h.Hash(), n) 258 default: 259 known = d.lightchain.HasHeader(h.Hash(), n) 260 } 261 if !known { 262 end = check 263 continue 264 } 265 start = check 266 } 267 return start, nil 268 } 269 270 // fetchBeaconHeaders feeds skeleton headers to the downloader queue for scheduling 271 // until sync errors or is finished. 272 func (d *Downloader) fetchBeaconHeaders(from uint64) error { 273 var head *types.Header 274 _, tail, _, err := d.skeleton.Bounds() 275 if err != nil { 276 return err 277 } 278 // A part of headers are not in the skeleton space, try to resolve 279 // them from the local chain. Note the range should be very short 280 // and it should only happen when there are less than 64 post-merge 281 // blocks in the network. 282 var localHeaders []*types.Header 283 if from < tail.Number.Uint64() { 284 count := tail.Number.Uint64() - from 285 if count > uint64(fsMinFullBlocks) { 286 return fmt.Errorf("invalid origin (%d) of beacon sync (%d)", from, tail.Number) 287 } 288 localHeaders = d.readHeaderRange(tail, int(count)) 289 log.Warn("Retrieved beacon headers from local", "from", from, "count", count) 290 } 291 for { 292 // Some beacon headers might have appeared since the last cycle, make 293 // sure we're always syncing to all available ones 294 head, _, _, err = d.skeleton.Bounds() 295 if err != nil { 296 return err 297 } 298 // If the pivot became stale (older than 2*64-8 (bit of wiggle room)), 299 // move it ahead to HEAD-64 300 d.pivotLock.Lock() 301 if d.pivotHeader != nil { 302 if head.Number.Uint64() > d.pivotHeader.Number.Uint64()+2*uint64(fsMinFullBlocks)-8 { 303 // Retrieve the next pivot header, either from skeleton chain 304 // or the filled chain 305 number := head.Number.Uint64() - uint64(fsMinFullBlocks) 306 307 log.Warn("Pivot seemingly stale, moving", "old", d.pivotHeader.Number, "new", number) 308 if d.pivotHeader = d.skeleton.Header(number); d.pivotHeader == nil { 309 if number < tail.Number.Uint64() { 310 dist := tail.Number.Uint64() - number 311 if len(localHeaders) >= int(dist) { 312 d.pivotHeader = localHeaders[dist-1] 313 log.Warn("Retrieved pivot header from local", "number", d.pivotHeader.Number, "hash", d.pivotHeader.Hash(), "latest", head.Number, "oldest", tail.Number) 314 } 315 } 316 } 317 // Print an error log and return directly in case the pivot header 318 // is still not found. It means the skeleton chain is not linked 319 // correctly with local chain. 320 if d.pivotHeader == nil { 321 log.Error("Pivot header is not found", "number", number) 322 d.pivotLock.Unlock() 323 return errNoPivotHeader 324 } 325 // Write out the pivot into the database so a rollback beyond 326 // it will reenable snap sync and update the state root that 327 // the state syncer will be downloading 328 rawdb.WriteLastPivotNumber(d.stateDB, d.pivotHeader.Number.Uint64()) 329 } 330 } 331 d.pivotLock.Unlock() 332 333 // Retrieve a batch of headers and feed it to the header processor 334 var ( 335 headers = make([]*types.Header, 0, maxHeadersProcess) 336 hashes = make([]common.Hash, 0, maxHeadersProcess) 337 ) 338 for i := 0; i < maxHeadersProcess && from <= head.Number.Uint64(); i++ { 339 header := d.skeleton.Header(from) 340 341 // The header is not found in skeleton space, try to find it in local chain. 342 if header == nil && from < tail.Number.Uint64() { 343 dist := tail.Number.Uint64() - from 344 if len(localHeaders) >= int(dist) { 345 header = localHeaders[dist-1] 346 } 347 } 348 // The header is still missing, the beacon sync is corrupted and bail out 349 // the error here. 350 if header == nil { 351 return fmt.Errorf("missing beacon header %d", from) 352 } 353 headers = append(headers, header) 354 hashes = append(hashes, headers[i].Hash()) 355 from++ 356 } 357 if len(headers) > 0 { 358 log.Trace("Scheduling new beacon headers", "count", len(headers), "from", from-uint64(len(headers))) 359 select { 360 case d.headerProcCh <- &headerTask{ 361 headers: headers, 362 hashes: hashes, 363 }: 364 case <-d.cancelCh: 365 return errCanceled 366 } 367 } 368 // If we still have headers to import, loop and keep pushing them 369 if from <= head.Number.Uint64() { 370 continue 371 } 372 // If the pivot block is committed, signal header sync termination 373 if d.committed.Load() { 374 select { 375 case d.headerProcCh <- nil: 376 return nil 377 case <-d.cancelCh: 378 return errCanceled 379 } 380 } 381 // State sync still going, wait a bit for new headers and retry 382 log.Trace("Pivot not yet committed, waiting...") 383 select { 384 case <-time.After(fsHeaderContCheck): 385 case <-d.cancelCh: 386 return errCanceled 387 } 388 } 389 }