Skip to content

Commit f1b64fc

Browse files
authored
Beacon sync manual target by hash (#3575)
* Provide _manual_ syncer target setup by block hash alone why The hash can be collected from external pales, e.g. etherscan.io. This will replace the old (not so convenient) scheme where on needed the header (instead of the block hash.) * Provide new syncer target command line activation details This replaces the activation for the old logic using the full header (instead of the block hash.) * Remove cruft, update comments/logging
1 parent dcc18fb commit f1b64fc

File tree

14 files changed

+185
-95
lines changed

14 files changed

+185
-95
lines changed

execution_chain/config.nim

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -349,14 +349,6 @@ type
349349
defaultValue: 4'u64
350350
name: "debug-persist-batch-size" .}: uint64
351351

352-
beaconSyncTargetFile* {.
353-
hidden
354-
desc: "Load a file containg an rlp-encoded object \"(Header,Hash32)\" " &
355-
"to be used " &
356-
"as the first target before any other request from the CL " &
357-
"is accepted"
358-
name: "debug-beacon-sync-target-file" .}: Option[InputFile]
359-
360352
rocksdbMaxOpenFiles {.
361353
hidden
362354
defaultValue: defaultMaxOpenFiles
@@ -514,6 +506,14 @@ type
514506
defaultValueDesc: "\"jwt.hex\" in the data directory (see --data-dir)"
515507
name: "jwt-secret" .}: Option[InputFile]
516508

