Require properly configured Engine API connection after the merge (#4006)

This commit is contained in:
zah 2022-08-22 22:44:40 +03:00 committed by GitHub
parent f1ddcfff0f
commit 4e41ed1d5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 75 additions and 32 deletions

View File

@ -172,6 +172,11 @@ type
desc: "Force the use of polling when determining the head block of Eth1" desc: "Force the use of polling when determining the head block of Eth1"
name: "web3-force-polling" .}: bool 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* {. nonInteractive* {.
desc: "Do not display interative prompts. Quit on missing configuration" desc: "Do not display interative prompts. Quit on missing configuration"
name: "non-interactive" .}: bool name: "non-interactive" .}: bool

View File

@ -141,6 +141,8 @@ type
stopFut: Future[void] stopFut: Future[void]
getBeaconTime: GetBeaconTimeFn getBeaconTime: GetBeaconTimeFn
requireEngineAPI: bool
when hasGenesisDetection: when hasGenesisDetection:
genesisValidators: seq[ImmutableValidatorData] genesisValidators: seq[ImmutableValidatorData]
genesisValidatorKeyToIndex: Table[ValidatorPubKey, ValidatorIndex] genesisValidatorKeyToIndex: Table[ValidatorPubKey, ValidatorIndex]
@ -548,53 +550,67 @@ proc forkchoiceUpdated*(p: Eth1Monitor,
# TODO can't be defined within exchangeTransitionConfiguration # TODO can't be defined within exchangeTransitionConfiguration
proc `==`(x, y: Quantity): bool {.borrow, noSideEffect.} 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, # Eth1 monitor can recycle connections without (external) warning; at least,
# don't crash. # don't crash.
if p.isNil: if p.isNil:
debug "exchangeTransitionConfiguration: nil Eth1Monitor" debug "exchangeTransitionConfiguration: nil Eth1Monitor"
if p.isNil or p.dataProvider.isNil: if p.isNil or p.dataProvider.isNil:
return return EtcStatus.exchangeError
let ccTransitionConfiguration = TransitionConfigurationV1( let consensusCfg = TransitionConfigurationV1(
terminalTotalDifficulty: p.depositsChain.cfg.TERMINAL_TOTAL_DIFFICULTY, terminalTotalDifficulty: p.depositsChain.cfg.TERMINAL_TOTAL_DIFFICULTY,
terminalBlockHash: terminalBlockHash:
if p.terminalBlockHash.isSome: if p.terminalBlockHash.isSome:
p.terminalBlockHash.get p.terminalBlockHash.get
else: else:
# https://github.com/nim-lang/Nim/issues/19802 (static default BlockHash),
(static(default(BlockHash))),
terminalBlockNumber: terminalBlockNumber:
if p.terminalBlockNumber.isSome: if p.terminalBlockNumber.isSome:
p.terminalBlockNumber.get p.terminalBlockNumber.get
else: else:
# https://github.com/nim-lang/Nim/issues/19802 (static default Quantity))
(static(default(Quantity)))) let executionCfg =
let ecTransitionConfiguration =
try: try:
awaitWithRetries( awaitWithRetries(
p.dataProvider.web3.provider.engine_exchangeTransitionConfigurationV1( p.dataProvider.web3.provider.engine_exchangeTransitionConfigurationV1(
ccTransitionConfiguration), consensusCfg),
timeout = 1.seconds) timeout = 1.seconds)
except CatchableError as err: except CatchableError as err:
debug "Failed to exchange transition configuration", err = err.msg error "Failed to exchange transition configuration", err = err.msg
return return EtcStatus.exchangeError
if ccTransitionConfiguration != ecTransitionConfiguration: if consensusCfg.terminalTotalDifficulty != executionCfg.terminalTotalDifficulty:
warn "exchangeTransitionConfiguration: Configuration mismatch detected", warn "Engine API configured with different terminal total difficulty",
consensusTerminalTotalDifficulty = engineAPI_value = executionCfg.terminalTotalDifficulty,
$ccTransitionConfiguration.terminalTotalDifficulty, localValue = consensusCfg.terminalTotalDifficulty
consensusTerminalBlockHash = return EtcStatus.mismatch
ccTransitionConfiguration.terminalBlockHash,
consensusTerminalBlockNumber = if p.terminalBlockNumber.isSome and p.terminalBlockHash.isSome:
ccTransitionConfiguration.terminalBlockNumber.uint64, var res = EtcStatus.match
executionTerminalTotalDifficulty = if consensusCfg.terminalBlockNumber != executionCfg.terminalBlockNumber:
$ecTransitionConfiguration.terminalTotalDifficulty, warn "Engine API reporting different terminal block number",
executionTerminalBlockHash = engineAPI_value = executionCfg.terminalBlockNumber.uint64,
ecTransitionConfiguration.terminalBlockHash, localValue = consensusCfg.terminalBlockNumber.uint64
executionTerminalBlockNumber = res = EtcStatus.mismatch
ecTransitionConfiguration.terminalBlockNumber.uint64 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 = template readJsonField(j: JsonNode, fieldName: string, ValueType: type): untyped =
var res: ValueType var res: ValueType
@ -1037,7 +1053,8 @@ proc init*(T: type Eth1Monitor,
depositContractSnapshot: Option[DepositContractSnapshot], depositContractSnapshot: Option[DepositContractSnapshot],
eth1Network: Option[Eth1Network], eth1Network: Option[Eth1Network],
forcePolling: bool, forcePolling: bool,
jwtSecret: Option[seq[byte]]): T = jwtSecret: Option[seq[byte]],
requireEngineAPI: bool): T =
doAssert web3Urls.len > 0 doAssert web3Urls.len > 0
var web3Urls = web3Urls var web3Urls = web3Urls
for url in mitems(web3Urls): for url in mitems(web3Urls):
@ -1055,7 +1072,8 @@ proc init*(T: type Eth1Monitor,
eth1Progress: newAsyncEvent(), eth1Progress: newAsyncEvent(),
forcePolling: forcePolling, forcePolling: forcePolling,
jwtSecret: jwtSecret, jwtSecret: jwtSecret,
blocksPerLogsRequest: targetBlocksPerLogsRequest) blocksPerLogsRequest: targetBlocksPerLogsRequest,
requireEngineAPI: requireEngineAPI)
proc safeCancel(fut: var Future[void]) = proc safeCancel(fut: var Future[void]) =
if not fut.isNil and not fut.finished: if not fut.isNil and not fut.finished:
@ -1340,6 +1358,20 @@ proc startEth1Syncing(m: Eth1Monitor, delayBeforeStart: Duration) {.async.} =
await m.ensureDataProvider() 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 # We might need to reset the chain if the new provider disagrees
# with the previous one regarding the history of the chain or if # with the previous one regarding the history of the chain or if
# we have detected a conensus violation - our view disagreeing with # we have detected a conensus violation - our view disagreeing with

