From 904a0f694c0b47bf53543ffdc0164a447f952937 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Mon, 27 Oct 2025 10:43:44 +0100 Subject: [PATCH 1/4] Beacon node startup cleanups * KZG can now be loaded lazily - at the same time, normalise command line parameter for using a different setup to use `--debug` prefix like all other hidden options, allowing the option can be shared between eth1/eth2 * move init logic around so that "main thread" / process-related initialization has its own home that is not shared with libraries * make init fail gracefully (instead of quitting) * remove error handling made redundant by `async` raises --- beacon_chain/conf.nim | 25 +-- beacon_chain/networking/eth2_network.nim | 198 ++++++++---------- beacon_chain/nimbus_beacon_node.nim | 184 ++++++++-------- beacon_chain/nimbus_light_client.nim | 6 +- beacon_chain/trusted_node_sync.nim | 3 +- .../test_fixture_fork_choice.nim | 3 - 6 files changed, 195 insertions(+), 224 deletions(-) diff --git a/beacon_chain/conf.nim b/beacon_chain/conf.nim index 8a5a061014..2db03a2c57 100644 --- a/beacon_chain/conf.nim +++ b/beacon_chain/conf.nim @@ -22,7 +22,6 @@ import eth/enr/enr, json_serialization, json_serialization/std/net as jsnet, web3/confutils_defs, chronos/transports/common, - kzg4844/kzg, ./spec/[engine_authentication, keystore, network, crypto], ./spec/datatypes/base, ./networking/network_metadata, @@ -680,13 +679,12 @@ type defaultValue: HistoryMode.Prune name: "history".}: HistoryMode - # https://notes.ethereum.org/@bbusa/dencun-devnet-6 - # "Please ensure that there is a way for us to specify the file through a - # runtime flag such as --trusted-setup-file (or similar)." trustedSetupFile* {. hidden - desc: "Experimental, debug option; could disappear at any time without warning" - name: "temporary-debug-trusted-setup-file" .}: Option[string] + desc: "Alternative EIP-4844 trusted setup file" + defaultValue: none(string) + defaultValueDesc: "Baked in trusted setup" + name: "debug-trusted-setup-file" .}: Option[string] bandwidthEstimate* {. hidden @@ -760,11 +758,6 @@ type desc: "Output wallet file" name: "new-wallet-file" .}: Option[OutFile] - #[ - of DepositsCmd.status: - discard - ]# - of DepositsCmd.`import`: importedDepositsDir* {. argument @@ -1530,16 +1523,6 @@ proc engineApiUrls*(config: auto): seq[EngineApiUrl] = (elUrls & config.web3Urls).toFinalEngineApiUrls( config.jwtSecret.configJwtSecretOpt) -proc loadKzgTrustedSetup*(): Result[void, string] = - static: doAssert const_preset in ["mainnet", "gnosis", "minimal"] - loadTrustedSetupFromString(kzg.trustedSetup, 0) - -proc loadKzgTrustedSetup*(trustedSetupPath: string): Result[void, string] = - try: - loadTrustedSetupFromString(readFile(trustedSetupPath), 0) - except IOError as err: - err(err.msg) - proc formatIt*(v: Option[IpAddress]): string = if v.isSome(): $v.get() diff --git a/beacon_chain/networking/eth2_network.nim b/beacon_chain/networking/eth2_network.nim index 23ba258f36..0d1b83ebee 100644 --- a/beacon_chain/networking/eth2_network.nim +++ b/beacon_chain/networking/eth2_network.nim @@ -193,8 +193,6 @@ type # erroneous request-specific responses. PeerScoreLow = 237 # 79 * 3 - TransmissionError* = object of CatchableError - Eth2NetworkingErrorKind* = enum # Potentially benign errors (network conditions) BrokenConnection @@ -381,12 +379,6 @@ proc openStream(node: Eth2Node, except LPError as exc: debug "Dialing failed", exc = exc.msg neterr BrokenConnection - except CancelledError as exc: - raise exc - except CatchableError as exc: - # TODO remove once libp2p supports `raises` - debug "Unexpected error when opening stream", exc = exc.msg - neterr UnknownError proc init(T: type Peer, network: Eth2Node, peerId: PeerId): Peer {.gcsafe.} @@ -620,9 +612,12 @@ func add(s: var seq[byte], pos: var int, bytes: openArray[byte]) = pos += bytes.len proc writeChunkSZ( - conn: Connection, responseCode: Opt[ResponseCode], - uncompressedLen: uint64, payloadSZ: openArray[byte], - contextBytes: openArray[byte] = []): Future[void] = + conn: Connection, + responseCode: Opt[ResponseCode], + uncompressedLen: uint64, + payloadSZ: openArray[byte], + contextBytes: openArray[byte] = [], +): Future[void] {.async: (raises: [CancelledError, LPStreamError], raw: true).} = let uncompressedLenBytes = toBytes(uncompressedLen, Leb128) @@ -639,10 +634,12 @@ proc writeChunkSZ( data.add(pos, payloadSZ) conn.write(data) -proc writeChunk(conn: Connection, - responseCode: Opt[ResponseCode], - payload: openArray[byte], - contextBytes: openArray[byte] = []): Future[void] = +proc writeChunk( + conn: Connection, + responseCode: Opt[ResponseCode], + payload: openArray[byte], + contextBytes: openArray[byte] = [], +): Future[void] {.async: (raises: [CancelledError, LPStreamError], raw: true).} = let uncompressedLenBytes = toBytes(payload.lenu64, Leb128) var @@ -676,16 +673,16 @@ func formatErrorMsg(msg: ErrorMsg): string = string.fromBytes(asSeq(msg)) -proc sendErrorResponse(peer: Peer, - conn: Connection, - responseCode: ResponseCode, - errMsg: ErrorMsg): Future[void] = +proc sendErrorResponse( + peer: Peer, conn: Connection, responseCode: ResponseCode, errMsg: ErrorMsg +): Future[void] {.async: (raises: [CancelledError, LPStreamError], raw: true).} = debug "Error processing request", peer, responseCode, errMsg = formatErrorMsg(errMsg) conn.writeChunk(Opt.some responseCode, SSZ.encode(errMsg)) -proc sendNotificationMsg(peer: Peer, protocolId: string, requestBytes: seq[byte]) - {.async: (raises: [CancelledError]).} = +proc sendNotificationMsg( + peer: Peer, protocolId: string, requestBytes: seq[byte] +) {.async: (raises: [CancelledError]).} = # Notifications are sent as a best effort, ie errors are not reported back # to the caller let @@ -701,34 +698,33 @@ proc sendNotificationMsg(peer: Peer, protocolId: string, requestBytes: seq[byte] try: await stream.writeChunk(Opt.none ResponseCode, requestBytes) - except CancelledError as exc: - raise exc - except CatchableError as exc: + except LPError as exc: debug "Error while writing notification", peer, protocolId, exc = exc.msg finally: - try: - await noCancel stream.close() - except CatchableError as exc: - debug "Unexpected error while closing notification stream", - peer, protocolId, exc = exc.msg + await noCancel stream.close() proc sendResponseChunkBytesSZ( - response: UntypedResponse, uncompressedLen: uint64, + response: UntypedResponse, + uncompressedLen: uint64, payloadSZ: openArray[byte], - contextBytes: openArray[byte] = []): Future[void] = + contextBytes: openArray[byte] = [], +): Future[void] {.async: (raises: [CancelledError, LPStreamError], raw: true).} = inc response.writtenChunks response.stream.writeChunkSZ( - Opt.some ResponseCode.Success, uncompressedLen, payloadSZ, contextBytes) + Opt.some ResponseCode.Success, uncompressedLen, payloadSZ, contextBytes + ) proc sendResponseChunkBytes( - response: UntypedResponse, payload: openArray[byte], - contextBytes: openArray[byte] = []): Future[void] = + response: UntypedResponse, + payload: openArray[byte], + contextBytes: openArray[byte] = [], +): Future[void] {.async: (raises: [CancelledError, LPStreamError], raw: true).} = inc response.writtenChunks response.stream.writeChunk(Opt.some ResponseCode.Success, payload, contextBytes) proc sendResponseChunk( - response: UntypedResponse, val: auto, - contextBytes: openArray[byte] = []): Future[void] = + response: UntypedResponse, val: auto, contextBytes: openArray[byte] = [] +): Future[void] {.async: (raises: [CancelledError, LPStreamError], raw: true).} = sendResponseChunkBytes(response, SSZ.encode(val), contextBytes) template sendUserHandlerResultAsChunkImpl*(stream: Connection, @@ -748,9 +744,7 @@ proc uncompressFramedStream(conn: Connection, await conn.readExactly(addr header[0], header.len) except LPStreamEOFError, LPStreamIncompleteError: return err "Unexpected EOF before snappy header" - except CancelledError as exc: - raise exc - except CatchableError as exc: + except LPStreamError as exc: return err "Unexpected error reading header: " & exc.msg if header != framingHeader: @@ -770,9 +764,7 @@ proc uncompressFramedStream(conn: Connection, await conn.readExactly(addr frameHeader[0], frameHeader.len) except LPStreamEOFError, LPStreamIncompleteError: return err "Snappy frame header missing" - except CancelledError as exc: - raise exc - except CatchableError as exc: + except LPStreamError as exc: return err "Unexpected error reading frame header: " & exc.msg let (id, dataLen) = decodeFrameHeader(frameHeader) @@ -788,9 +780,7 @@ proc uncompressFramedStream(conn: Connection, await conn.readExactly(addr frameData[0], dataLen) except LPStreamEOFError, LPStreamIncompleteError: return err "Incomplete snappy frame" - except CancelledError as exc: - raise exc - except CatchableError as exc: + except LPStreamError as exc: return err "Unexpected error reading frame data: " & exc.msg if id == chunkCompressed: @@ -888,9 +878,7 @@ proc readVarint2(conn: Connection): Future[NetRes[uint64]] {. neterr UnexpectedEOF except InvalidVarintError: neterr InvalidSizePrefix - except CancelledError as exc: - raise exc - except CatchableError as exc: + except LPStreamError as exc: debug "Unexpected error", exc = exc.msg neterr UnknownError @@ -941,9 +929,7 @@ proc readResponseChunk( # the stream. It also means that our connection with remote peer is not # broken and new streams could be initiated. return neterr PotentiallyExpectedEOF - except CancelledError as exc: - raise exc - except CatchableError as exc: + except LPStreamError as exc: warn "Unexpected error", exc = exc.msg return neterr UnknownError @@ -1045,15 +1031,11 @@ proc doMakeEth2Request( res except CancelledError as exc: raise exc - except CatchableError: + except LPStreamError: peer.updateScore(PeerScorePoorRequest) neterr BrokenConnection finally: - try: - await noCancel stream.closeWithEOF() - except CatchableError as exc: - debug "Unexpected error while closing stream", - peer, protocolId, exc = exc.msg + await stream.closeWithEOF() proc makeEth2Request( peer: Peer, protocolId: string, requestBytes: seq[byte], @@ -1236,8 +1218,6 @@ proc handleIncomingStream(network: Eth2Node, when isEmptyMsg: NetRes[MsgRec].ok default(MsgRec) else: - # TODO(zah) The TTFB timeout is not implemented in LibP2P streams - # back-end let deadline = sleepAsync RESP_TIMEOUT_DUR awaitWithTimeout( @@ -1337,10 +1317,7 @@ proc handleIncomingStream(network: Eth2Node, debug "Error processing an incoming request", exc = exc.msg, msgName finally: - try: - await noCancel conn.closeWithEOF() - except CatchableError as exc: - debug "Unexpected error while closing incoming connection", exc = exc.msg + await noCancel conn.closeWithEOF() releasePeer(peer) func toPeerAddr*(r: enr.TypedRecord, @@ -2347,15 +2324,18 @@ func gossipId( messageDigest.data[0..19] -proc newBeaconSwitch(config: BeaconNodeConf | LightClientConf, - seckey: PrivateKey, address: MultiAddress, - rng: ref HmacDrbgContext): Switch {.raises: [CatchableError].} = +proc newBeaconSwitch( + config: BeaconNodeConf | LightClientConf, + seckey: PrivateKey, + address: MultiAddress, + rng: ref HmacDrbgContext, +): Result[Switch, string] = let service: Service = WildcardAddressResolverService.new() var sb = SwitchBuilder.new() # Order of multiplexers matters, the first will be default - - sb + try: + ok sb .withPrivateKey(seckey) .withAddress(address) .withRng(rng) @@ -2366,15 +2346,18 @@ proc newBeaconSwitch(config: BeaconNodeConf | LightClientConf, .withTcpTransport({ServerFlags.ReuseAddr}) .withServices(@[service]) .build() - -proc createEth2Node*(rng: ref HmacDrbgContext, - config: BeaconNodeConf | LightClientConf, - netKeys: NetKeyPair, - cfg: RuntimeConfig, - forkDigests: ref ForkDigests, - getBeaconTime: GetBeaconTimeFn, - genesis_validators_root: Eth2Digest): Eth2Node - {.raises: [CatchableError].} = + except LPError as exc: + err(exc.msg) + +proc createEth2Node*( + rng: ref HmacDrbgContext, + config: BeaconNodeConf | LightClientConf, + netKeys: NetKeyPair, + cfg: RuntimeConfig, + forkDigests: ref ForkDigests, + getBeaconTime: GetBeaconTimeFn, + genesis_validators_root: Eth2Digest, +): Result[Eth2Node, string] = let wallEpoch = getBeaconTime().slotOrZero(cfg.timeParams).epoch enrForkId = cfg.getENRForkID(wallEpoch, genesis_validators_root) @@ -2397,16 +2380,18 @@ proc createEth2Node*(rng: ref HmacDrbgContext, if s.startsWith("enr:"): let enr = parseBootstrapAddress(s).valueOr: - fatal "Failed to parse bootstrap address", enr=s - quit 1 + warn "Failed to parse direct peer address, skipping", enr=s, err = error + return err("Invalid direct peer") typedEnr = TypedRecord.fromRecord(enr) peerAddress = toPeerAddr(typedEnr, tcpProtocol).get() (peerAddress.peerId, peerAddress.addrs[0]) elif s.startsWith("/"): - parseFullAddress(s).tryGet() + parseFullAddress(s).valueOr: + warn "Failed to parse direct peer address, skipping", s, err = error + return err("Invalid direct peer") else: - fatal "direct peers address should start with / (multiaddress) or enr:", conf=s - quit 1 + warn "Direct peers address should start with / (multiaddress) or enr:, skipping", s + return err("Invalid direct peer") res.mgetOrPut(peerId, @[]).add(address) info "Adding privileged direct peer", peerId, address res @@ -2423,9 +2408,9 @@ proc createEth2Node*(rng: ref HmacDrbgContext, # TODO nim-libp2p still doesn't have support for announcing addresses # that are different from the host address (this is relevant when we # are running behind a NAT). - var switch = newBeaconSwitch(config, netKeys.seckey, hostAddress, rng) - - let phase0Prefix = "/eth2/" & $forkDigests.phase0 + let + switch = ?newBeaconSwitch(config, netKeys.seckey, hostAddress, rng) + phase0Prefix = "/eth2/" & $forkDigests.phase0 func msgIdProvider(m: messages.Message): Result[seq[byte], ValidationResult] = try: @@ -2471,18 +2456,26 @@ proc createEth2Node*(rng: ref HmacDrbgContext, directPeers = directPeers, bandwidthEstimatebps = config.bandwidthEstimate.get(100_000_000) ) - pubsub = GossipSub.init( - switch = switch, - msgIdProvider = msgIdProvider, - # We process messages in the validator, so we don't need data callbacks - triggerSelf = false, - sign = false, - verifySignature = false, - anonymize = true, - maxMessageSize = static(MAX_PAYLOAD_SIZE.int), - parameters = params) - - switch.mount(pubsub) + pubsub = + try: + GossipSub.init( + switch = switch, + msgIdProvider = msgIdProvider, + # We process messages in the validator, so we don't need data callbacks + triggerSelf = false, + sign = false, + verifySignature = false, + anonymize = true, + maxMessageSize = static(MAX_PAYLOAD_SIZE.int), + parameters = params, + ) + except InitializationError as exc: + raiseAssert "Invalid gossipsub parameters: " & exc.msg + + try: + switch.mount(pubsub) + except LPError as exc: # Invalid params.. + return err("Cannot mount pubsub: " & exc.msg) let node = Eth2Node.new( config, cfg, enrForkId, discoveryForkId, forkDigests, getBeaconTime, switch, pubsub, extIp, @@ -2494,7 +2487,7 @@ proc createEth2Node*(rng: ref HmacDrbgContext, proc(topic: string): bool {.gcsafe, raises: [].} = topic in node.validTopics - node + ok node func announcedENR*(node: Eth2Node): enr.Record = doAssert node.discovery != nil, "The Eth2Node must be initialized" @@ -2649,14 +2642,7 @@ func gossipEncode(msg: auto): seq[byte] = proc broadcast(node: Eth2Node, topic: string, msg: seq[byte]): Future[SendResult] {.async: (raises: [CancelledError]).} = - let peers = - try: - await node.pubsub.publish(topic, msg) - except CancelledError as exc: - raise exc - except CatchableError as exc: - debug "Unexpected error during broadcast", exc = exc.msg - return err("Broadcast failed") + let peers = await node.pubsub.publish(topic, msg) # TODO remove workaround for sync committee BN/VC log spam if peers > 0 or find(topic, "sync_committee_") != -1: diff --git a/beacon_chain/nimbus_beacon_node.nim b/beacon_chain/nimbus_beacon_node.nim index 8052cf746c..631162b771 100644 --- a/beacon_chain/nimbus_beacon_node.nim +++ b/beacon_chain/nimbus_beacon_node.nim @@ -109,7 +109,8 @@ proc doRunTrustedNodeSync( trustedBlockRoot: Option[Eth2Digest], backfill: bool, reindex: bool, - genesisState: ref ForkedHashedBeaconState) {.async.} = + genesisState: ref ForkedHashedBeaconState, +) {.async: (raises: [CancelledError]).} = let syncTarget = if stateId.isSome: if trustedBlockRoot.isSome: @@ -291,7 +292,8 @@ proc initFullNode( dag: ChainDAGRef, clist: ChainListRef, taskpool: Taskpool, - getBeaconTime: GetBeaconTimeFn) {.async.} = + getBeaconTime: GetBeaconTimeFn, +) {.async: (raises: [CancelledError]).} = template config(): auto = node.config proc onPhase0AttestationReceived(data: phase0.Attestation) = @@ -715,11 +717,25 @@ proc init*( T: type BeaconNode, rng: ref HmacDrbgContext, config: BeaconNodeConf, - metadata: Eth2NetworkMetadata, taskpool: Taskpool, -): Future[BeaconNode] {.async.} = - var - genesisState: ref ForkedHashedBeaconState = nil +): Future[Opt[BeaconNode]] {.async: (raises: [CancelledError]).} = + var config = config + + config.createDumpDirs() + + let metadata = config.loadEth2Network() + + # Updating the config based on the metadata certainly is not beautiful but it + # works + for node in metadata.bootstrapNodes: + config.bootstrapNodes.add node + if config.syncHorizon.isNone: + config.syncHorizon = some(metadata.cfg.timeParams.defaultSyncHorizon) + + if ProcessState.stopIt(notice("Shutting down", reason = it)): + return Opt.none(BeaconNode) + + var genesisState: ref ForkedHashedBeaconState = nil template cfg: auto = metadata.cfg @@ -734,7 +750,7 @@ proc init*( beaconClock = BeaconClock.init( metadata.cfg.timeParams, genesisTime).valueOr: fatal "Invalid genesis time in genesis state", genesisTime - quit 1 + return Opt.none(BeaconNode) currentSlot = beaconClock.currentSlot checkpoint = Checkpoint( epoch: epoch(getStateField(genesisState[], slot)), @@ -752,7 +768,7 @@ proc init*( if metadata.cfg.ALTAIR_FORK_EPOCH != GENESIS_EPOCH: fatal WeakSubjectivityLogMessage, current_slot = currentSlot, altair_fork_epoch = metadata.cfg.ALTAIR_FORK_EPOCH - quit 1 + return Opt.none(BeaconNode) if metadata.genesis.kind == BakedIn: if config.genesisState.isSome: @@ -838,15 +854,15 @@ proc init*( except SszError as err: fatal "Checkpoint state loading failed", err = formatMsg(err, checkpointStatePath) - quit 1 + return Opt.none(BeaconNode) except CatchableError as err: fatal "Failed to read checkpoint state file", err = err.msg - quit 1 + return Opt.none(BeaconNode) if not getStateField(tmp[], slot).is_epoch: fatal "--finalized-checkpoint-state must point to a state for an epoch slot", slot = getStateField(tmp[], slot) - quit 1 + return Opt.none(BeaconNode) tmp else: nil @@ -873,7 +889,7 @@ proc init*( if genesisState.isNil and checkpointState.isNil: fatal "No database and no genesis snapshot found. Please supply a genesis.ssz " & "with the network configuration" - quit 1 + return Opt.none(BeaconNode) if not genesisState.isNil and not checkpointState.isNil: if getStateField(genesisState[], genesis_validators_root) != @@ -883,7 +899,7 @@ proc init*( genesisState[], genesis_validators_root), rootFromCheckpoint = getStateField( checkpointState[], genesis_validators_root) - quit 1 + return Opt.none(BeaconNode) try: # Always store genesis state if we have it - this allows reindexing and @@ -901,12 +917,11 @@ proc init*( doAssert ChainDAGRef.isInitialized(db).isOk(), "preInit should have initialized db" except CatchableError as exc: error "Failed to initialize database", err = exc.msg - quit 1 - else: - if not checkpointState.isNil: - fatal "A database already exists, cannot start from given checkpoint", - dataDir = config.dataDir - quit 1 + return Opt.none(BeaconNode) + elif not checkpointState.isNil: + fatal "A database already exists, cannot start from given checkpoint", + dataDir = config.dataDir + return Opt.none(BeaconNode) # Doesn't use std/random directly, but dependencies might randomize(rng[].rand(high(int))) @@ -929,9 +944,9 @@ proc init*( config, cfg, db, eventBus, validatorMonitor, networkGenesisValidatorsRoot) genesisTime = getStateField(dag.headState, genesis_time) - beaconClock = BeaconClock.init(cfg.timeParams, genesisTime).valueOr: + beaconClock = BeaconClock.init(metadata.cfg.timeParams, genesisTime).valueOr: fatal "Invalid genesis time in state", genesisTime - quit 1 + return Opt.none(BeaconNode) getBeaconTime = beaconClock.getBeaconTimeFn() @@ -951,7 +966,7 @@ proc init*( res.clear().isOkOr: fatal "Unable to reset backfill database", path = config.databaseDir(), reason = error - quit 1 + return Opt.none(BeaconNode) res info "Backfill database initialized", path = config.databaseDir(), @@ -977,8 +992,16 @@ proc init*( nickname = if config.nodeName == "auto": shortForm(netKeys) else: config.nodeName network = createEth2Node( - rng, config, netKeys, cfg, dag.forkDigests, getBeaconTime, - getStateField(dag.headState, genesis_validators_root)) + rng, + config, + netKeys, + cfg, + dag.forkDigests, + getBeaconTime, + getStateField(dag.headState, genesis_validators_root), + ).valueOr: + error "Failed to initialize node", err = error + return Opt.none(BeaconNode) case config.slashingDbKind of SlashingDbKind.v2: @@ -1078,7 +1101,7 @@ proc init*( node.updateLightClientFromDag() - node + ok node func verifyFinalization(node: BeaconNode, slot: Slot) = # Epoch must be >= 4 to check finalization @@ -2604,7 +2627,6 @@ proc run*(node: BeaconNode, stopper: StopFuture) {.raises: [CatchableError].} = # time to say goodbye node.stop() - func formatGwei(amount: Gwei): string = # TODO This is implemented in a quite a silly way. # Better routines for formatting decimal numbers @@ -2748,56 +2770,57 @@ when not defined(windows): asyncSpawn statusBarUpdatesPollingLoop() -proc doRunBeaconNode*( - config: var BeaconNodeConf, - rng: ref HmacDrbgContext, - taskpool: Taskpool, - stopper: StopFuture, -) {.raises: [CatchableError], gcsafe.} = - doAssert taskpool != nil +proc doRunBeaconNode( + config: var BeaconNodeConf, rng: ref HmacDrbgContext +) {.raises: [CatchableError].} = info "Launching beacon node", - version = fullVersionStr, - bls_backend = $BLS_BACKEND, - const_preset, - cmdParams = commandLineParams(), - config - - config.createDumpDirs() - - # There are no managed event loops in here, to do a graceful shutdown, but - # letting the default Ctrl+C handler exit is safe, since we only read from - # the db. - let metadata = config.loadEth2Network() - - # Updating the config based on the metadata certainly is not beautiful but it - # works - for node in metadata.bootstrapNodes: - config.bootstrapNodes.add node - if config.syncHorizon.isNone: - config.syncHorizon = some(metadata.cfg.timeParams.defaultSyncHorizon) - - block: - let res = - if config.trustedSetupFile.isNone: - conf.loadKzgTrustedSetup() - else: - conf.loadKzgTrustedSetup(config.trustedSetupFile.get) - if res.isErr(): - raiseAssert res.error() + version = fullVersionStr, + bls_backend = $BLS_BACKEND, + const_preset, + cmdParams = commandLineParams(), + config + + ProcessState.setupStopHandlers() + + createPidFile(config.dataDir.string / "beacon_node.pid") + + if config.rpcEnabled.isSome: + warn "Nimbus's JSON-RPC server has been removed. This includes the --rpc, --rpc-port, and --rpc-address configuration options. https://nimbus.guide/rest-api.html shows how to enable and configure the REST Beacon API server which replaces it." + + template ignoreDeprecatedOption(option: untyped): untyped = + if config.option.isSome: + warn "Ignoring deprecated configuration option", option = config.option.get + + ignoreDeprecatedOption requireEngineAPI + ignoreDeprecatedOption safeSlotsToImportOptimistically + ignoreDeprecatedOption terminalTotalDifficultyOverride + ignoreDeprecatedOption optimistic + ignoreDeprecatedOption validatorMonitorTotals + ignoreDeprecatedOption web3ForcePolling + ignoreDeprecatedOption finalizedDepositTreeSnapshot + ignoreDeprecatedOption finalizedCheckpointBlock + + # Trusted setup is needed for Cancun+ blocks and is shared between threads, + # so it needs to be initalized from the main thread before anything else tries + # to use it + if config.trustedSetupFile.isSome: + kzg.loadTrustedSetup(config.trustedSetupFile.get(), 0).isOkOr: + fatal "Cannot load KZG trusted setup from file", msg = error + quit(QuitFailure) if ProcessState.stopIt(notice("Shutting down", reason = it)): return - let node = waitFor BeaconNode.init(rng, config, metadata, taskpool) + let + taskpool = setupTaskpool(config.numThreads) + node = waitFor(BeaconNode.init(rng, config, taskpool)).valueOr: + return # Nim GC metrics (for the main thread) will be collected in onSecond(), but # we disable piggy-backing on other metrics here. setSystemMetricsAutomaticUpdate(false) - node.metricsServer = (waitFor config.initMetricsServer()).valueOr: - return - - if ProcessState.stopIt(notice("Shutting down", reason = it)): + node.metricsServer = waitFor(config.initMetricsServer()).valueOr: return when not defined(windows): @@ -2806,9 +2829,10 @@ proc doRunBeaconNode*( initStatusBar(node) if node.nickname != "": - dynamicLogScope(node = node.nickname): node.run(stopper) + dynamicLogScope(node = node.nickname): + node.run(nil) else: - node.run(stopper) + node.run(nil) proc doRecord(config: BeaconNodeConf, rng: var HmacDrbgContext) {. raises: [CatchableError].} = @@ -2904,29 +2928,7 @@ proc handleStartUpCmd(config: var BeaconNodeConf) {.raises: [CatchableError].} = let rng = HmacDrbgContext.new() case config.cmd - of BNStartUpCmd.beaconNode: - createPidFile(config.dataDir.string / "beacon_node.pid") - ProcessState.setupStopHandlers() - - if config.rpcEnabled.isSome: - warn "Nimbus's JSON-RPC server has been removed. This includes the --rpc, --rpc-port, and --rpc-address configuration options. https://nimbus.guide/rest-api.html shows how to enable and configure the REST Beacon API server which replaces it." - - template ignoreDeprecatedOption(option: untyped): untyped = - if config.option.isSome: - warn "Ignoring deprecated configuration option", - option = config.option.get - ignoreDeprecatedOption requireEngineAPI - ignoreDeprecatedOption safeSlotsToImportOptimistically - ignoreDeprecatedOption terminalTotalDifficultyOverride - ignoreDeprecatedOption optimistic - ignoreDeprecatedOption validatorMonitorTotals - ignoreDeprecatedOption web3ForcePolling - ignoreDeprecatedOption finalizedDepositTreeSnapshot - ignoreDeprecatedOption finalizedCheckpointBlock - - let taskpool = setupTaskpool(config.numThreads) - - doRunBeaconNode(config, rng, taskpool, nil) + of BNStartUpCmd.beaconNode: doRunBeaconNode(config, rng) of BNStartUpCmd.deposits: doDeposits(config, rng[]) of BNStartUpCmd.wallets: doWallets(config, rng[]) of BNStartUpCmd.record: doRecord(config, rng[]) diff --git a/beacon_chain/nimbus_light_client.nim b/beacon_chain/nimbus_light_client.nim index 770cbc4a46..a884481860 100644 --- a/beacon_chain/nimbus_light_client.nim +++ b/beacon_chain/nimbus_light_client.nim @@ -87,8 +87,10 @@ proc main() {.noinline, raises: [CatchableError].} = rng = HmacDrbgContext.new() netKeys = getRandomNetKeys(rng[]) network = createEth2Node( - rng, config, netKeys, cfg, - forkDigests, getBeaconTime, genesis_validators_root) + rng, config, netKeys, cfg, forkDigests, getBeaconTime, genesis_validators_root + ).valueOr: + error "Failed to initialize node", err = error + quit QuitFailure engineApiUrls = config.engineApiUrls elManager = if engineApiUrls.len > 0: diff --git a/beacon_chain/trusted_node_sync.nim b/beacon_chain/trusted_node_sync.nim index 259d60b1a8..101b3464fa 100644 --- a/beacon_chain/trusted_node_sync.nim +++ b/beacon_chain/trusted_node_sync.nim @@ -63,7 +63,8 @@ proc doTrustedNodeSync*( syncTarget: TrustedNodeSyncTarget, backfill: bool, reindex: bool, - genesisState: ref ForkedHashedBeaconState = nil) {.async.} = + genesisState: ref ForkedHashedBeaconState = nil, +) {.async: (raises: [CancelledError]).} = logScope: restUrl syncTarget diff --git a/tests/consensus_spec/test_fixture_fork_choice.nim b/tests/consensus_spec/test_fixture_fork_choice.nim index 1752f66cd2..30d5717253 100644 --- a/tests/consensus_spec/test_fixture_fork_choice.nim +++ b/tests/consensus_spec/test_fixture_fork_choice.nim @@ -456,8 +456,5 @@ template fcSuite(suiteName: static[string], testPathElem: static[string]) = for kind, path in walkDir(basePath, relative = true, checkDir = true): runTest(suiteName, basePath/path, fork) -from ../../beacon_chain/conf import loadKzgTrustedSetup -discard loadKzgTrustedSetup() # Required for Deneb tests - fcSuite("ForkChoice", "fork_choice") fcSuite("Sync", "sync") From 59883db987ae747147b1524fff11a4ae4009d371 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Mon, 27 Oct 2025 14:34:05 +0100 Subject: [PATCH 2/4] test fixes --- beacon_chain/fork_choice/fork_choice.nim | 10 +++++----- tests/test_keymanager_api.nim | 10 +--------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/beacon_chain/fork_choice/fork_choice.nim b/beacon_chain/fork_choice/fork_choice.nim index 93582aa1e7..2175b46e63 100644 --- a/beacon_chain/fork_choice/fork_choice.nim +++ b/beacon_chain/fork_choice/fork_choice.nim @@ -692,7 +692,7 @@ when isMainModule: var new_balances: seq[Gwei] for i in 0 ..< validator_count: - indices.add fakeHash(i), i + indices[fakeHash(i)] = i votes.add VoteTracker( # Move vote from root 0 to root 1 current_root: fakeHash(0), @@ -729,8 +729,8 @@ when isMainModule: var votes: seq[VoteTracker] # Add 2 blocks - indices.add fakeHash(1), 0 - indices.add fakeHash(2), 1 + indices[fakeHash(1)] = 0 + indices[fakeHash(2)] = 1 # 1 validator at the start, 2 at the end var deltas = newSeqUninit[Delta](2) @@ -768,8 +768,8 @@ when isMainModule: var votes: seq[VoteTracker] # Add 2 blocks - indices.add fakeHash(1), 0 - indices.add fakeHash(2), 1 + indices[fakeHash(1)] = 0 + indices[fakeHash(2)] = 1 # 2 validator at the start, 1 at the end var deltas = newSeqUninit[Delta](2) diff --git a/tests/test_keymanager_api.nim b/tests/test_keymanager_api.nim index 287a1098da..09d66e335f 100644 --- a/tests/test_keymanager_api.nim +++ b/tests/test_keymanager_api.nim @@ -342,14 +342,6 @@ proc initBeaconNode(basePort: int): Future[BeaconNode] {.async: (raises: []).} = raiseAssert exc.msg let - metadata = - try: - loadEth2NetworkMetadata(dataDir).expect("Metadata is compatible") - except IOError as exc: - raiseAssert exc.msg - except PresetFileError as exc: - raiseAssert exc.msg - runNodeConf = try: BeaconNodeConf.load(cmdLine = mapIt([ "--tcp-port=" & $(basePort + PortKind.PeerToPeer.ord), "--udp-port=" & $(basePort + PortKind.PeerToPeer.ord), @@ -376,7 +368,7 @@ proc initBeaconNode(basePort: int): Future[BeaconNode] {.async: (raises: []).} = try: let taskpool = Taskpool.new() - await BeaconNode.init(rng, runNodeConf, metadata, taskpool) + await BeaconNode.init(rng, runNodeConf, taskpool) except CatchableError as exc: raiseAssert exc.msg From cd942def3f0511ccd14f731792ea48211508ac89 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Mon, 27 Oct 2025 15:26:58 +0100 Subject: [PATCH 3/4] more tests --- beacon_chain/sync/sync_manager.nim | 2 +- tests/test_keymanager_api.nim | 58 +++++++++++++++--------------- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/beacon_chain/sync/sync_manager.nim b/beacon_chain/sync/sync_manager.nim index a57118cdfb..600b5b35a3 100644 --- a/beacon_chain/sync/sync_manager.nim +++ b/beacon_chain/sync/sync_manager.nim @@ -580,7 +580,7 @@ proc syncStep[A, B]( if man.remainingSlots() <= man.maxHeadAge: case man.direction of SyncQueueKind.Forward: - info "We are in sync with network", + info "Beacon node in sync with consensus network", peer = peer, peer_score = peer.getScore(), peer_speed = peer.netKbps(), diff --git a/tests/test_keymanager_api.nim b/tests/test_keymanager_api.nim index 09d66e335f..b8415ab440 100644 --- a/tests/test_keymanager_api.nim +++ b/tests/test_keymanager_api.nim @@ -342,35 +342,37 @@ proc initBeaconNode(basePort: int): Future[BeaconNode] {.async: (raises: []).} = raiseAssert exc.msg let - runNodeConf = try: BeaconNodeConf.load(cmdLine = mapIt([ - "--tcp-port=" & $(basePort + PortKind.PeerToPeer.ord), - "--udp-port=" & $(basePort + PortKind.PeerToPeer.ord), - "--discv5=off", - "--network=" & dataDir, - "--data-dir=" & nodeDataDir, - "--validators-dir=" & nodeValidatorsDir, - "--secrets-dir=" & nodeSecretsDir, - "--metrics-address=127.0.0.1", - "--metrics-port=" & $(basePort + PortKind.Metrics.ord), - "--rest=true", - "--rest-address=127.0.0.1", - "--rest-port=" & $(basePort + PortKind.KeymanagerBN.ord), - "--no-el", - "--keymanager=true", - "--keymanager-address=127.0.0.1", - "--keymanager-port=" & $(basePort + PortKind.KeymanagerBN.ord), - "--keymanager-token-file=" & tokenFilePath, - "--suggested-fee-recipient=" & $defaultFeeRecipient, - "--doppelganger-detection=off", - "--sync-horizon=" & $(metadata.cfg.timeParams.defaultSyncHorizon)], it)) - except Exception as exc: # TODO fix confutils exceptions - raiseAssert exc.msg + runNodeConf = + try: + BeaconNodeConf.load( + cmdLine = + @[ + "--tcp-port=" & $(basePort + PortKind.PeerToPeer.ord), + "--udp-port=" & $(basePort + PortKind.PeerToPeer.ord), + "--discv5=off", + "--network=" & dataDir, + "--data-dir=" & nodeDataDir, + "--validators-dir=" & nodeValidatorsDir, + "--secrets-dir=" & nodeSecretsDir, + "--metrics-address=127.0.0.1", + "--metrics-port=" & $(basePort + PortKind.Metrics.ord), + "--rest=true", + "--rest-address=127.0.0.1", + "--rest-port=" & $(basePort + PortKind.KeymanagerBN.ord), + "--no-el", + "--keymanager=true", + "--keymanager-address=127.0.0.1", + "--keymanager-port=" & $(basePort + PortKind.KeymanagerBN.ord), + "--keymanager-token-file=" & tokenFilePath, + "--suggested-fee-recipient=" & $defaultFeeRecipient, + "--doppelganger-detection=off", + ] + ) + except CatchableError as exc: + raiseAssert exc.msg - try: - let taskpool = Taskpool.new() - await BeaconNode.init(rng, runNodeConf, taskpool) - except CatchableError as exc: - raiseAssert exc.msg + taskpool = Taskpool.new() + (await BeaconNode.init(rng, runNodeConf, taskpool)).expect("working node") # proc startValidatorClient(basePort: int) {.async, thread.} = # let rng = HmacDrbgContext.new() From aeb3b67ebc05e19629db45612cdc096af356de63 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Mon, 27 Oct 2025 16:30:57 +0100 Subject: [PATCH 4/4] eh --- tests/test_keymanager_api.nim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_keymanager_api.nim b/tests/test_keymanager_api.nim index b8415ab440..6548aa2f22 100644 --- a/tests/test_keymanager_api.nim +++ b/tests/test_keymanager_api.nim @@ -371,8 +371,12 @@ proc initBeaconNode(basePort: int): Future[BeaconNode] {.async: (raises: []).} = except CatchableError as exc: raiseAssert exc.msg - taskpool = Taskpool.new() - (await BeaconNode.init(rng, runNodeConf, taskpool)).expect("working node") + taskpool = + try: + Taskpool.new() + except CatchableError as exc: + raiseAssert exc.msg + (await noCancel BeaconNode.init(rng, runNodeConf, taskpool)).expect("working node") # proc startValidatorClient(basePort: int) {.async, thread.} = # let rng = HmacDrbgContext.new()