Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 86 additions & 13 deletions execution_chain/nimbus.nim
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import
el_sync,
nimbus_desc,
nimbus_execution_client,
nimbus_light_client,
version_info,
]

Expand Down Expand Up @@ -148,6 +149,11 @@ type
defaultValue: true
name: "el-sync" .}: bool

light* {.
desc: "Run a light node: pair the execution client with the consensus light client instead of a full beacon node (requires --trusted-block-root)"
defaultValue: false
name: "light" .}: bool

# detect if user added --engine-api option which is not valid in unified mode
engineApiEnabled* {.
hidden
Expand Down Expand Up @@ -184,6 +190,11 @@ type
tcpPort: Port
udpPort: Option[Port]

LightThreadConfig = object
tsp: ThreadSignalPtr
tcpPort: Port
udpPort: Port

var jwtKey: JwtSharedKey

proc dataDir*(config: NimbusConf): string =
Expand Down Expand Up @@ -263,6 +274,30 @@ proc runBeaconNode(p: BeaconThreadConfig) {.thread.} =
# Stop the other thread as well, in case we're stopping early
waitFor p.tsp.fire()

proc runLightClientThread(p: LightThreadConfig) {.thread.} =
var config = LightClientConf.loadWithBanners(clientId, copyright, [specBanner], true).valueOr:
stderr.writeLine error # Logging not yet set up
quit QuitFailure

let engineUrl =
EngineApiUrl.init(&"http://127.0.0.1:{defaultEngineApiPort}/", Opt.some(jwtKey))

config.metricsEnabled = false
config.elUrls.add EngineApiUrlConfigValue(
url: engineUrl.url, jwtSecret: some toHex(distinctBase(jwtKey))
)
config.tcpPort = p.tcpPort
config.udpPort = p.udpPort

info "Launching light client",
version = fullVersionStr, cmdParams = commandLineParams(), config

dynamicLogScope(comp = "lc"):
runLightClient(config, p.tsp.justWait())

# Stop the other thread as well, in case the light client stopped early
waitFor p.tsp.fire()

proc runExecutionClient(p: ExecutionThreadConfig) {.thread.} =
var config = makeConfig(ignoreUnknown = true)
config.metricsEnabled = false
Expand Down Expand Up @@ -344,18 +379,42 @@ proc runCombinedClient() =
"Baked-in KZG setup is correct"
)

var bnThread: Thread[BeaconThreadConfig]
let bnStop = ThreadSignalPtr.new().expect("working ThreadSignalPtr")
createThread(
bnThread,
runBeaconNode,
BeaconThreadConfig(
tsp: bnStop,
tcpPort: config.beaconTcpPort.get(config.tcpPort.get(Port defaultEth2TcpPort)),
udpPort: config.beaconUdpPort.get(config.udpPort.get(Port defaultEth2TcpPort)),
elSync: config.elSync,
),
)
# Consensus side: either a full beacon node or, with `--light`, the consensus
# light client. Both are stopped via `bnStop` and drive the EL over the
# internal loopback Engine API.
var
bnThread: Thread[BeaconThreadConfig]
lightThread: Thread[LightThreadConfig]
let
bnStop = ThreadSignalPtr.new().expect("working ThreadSignalPtr")
beaconTcpPort =
config.beaconTcpPort.get(config.tcpPort.get(Port defaultEth2TcpPort))
beaconUdpPort =
config.beaconUdpPort.get(config.udpPort.get(Port defaultEth2TcpPort))
if config.light:
# The light client drives the EL directly via the Engine API, so the
# beacon-node CL-driven EL sync loop (`elSyncLoop`/`--el-sync`) must not run.
config.elSync = false
createThread(
lightThread,
runLightClientThread,
LightThreadConfig(
tsp: bnStop,
tcpPort: beaconTcpPort,
udpPort: beaconUdpPort,
),
)
else:
createThread(
bnThread,
runBeaconNode,
BeaconThreadConfig(
tsp: bnStop,
tcpPort: beaconTcpPort,
udpPort: beaconUdpPort,
elSync: config.elSync,
),
)

var ecThread: Thread[ExecutionThreadConfig]
let ecStop = ThreadSignalPtr.new().expect("working ThreadSignalPtr")
Expand All @@ -379,13 +438,27 @@ proc runCombinedClient() =
),
)

# Shut the whole process down if either worker thread exits on its own (e.g. an
# unrecoverable startup error). Without this the main thread would keep polling
# `ProcessState` forever while the surviving thread - or nothing - runs on.
proc workerExited(): bool =
let consensusStopped =
if config.light: not lightThread.running else: not bnThread.running
consensusStopped or not ecThread.running

while not ProcessState.stopIt(notice("Shutting down", reason = it)):
if workerExited():
notice "A worker thread stopped, shutting down"
break
os.sleep(100)

waitFor bnStop.fire()
waitFor ecStop.fire()

joinThread(bnThread)
if config.light:
joinThread(lightThread)
else:
joinThread(bnThread)
joinThread(ecThread)

waitFor metricsServer.stopMetricsServer()
Expand Down
Loading
Loading