diff --git a/beacon_chain/beacon_node.nim b/beacon_chain/beacon_node.nim index 8c6804f63..9f7f31e8a 100644 --- a/beacon_chain/beacon_node.nim +++ b/beacon_chain/beacon_node.nim @@ -26,7 +26,7 @@ import spec/state_transition, conf, time, beacon_chain_db, validator_pool, extras, attestation_pool, exit_pool, eth2_network, eth2_discovery, - beacon_node_common, beacon_node_types, + beacon_node_common, beacon_node_types, beacon_node_status, block_pools/[spec_cache, chain_dag, quarantine, clearance, block_pools_types], nimbus_binary_common, network_metadata, mainchain_monitor, version, ssz/[merkleization], merkle_minimal, @@ -42,13 +42,6 @@ const type RpcServer* = RpcHttpServer - # "state" is already taken by BeaconState - BeaconNodeStatus* = enum - Starting, Running, Stopping - -# this needs to be global, so it can be set in the Ctrl+C signal handler -var status = BeaconNodeStatus.Starting - template init(T: type RpcHttpServer, ip: ValidIpAddress, port: Port): T = newRpcHttpServer([initTAddress(ip, port)]) @@ -190,6 +183,8 @@ proc init*(T: type BeaconNode, mainchainMonitor.start() genesisState = await mainchainMonitor.waitGenesis() + if bnStatus == BeaconNodeStatus.Stopping: + return nil info "Eth2 genesis state detected", genesisTime = genesisState.genesisTime, @@ -849,7 +844,7 @@ proc installMessageValidators(node: BeaconNode) = node.processor[].voluntaryExitValidator(signedVoluntaryExit)) proc stop*(node: BeaconNode) = - status = BeaconNodeStatus.Stopping + bnStatus = BeaconNodeStatus.Stopping info "Graceful shutdown" if not node.config.inProcessValidators: node.vcProcess.close() @@ -858,9 +853,9 @@ proc stop*(node: BeaconNode) = info "Database closed" proc run*(node: BeaconNode) = - if status == BeaconNodeStatus.Starting: + if bnStatus == BeaconNodeStatus.Starting: # it might have been set to "Stopping" with Ctrl+C - status = BeaconNodeStatus.Running + bnStatus = BeaconNodeStatus.Running if node.rpcServer != nil: node.rpcServer.installRpcHandlers(node) @@ -888,7 +883,7 @@ proc run*(node: BeaconNode) = node.startSyncManager() # main event loop - while status == BeaconNodeStatus.Running: + while bnStatus == BeaconNodeStatus.Running: try: poll() except CatchableError as e: @@ -1208,7 +1203,7 @@ programMain: # workaround for https://github.com/nim-lang/Nim/issues/4057 setupForeignThreadGc() info "Shutting down after having received SIGINT" - status = BeaconNodeStatus.Stopping + bnStatus = BeaconNodeStatus.Stopping setControlCHook(controlCHandler) when useInsecureFeatures: @@ -1219,6 +1214,8 @@ programMain: metrics.startHttpServer($metricsAddress, config.metricsPort) var node = waitFor BeaconNode.init(rng, config, stateSnapshotContents) + if bnStatus == BeaconNodeStatus.Stopping: + return when hasPrompt: initPrompt(node) diff --git a/beacon_chain/beacon_node_status.nim b/beacon_chain/beacon_node_status.nim new file mode 100644 index 000000000..ec95b55bc --- /dev/null +++ b/beacon_chain/beacon_node_status.nim @@ -0,0 +1,16 @@ +# beacon_chain +# Copyright (c) 2018-2020 Status Research & Development GmbH +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +type + # "state" is already taken by BeaconState + BeaconNodeStatus* = enum + Starting + Running + Stopping + +# this needs to be global, so it can be set in the Ctrl+C signal handler +var bnStatus* = BeaconNodeStatus.Starting diff --git a/beacon_chain/mainchain_monitor.nim b/beacon_chain/mainchain_monitor.nim index 0b8fcc474..3ff80d95e 100644 --- a/beacon_chain/mainchain_monitor.nim +++ b/beacon_chain/mainchain_monitor.nim @@ -3,7 +3,8 @@ import chronos, web3, web3/ethtypes as web3Types, json, chronicles, eth/common/eth_types, eth/async_utils, spec/[datatypes, digest, crypto, beaconstate, helpers, validator], - network_metadata, merkle_minimal + network_metadata, merkle_minimal, + beacon_node_status from times import epochTime @@ -564,8 +565,27 @@ proc findGenesisBlockInRange(m: MainchainMonitor, return endBlock +proc safeCancel(fut: var Future[void]) = + if not fut.isNil and not fut.finished: + fut.cancel() + fut = nil + +proc stop*(m: MainchainMonitor) = + safeCancel m.runFut + safeCancel m.genesisMonitoringFut + +template checkIfShouldStopMainchainMonitor(m: MainchainMonitor) = + if bnStatus == BeaconNodeStatus.Stopping: + if not m.genesisStateFut.isNil: + m.genesisStateFut.complete() + m.genesisStateFut = nil + m.stop + return + proc checkForGenesisLoop(m: MainchainMonitor) {.async.} = while true: + m.checkIfShouldStopMainchainMonitor() + if not m.genesisState.isNil: return @@ -636,6 +656,9 @@ proc waitGenesis*(m: MainchainMonitor): Future[BeaconStateRef] {.async.} = await m.genesisStateFut m.genesisStateFut = nil + if bnStatus == BeaconNodeStatus.Stopping: + return new BeaconStateRef # cannot return nil... + if m.genesisState != nil: return m.genesisState else: @@ -677,6 +700,8 @@ proc processDeposits(m: MainchainMonitor, # it could easily re-order the steps due to the intruptable # interleaved execution of async code. while true: + m.checkIfShouldStopMainchainMonitor() + let blk = await m.depositQueue.popFirst() m.eth1Chain.trimHeight(Eth1BlockNumber(blk.number) - 1) @@ -776,15 +801,6 @@ proc run(m: MainchainMonitor, delayBeforeStart: Duration) {.async.} = finally: await close(dataProvider) -proc safeCancel(fut: var Future[void]) = - if not fut.isNil and not fut.finished: - fut.cancel() - fut = nil - -proc stop*(m: MainchainMonitor) = - safeCancel m.runFut - safeCancel m.genesisMonitoringFut - proc start(m: MainchainMonitor, delayBeforeStart: Duration) = if m.runFut.isNil: let runFut = m.run(delayBeforeStart)