|
| 1 | +# Nimbus |
| 2 | +# Copyright (c) 2023-2025 Status Research & Development GmbH |
| 3 | +# Licensed and distributed under either of |
| 4 | +# * MIT license (license terms in the root directory or at |
| 5 | +# https://opensource.org/licenses/MIT). |
| 6 | +# * Apache v2 license (license terms in the root directory or at |
| 7 | +# https://www.apache.org/licenses/LICENSE-2.0). |
| 8 | +# at your option. This file may not be copied, modified, or distributed |
| 9 | +# except according to those terms. |
| 10 | + |
| 11 | +{.push raises:[].} |
| 12 | + |
| 13 | +import |
| 14 | + pkg/[chronicles, chronos], |
| 15 | + pkg/eth/common, |
| 16 | + pkg/stew/interval_set, |
| 17 | + ../../../../networking/p2p, |
| 18 | + ../../worker_desc, |
| 19 | + ./[headers_fetch, headers_helpers] |
| 20 | + |
| 21 | +logScope: |
| 22 | + topics = "beacon sync" |
| 23 | + |
| 24 | +# ------------------------------------------------------------------------------ |
| 25 | +# Public functions |
| 26 | +# ------------------------------------------------------------------------------ |
| 27 | + |
| 28 | +proc headersTargetRequest*(ctx: BeaconCtxRef; h: Hash32; info: static[string]) = |
| 29 | + ## Request *manual* syncer target. It has to be activated by the |
| 30 | + ## `headersTargetActivate()` function below. |
| 31 | + ctx.pool.initTarget = Opt.some(h) |
| 32 | + trace info & ": request syncer target", targetHash=h.short |
| 33 | + |
| 34 | +proc headersTargetReset*(ctx: BeaconCtxRef) = |
| 35 | + ## Reset *manual* syncer target. |
| 36 | + ctx.pool.initTarget = Opt.none(Hash32) |
| 37 | + |
| 38 | + |
| 39 | +template headersTargetActivate*( |
| 40 | + buddy: BeaconBuddyRef; |
| 41 | + info: static[string]; |
| 42 | + ) = |
| 43 | + ## Async/template |
| 44 | + ## |
| 45 | + ## Load target header and trigger syncer activation if there is a request |
| 46 | + ## for doing so (i.e. the `initTarget` variable is set.) |
| 47 | + ## |
| 48 | + block body: |
| 49 | + # Check whether a target is available at all. |
| 50 | + let ctx = buddy.ctx |
| 51 | + if ctx.pool.initTarget.isNone(): |
| 52 | + break body # return |
| 53 | + |
| 54 | + let |
| 55 | + peer {.inject.} = buddy.peer |
| 56 | + h = ctx.pool.initTarget.unsafeGet # load hash |
| 57 | + |
| 58 | + # Can be used only before first activation |
| 59 | + if ctx.pool.lastState != SyncState.idle: |
| 60 | + debug info & ": cannot setup target while syncer is activated", peer, |
| 61 | + targetHash=h.short, syncState=($buddy.syncState), |
| 62 | + nSyncPeers=ctx.pool.nBuddies |
| 63 | + ctx.pool.initTarget = Opt.none(Hash32) |
| 64 | + break body # return |
| 65 | + |
| 66 | + # Ignore failed peers |
| 67 | + if buddy.peerID in ctx.pool.failedPeers: |
| 68 | + break body # return |
| 69 | + |
| 70 | + # Grab header, so no other peer will interfere |
| 71 | + ctx.pool.initTarget = Opt.none(Hash32) |
| 72 | + |
| 73 | + # Fetch header or return |
| 74 | + const iv = BnRange.new(0u,0u) # dummy interval |
| 75 | + let hdrs = buddy.fetchHeadersReversed(iv, h, info).valueOr: |
| 76 | + if buddy.ctrl.running: |
| 77 | + trace info & ": peer failed on syncer target", peer, |
| 78 | + targetHash=h.short, failedPeers=ctx.pool.failedPeers.len, |
| 79 | + nSyncPeers=ctx.pool.nBuddies, hdrErrors=buddy.hdrErrors, |
| 80 | + syncState=($buddy.syncState) |
| 81 | + ctx.pool.initTarget = Opt.some(h) # restore target |
| 82 | + |
| 83 | + else: |
| 84 | + # Collect problematic peers for detecting cul-de-sac syncing |
| 85 | + ctx.pool.failedPeers.incl buddy.peerID |
| 86 | + |
| 87 | + # Abandon *manual* syncer target if there are too many errors |
| 88 | + if nFetchTargetFailedPeersThreshold < ctx.pool.failedPeers.len: |
| 89 | + warn "No such syncer target, abandoning it", peer, |
| 90 | + targetHash=h.short, failedPeers=ctx.pool.failedPeers.len, |
| 91 | + nSyncPeers=ctx.pool.nBuddies, hdrErrors=buddy.hdrErrors |
| 92 | + ctx.pool.failedPeers.clear() |
| 93 | + # not restoring target |
| 94 | + |
| 95 | + else: |
| 96 | + trace info & ": peer repeatedly failed", peer, |
| 97 | + targetHash=h.short, failedPeers=ctx.pool.failedPeers.len, |
| 98 | + nSyncPeers=ctx.pool.nBuddies, hdrErrors=buddy.hdrErrors, |
| 99 | + syncState=($buddy.syncState) |
| 100 | + ctx.pool.initTarget = Opt.some(h) # restore target |
| 101 | + |
| 102 | + break body # return |
| 103 | + # End `fetchHeadersReversed(..).valueOr` |
| 104 | + |
| 105 | + # Got header so the cul-de-sac protection can be cleared |
| 106 | + ctx.pool.failedPeers.clear() |
| 107 | + |
| 108 | + # Verify that the target header is usable |
| 109 | + let hdr = hdrs[0] |
| 110 | + if hdr.number <= ctx.chain.baseNumber: |
| 111 | + warn "Unusable syncer target, abandoning it", peer, target=hdr.bnStr, |
| 112 | + targetHash=h.short, base=ctx.chain.baseNumber.bnStr, |
| 113 | + head=ctx.chain.latestNumber.bnStr, nSyncPeers=ctx.pool.nBuddies |
| 114 | + break body # return |
| 115 | + |
| 116 | + # Start syncer |
| 117 | + debug info & ": activating manually", peer, target=hdr.bnStr, |
| 118 | + targetHash=h.short, nSyncPeers=ctx.pool.nBuddies |
| 119 | + |
| 120 | + ctx.hdrCache.headTargetUpdate(hdr, ctx.chain.baseHash) |
| 121 | + # End block: `body` |
| 122 | + |
| 123 | + discard # visual alignment |
| 124 | + |
| 125 | +# ------------------------------------------------------------------------------ |
| 126 | +# End |
| 127 | +# ------------------------------------------------------------------------------ |
0 commit comments