509+
beaconSyncTarget* {.
510+
separator: "\pBEACON SYNC OPTIONS:"
511+
desc: "Set the initial sync target specified by its 32 byte block" &
512+
" hash (e.g. as found on etherscan.io) represented by a" &
513+
" hex string. With this option, it is advisable to run this EL" &
514+
" against a CL which will result in a smaller memory footprint"
515+
name: "beacon-sync-target" .}: Option[string]
516+
517517
of `import`:
518518
maxBlocks* {.
519519
desc: "Maximum number of blocks to import"

execution_chain/nimbus_execution_client.nim

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,11 @@ proc setupP2P(nimbus: NimbusNode, conf: NimbusConf,
121121
nimbus.ethNode, nimbus.fc, conf.maxPeers)
122122

123123
# Optional for pre-setting the sync target (i.e. debugging)
124-
if conf.beaconSyncTargetFile.isSome():
125-
nimbus.beaconSyncRef.targetInit conf.beaconSyncTargetFile.unsafeGet.string
124+
if conf.beaconSyncTarget.isSome():
125+
let hex = conf.beaconSyncTarget.unsafeGet
126+
if not nimbus.beaconSyncRef.targetInit hex:
127+
fatal "Error parsing --beacon-sync-target hash32 argument", hash32=hex
128+
quit QuitFailure
126129

127130
# Connect directly to the static nodes
128131
let staticPeers = conf.getStaticPeers()

execution_chain/sync/beacon.nim

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import
1515
pkg/stew/[interval_set, sorted_set],
1616
../core/chain,
1717
../networking/p2p,
18+
./beacon/worker/headers/headers_target,
1819
./beacon/[worker, worker_desc],
1920
./[sync_desc, sync_sched, wire_protocol]
2021

@@ -68,10 +69,14 @@ proc init*(
6869
desc.ctx.pool.chain = chain
6970
desc
7071

71-
proc targetInit*(desc: BeaconSyncRef; rlpFile: string) =
72-
## Set up inital sprint (intended for debugging)
73-
desc.ctx.initalTargetFromFile(rlpFile, "targetInit").isOkOr:
74-
raiseAssert error
72+
proc targetInit*(desc: BeaconSyncRef; hex: string): bool =
73+
## Set up inital target sprint (if any, mainly for debugging)
74+
try:
75+
desc.ctx.headersTargetRequest(Hash32.fromHex(hex), "init")
76+
return true
77+
except ValueError:
78+
discard
79+
# false
7580

7681
proc start*(desc: BeaconSyncRef): bool =
7782
desc.startSync()

execution_chain/sync/beacon/README.md

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -204,36 +204,13 @@ command line without need to be requested by the consensus layer. In fact,
204204
this allows for single run synchronisation tests without the need of a
205205
consensus layer. The command line option needed here is
206206

207-
--debug-beacon-sync-target-file=<file>
208-
209-
where *&lt;file&gt;* contains the hexadecimal ASCII representation of an
210-
*RLP* encoded object
211-
212-
(block-header, finalised-hash)
213-
214-
as would be sent by the consensus layer to request a new synchronisation
215-
target. On mainnet, the following data example as *&lt;file&gt;* contents
216-
217-
f90287f90263a058391384fde62f9de477a57625cd4b1fdece5a45a06d9d5cd30bf02ee317e339a0
218-
1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347944838b106fce964
219-
7bdf1e7877bf73ce8b0bad5f97a0c3c83afe8ea7e07985b6bddd449d48f39896d0e9dcd1ccdbde52
220-
09266210f2cca0c0570c838716bb0fc00e8ee4acb50cf11931dc6e45b2bfd8499d05a7c8bf0168a0
221-
81867c86c8b06e1b8818983fdfa60723d7f22d9e2a193deab314391d79c073fbb90100fdfbb6e6dd
222-
daf8cefecdf5fdc5fbb825a3d73438ff7fd68db79f7f6dbc5a6d66c3ff7fafa9fdb0b4fcdbf7b5f3
223-
f739c927f14dfbfe0beeaea6f167adfeaf7ecbfee6b1fff88bcbffcbdfb10b55d9de7a5a8bfef3ff
224-
df6de5fdffde96dffbfffb37ff3f3fbbe674f7fdfc9fdfb74e29a160b7caffbfbeedd6defd77be77
225-
7ebbdfd3bddb5bb7cfbf6b29fc116ffdbed56ed4fbae71cfefedbbf9ebf9fe1fbe50e69ef32ffa65
226-
9ffd7ff67e3feef943df7deeffbcfee7f77757ffeb2edaae2ecbdcb85ef5fffeeaaf7ef7fd96be47
227-
2b6af765af3c6bffe77a3ddf2d676745de72ffb6faef6f27a1e5f7ffddb2fbff3f7b5a577a476dfb
228-
eb61d085e5fdfcefcd3fe5808401517663840225510083ee2a9f8467e1291798546974616e202874
229-
6974616e6275696c6465722e78797a29a04990a571fa4d7089fb9524e4fecfaccf9d04e94ea715c6
230-
330e11491233d3709b8800000000000000008418f4bb9ea0484b6897179f3ee33b0f59fec8d7ff56
231-
c5c191a088a237f71e4721e56139b199830c0000830a0000a0ae687f92ab829e434b6371031e89fd
232-
3ed8958c620c86486ba564a67a6e77c730a0aca1b7eeb3c34a7a8929713b6d5823c893d20a864c60
233-
8413e68b5f4b5a16b799
234-
235-
will initalise the syncer on *mainnet* to start syncing up to block number
236-
*#22115939* (with finalised hash for *#22106390*).
207+
--beacon-sync-target=<hash32>
208+
209+
where *&lt;hash32&gt;* is a hexadecimal ASCII representation of a block
210+
header. The hashes for particular blocks can be looked up at *etherscan.io*.
211+
This works well for short sync sprints (i.e. number of blocks to import.)
212+
For longer sync sprints the consensus layer should be availale as it reduces
213+
the memory footprint (by updating the finalised block.)
237214

238215
### Syncing on a low memory machine
239216

execution_chain/sync/beacon/worker.nim

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
{.push raises:[].}
1212

1313
import
14-
std/[strutils, syncio],
1514
pkg/[chronicles, chronos],
1615
pkg/eth/common,
1716
pkg/stew/[interval_set, sorted_set],
1817
../../common,
18+
./worker/headers/headers_target,
1919
./worker/update/[metrics, ticker],
2020
./worker/[blocks, headers, start_stop, update],
2121
./worker_desc
@@ -70,24 +70,6 @@ proc stop*(buddy: BeaconBuddyRef; info: static[string]) =
7070
nSyncPeers=(buddy.ctx.pool.nBuddies-1), syncState=($buddy.syncState)
7171
buddy.stopBuddy()
7272

73-
# --------------------
74-
75-
proc initalTargetFromFile*(
76-
ctx: BeaconCtxRef;
77-
file: string;
78-
info: static[string];
79-
): Result[void,string] =
80-
## Set up inital sprint from argument file (itended for debugging)
81-
try:
82-
var f = file.open(fmRead)
83-
defer: f.close()
84-
var rlp = rlpFromHex(f.readAll().splitWhitespace.join)
85-
ctx.pool.clReq = rlp.read(SyncClMesg)
86-
except CatchableError as e:
87-
return err("Error decoding file: \"" & file & "\"" &
88-
" (" & $e.name & ": " & e.msg & ")")
89-
ok()
90-
9173
# ------------------------------------------------------------------------------
9274
# Public functions
9375
# ------------------------------------------------------------------------------
@@ -196,6 +178,12 @@ template runPeer*(buddy: BeaconBuddyRef; info: static[string]): Duration =
196178

197179
# End `while()`
198180

181+
else:
182+
# Potential manual target set up
183+
buddy.headersTargetActivate info
184+
185+
# End block: `body`
186+
199187
# Idle sleep unless there is something to do
200188
if not buddy.somethingToCollect():
201189
bodyRc = workerIdleWaitInterval

execution_chain/sync/beacon/worker/blocks.nim

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ template blocksCollect*(
172172
break body # return
173173

174174
# This message might run in addition to the `chronicles.info` part
175-
trace info & "queued/staged or imported blocks",
175+
trace info & ": queued/staged or imported blocks",
176176
topImported=ctx.subState.top.bnStr,
177177
unprocBottom=ctx.blocksUnprocAvailBottom.bnStrIfAvail(ctx),
178178
nQueued, nImported, nStagedQ=ctx.blk.staged.len,
@@ -203,14 +203,12 @@ template blocksUnstage*(
203203
##
204204
var bodyRc = false
205205
block body:
206-
let
207-
ctx = buddy.ctx
208-
peer = buddy.peer
209-
206+
let ctx = buddy.ctx
210207
if ctx.blk.staged.len == 0:
211208
break body # return false => switch peer
212209

213210
var
211+
peer {.inject.} = buddy.peer
214212
nImported {.inject.} = 0u64 # statistics
215213
switchPeer {.inject.} = false # for return code
216214

@@ -253,7 +251,7 @@ template blocksUnstage*(
253251
nSyncPeers=ctx.pool.nBuddies
254252

255253
elif switchPeer or 0 < ctx.blk.staged.len:
256-
debug info & ": no blocks unqueued", peer,
254+
trace info & ": no blocks unqueued", peer,
257255
topImported=ctx.subState.top.bnStr, nStagedQ=ctx.blk.staged.len,
258256
nSyncPeers=ctx.pool.nBuddies, switchPeer
259257

execution_chain/sync/beacon/worker/headers.nim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ template headersCollect*(buddy: BeaconBuddyRef; info: static[string]) =
170170
not ctx.pool.seenData and
171171
buddy.peerID notin ctx.pool.failedPeers and
172172
buddy.ctrl.stopped:
173-
# Collect peer for detecting cul-de-sac syncing (i.e. non-existing
173+
# Collect peers for detecting cul-de-sac syncing (i.e. non-existing
174174
# block chain or similar.)
175175
ctx.pool.failedPeers.incl buddy.peerID
176176

@@ -247,7 +247,7 @@ proc headersUnstage*(buddy: BeaconBuddyRef; info: static[string]): bool =
247247
nSyncPeers=ctx.pool.nBuddies
248248

249249
elif switchPeer or 0 < ctx.hdr.staged.len:
250-
debug info & ": no headers processed", peer,
250+
trace info & ": no headers processed", peer,
251251
D=ctx.hdrCache.antecedent.bnStr,
252252
nStashed, nStagedQ=ctx.hdr.staged.len, nSyncPeers=ctx.pool.nBuddies,
253253
switchPeer

execution_chain/sync/beacon/worker/headers/headers_fetch.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ template fetchHeadersReversed*(
127127
break body # return err()
128128

129129
# Verify that first block number matches
130-
if h[^1].number != ivReq.minPt:
130+
if h[^1].number != ivReq.minPt and ivReq.minPt != 0:
131131
buddy.hdrFetchRegisterError()
132132
trace trEthRecvReceivedBlockHeaders, peer, nReq=req.maxResults,
133133
hash=topHash.toStr, reqMinPt=ivReq.minPt.bnStr,

execution_chain/sync/beacon/worker/headers/headers_headers.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ proc headersStashOnDisk*(
128128
return err() # stop
129129

130130
let dBottom = ctx.hdrCache.antecedent.number # new antecedent
131-
debug info & ": Serialised headers stashed", peer,
131+
trace info & ": Serialised headers stashed", peer,
132132
iv=(if dBottom < dTop: (dBottom,dTop-1).bnStr else: "n/a"),
133133
nHeaders=(dTop - dBottom),
134134
nSkipped=(if rc.isErr: 0u64
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
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

Comments
 (0)