From 802222f10517be993692d5ad915b5eb0ad71d302 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 20 Aug 2022 09:17:17 +0300 Subject: [PATCH] Require properly configured Engine API connection after the merge --- beacon_chain/conf.nim | 5 ++ beacon_chain/eth1/eth1_monitor.nim | 88 +++++++++++++++++++--------- beacon_chain/nimbus_beacon_node.nim | 6 +- beacon_chain/nimbus_light_client.nim | 3 +- docs/the_nimbus_book/src/merge.md | 2 + scripts/test_merge_node.nim | 2 +- scripts/test_merge_vectors.nim | 3 +- 7 files changed, 76 insertions(+), 33 deletions(-) diff --git a/beacon_chain/conf.nim b/beacon_chain/conf.nim index 2817d22457..28f310e29d 100644 --- a/beacon_chain/conf.nim +++ b/beacon_chain/conf.nim @@ -172,6 +172,11 @@ type desc: "Force the use of polling when determining the head block of Eth1" name: "web3-force-polling" .}: bool + requireEngineAPI* {. + defaultValue: true + desc: "Require Nimbus to be configured with an Engine API end-point after the Bellatrix fork epoch" + name: "require-engine-api-in-bellatrix" .}: bool + nonInteractive* {. desc: "Do not display interative prompts. Quit on missing configuration" name: "non-interactive" .}: bool diff --git a/beacon_chain/eth1/eth1_monitor.nim b/beacon_chain/eth1/eth1_monitor.nim index a43b369624..7258a97832 100644 --- a/beacon_chain/eth1/eth1_monitor.nim +++ b/beacon_chain/eth1/eth1_monitor.nim @@ -141,6 +141,8 @@ type stopFut: Future[void] getBeaconTime: GetBeaconTimeFn + requireEngineAPI: bool + when hasGenesisDetection: genesisValidators: seq[ImmutableValidatorData] genesisValidatorKeyToIndex: Table[ValidatorPubKey, ValidatorIndex] @@ -548,53 +550,67 @@ proc forkchoiceUpdated*(p: Eth1Monitor, # TODO can't be defined within exchangeTransitionConfiguration proc `==`(x, y: Quantity): bool {.borrow, noSideEffect.} -proc exchangeTransitionConfiguration*(p: Eth1Monitor): Future[void] {.async.} = +type + EtcStatus {.pure.} = enum + exchangeError + mismatch + localConfigurationUpdated + match + +proc exchangeTransitionConfiguration*(p: Eth1Monitor): Future[EtcStatus] {.async.} = # Eth1 monitor can recycle connections without (external) warning; at least, # don't crash. if p.isNil: debug "exchangeTransitionConfiguration: nil Eth1Monitor" if p.isNil or p.dataProvider.isNil: - return + return EtcStatus.exchangeError - let ccTransitionConfiguration = TransitionConfigurationV1( + let consensusCfg = TransitionConfigurationV1( terminalTotalDifficulty: p.depositsChain.cfg.TERMINAL_TOTAL_DIFFICULTY, terminalBlockHash: if p.terminalBlockHash.isSome: p.terminalBlockHash.get else: - # https://github.com/nim-lang/Nim/issues/19802 - (static(default(BlockHash))), + (static default BlockHash), terminalBlockNumber: if p.terminalBlockNumber.isSome: p.terminalBlockNumber.get else: - # https://github.com/nim-lang/Nim/issues/19802 - (static(default(Quantity)))) - let ecTransitionConfiguration = + (static default Quantity)) + let executionCfg = try: awaitWithRetries( p.dataProvider.web3.provider.engine_exchangeTransitionConfigurationV1( - ccTransitionConfiguration), + consensusCfg), timeout = 1.seconds) except CatchableError as err: - debug "Failed to exchange transition configuration", err = err.msg - return - - if ccTransitionConfiguration != ecTransitionConfiguration: - warn "exchangeTransitionConfiguration: Configuration mismatch detected", - consensusTerminalTotalDifficulty = - $ccTransitionConfiguration.terminalTotalDifficulty, - consensusTerminalBlockHash = - ccTransitionConfiguration.terminalBlockHash, - consensusTerminalBlockNumber = - ccTransitionConfiguration.terminalBlockNumber.uint64, - executionTerminalTotalDifficulty = - $ecTransitionConfiguration.terminalTotalDifficulty, - executionTerminalBlockHash = - ecTransitionConfiguration.terminalBlockHash, - executionTerminalBlockNumber = - ecTransitionConfiguration.terminalBlockNumber.uint64 + error "Failed to exchange transition configuration", err = err.msg + return EtcStatus.exchangeError + + if consensusCfg.terminalTotalDifficulty != executionCfg.terminalTotalDifficulty: + warn "Engine API configured with different terminal total difficulty", + engineAPI_value = executionCfg.terminalTotalDifficulty, + localValue = consensusCfg.terminalTotalDifficulty + return EtcStatus.mismatch + + if p.terminalBlockNumber.isSome and p.terminalBlockHash.isSome: + var res = EtcStatus.match + if consensusCfg.terminalBlockNumber != executionCfg.terminalBlockNumber: + warn "Engine API reporting different terminal block number", + engineAPI_value = executionCfg.terminalBlockNumber.uint64, + localValue = consensusCfg.terminalBlockNumber.uint64 + res = EtcStatus.mismatch + if consensusCfg.terminalBlockHash != executionCfg.terminalBlockHash: + warn "Engine API reporting different terminal block hash", + engineAPI_value = executionCfg.terminalBlockHash, + localValue = consensusCfg.terminalBlockHash + res = EtcStatus.mismatch + return res + else: + p.terminalBlockNumber = some executionCfg.terminalBlockNumber + p.terminalBlockHash = some executionCfg.terminalBlockHash + return EtcStatus.localConfigurationUpdated template readJsonField(j: JsonNode, fieldName: string, ValueType: type): untyped = var res: ValueType @@ -1037,7 +1053,8 @@ proc init*(T: type Eth1Monitor, depositContractSnapshot: Option[DepositContractSnapshot], eth1Network: Option[Eth1Network], forcePolling: bool, - jwtSecret: Option[seq[byte]]): T = + jwtSecret: Option[seq[byte]], + requireEngineAPI: bool): T = doAssert web3Urls.len > 0 var web3Urls = web3Urls for url in mitems(web3Urls): @@ -1055,7 +1072,8 @@ proc init*(T: type Eth1Monitor, eth1Progress: newAsyncEvent(), forcePolling: forcePolling, jwtSecret: jwtSecret, - blocksPerLogsRequest: targetBlocksPerLogsRequest) + blocksPerLogsRequest: targetBlocksPerLogsRequest, + requireEngineAPI: requireEngineAPI) proc safeCancel(fut: var Future[void]) = if not fut.isNil and not fut.finished: @@ -1340,6 +1358,20 @@ proc startEth1Syncing(m: Eth1Monitor, delayBeforeStart: Duration) {.async.} = await m.ensureDataProvider() + if m.currentEpoch >= m.cfg.BELLATRIX_FORK_EPOCH: + let status = await m.exchangeTransitionConfiguration() + if status == EtcStatus.localConfigurationUpdated: + info "Obtained terminal block from Engine API", + terminalBlockNumber = m.terminalBlockNumber.get.uint64, + terminalBlockHash = m.terminalBlockHash.get + elif status != EtcStatus.match and isFirstRun and m.requireEngineAPI: + fatal "The Bellatrix hard fork requires the beacon node to be connected to a properly configured Engine API end-point. " & + "See https://nimbus.guide/merge.html for more details. " & + "If you want to temporarily continue operating Nimbus without configuring an Engine API end-point, " & + "please specify the command-line option --require-engine-api-in-bellatrix=no when launching it. " & + "Please note that you MUST complete the migration before the network TTD is reached (estimated to happen near 13th of September)" + quit 1 + # We might need to reset the chain if the new provider disagrees # with the previous one regarding the history of the chain or if # we have detected a conensus violation - our view disagreeing with diff --git a/beacon_chain/nimbus_beacon_node.nim b/beacon_chain/nimbus_beacon_node.nim index b1a88cb701..6f17d9f8ba 100644 --- a/beacon_chain/nimbus_beacon_node.nim +++ b/beacon_chain/nimbus_beacon_node.nim @@ -538,7 +538,8 @@ proc init*(T: type BeaconNode, getDepositContractSnapshot(), eth1Network, config.web3ForcePolling, - optJwtSecret) + optJwtSecret, + config.requireEngineAPI) eth1Monitor.loadPersistedDeposits() @@ -639,7 +640,8 @@ proc init*(T: type BeaconNode, getDepositContractSnapshot(), eth1Network, config.web3ForcePolling, - optJwtSecret) + optJwtSecret, + config.requireEngineAPI) if config.rpcEnabled: 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." diff --git a/beacon_chain/nimbus_light_client.nim b/beacon_chain/nimbus_light_client.nim index 86dcb404eb..9ad6545822 100644 --- a/beacon_chain/nimbus_light_client.nim +++ b/beacon_chain/nimbus_light_client.nim @@ -61,7 +61,8 @@ programMain: cfg, db = nil, getBeaconTime, config.web3Urls, none(DepositContractSnapshot), metadata.eth1Network, forcePolling = false, - rng[].loadJwtSecret(config, allowCreate = false)) + rng[].loadJwtSecret(config, allowCreate = false), + true) waitFor res.ensureDataProvider() res else: diff --git a/docs/the_nimbus_book/src/merge.md b/docs/the_nimbus_book/src/merge.md index 827032781c..b16a2449f2 100644 --- a/docs/the_nimbus_book/src/merge.md +++ b/docs/the_nimbus_book/src/merge.md @@ -22,6 +22,8 @@ As a node operator, you will need to run both an execution client and a consensu If you were running an execution client before, make sure to update its configuration to include an option for [JWT secrets](./eth1.md#3-pass-the-jwt-secret-to-nimbus) and engine API. +Please note that once the Bellatrix fork epoch is reached on 6th of September 2022, Nimbus will refuse to start unless connected to a properly configured execution client. If you need more time to complete the transition, you can temporarily run the beacon node with the command-line option `--require-engine-api-in-bellatrix=no`, but please note that such a setup will stop working once the network TTD is reached (currently estimated to happen on 13th of September, see https://wenmerge.com/ for more up-to-date information). + ### Prepare a suggested fee recipient After the merge, validators that propose blocks are eligible to recieve transaction fees - read more about fee recipients [here](https://launchpad.ethereum.org/en/merge-readiness#fee-recipient). diff --git a/scripts/test_merge_node.nim b/scripts/test_merge_node.nim index 16f1992297..a4850f2157 100644 --- a/scripts/test_merge_node.nim +++ b/scripts/test_merge_node.nim @@ -59,7 +59,7 @@ proc run() {.async.} = eth1Monitor = Eth1Monitor.init( defaultRuntimeConfig, db = nil, nil, @[paramStr(1)], none(DepositContractSnapshot), none(Eth1Network), false, - some readJwtSecret(paramStr(2)).get) + some readJwtSecret(paramStr(2)).get, true) await eth1Monitor.ensureDataProvider() try: diff --git a/scripts/test_merge_vectors.nim b/scripts/test_merge_vectors.nim index 70ee2dd139..1db8ca550a 100644 --- a/scripts/test_merge_vectors.nim +++ b/scripts/test_merge_vectors.nim @@ -60,7 +60,8 @@ proc run() {.async.} = jwtSecret = some readJwtSecret("jwt.hex").get eth1Monitor = Eth1Monitor.init( defaultRuntimeConfig, db = nil, nil, @[web3Url], - none(DepositContractSnapshot), none(Eth1Network), false, jwtSecret) + none(DepositContractSnapshot), none(Eth1Network), + false, jwtSecret, true) web3Provider = (await Web3DataProvider.new( default(Eth1Address), web3Url, jwtSecret)).get