View File

@ -538,7 +538,8 @@ proc init*(T: type BeaconNode,
getDepositContractSnapshot(), getDepositContractSnapshot(),
eth1Network, eth1Network,
config.web3ForcePolling, config.web3ForcePolling,
optJwtSecret) optJwtSecret,
config.requireEngineAPI)
eth1Monitor.loadPersistedDeposits() eth1Monitor.loadPersistedDeposits()
@ -639,7 +640,8 @@ proc init*(T: type BeaconNode,
getDepositContractSnapshot(), getDepositContractSnapshot(),
eth1Network, eth1Network,
config.web3ForcePolling, config.web3ForcePolling,
optJwtSecret) optJwtSecret,
config.requireEngineAPI)
if config.rpcEnabled: 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." 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."

View File

@ -61,7 +61,8 @@ programMain:
cfg, db = nil, getBeaconTime, config.web3Urls, cfg, db = nil, getBeaconTime, config.web3Urls,
none(DepositContractSnapshot), metadata.eth1Network, none(DepositContractSnapshot), metadata.eth1Network,
forcePolling = false, forcePolling = false,
rng[].loadJwtSecret(config, allowCreate = false)) rng[].loadJwtSecret(config, allowCreate = false),
true)
waitFor res.ensureDataProvider() waitFor res.ensureDataProvider()
res res
else: else:

View File

@ -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. 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 ### 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). 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).

View File

@ -59,7 +59,7 @@ proc run() {.async.} =
eth1Monitor = Eth1Monitor.init( eth1Monitor = Eth1Monitor.init(
defaultRuntimeConfig, db = nil, nil, @[paramStr(1)], defaultRuntimeConfig, db = nil, nil, @[paramStr(1)],
none(DepositContractSnapshot), none(Eth1Network), false, none(DepositContractSnapshot), none(Eth1Network), false,
some readJwtSecret(paramStr(2)).get) some readJwtSecret(paramStr(2)).get, true)
await eth1Monitor.ensureDataProvider() await eth1Monitor.ensureDataProvider()
try: try:

View File

@ -60,7 +60,8 @@ proc run() {.async.} =
jwtSecret = some readJwtSecret("jwt.hex").get jwtSecret = some readJwtSecret("jwt.hex").get
eth1Monitor = Eth1Monitor.init( eth1Monitor = Eth1Monitor.init(
defaultRuntimeConfig, db = nil, nil, @[web3Url], defaultRuntimeConfig, db = nil, nil, @[web3Url],
none(DepositContractSnapshot), none(Eth1Network), false, jwtSecret) none(DepositContractSnapshot), none(Eth1Network),
false, jwtSecret, true)
web3Provider = (await Web3DataProvider.new( web3Provider = (await Web3DataProvider.new(
default(Eth1Address), web3Url, jwtSecret)).get default(Eth1Address), web3Url, jwtSecret)).get