Bellatrix TTD detection (#3745)
* Bellatrix TTD detection * Update beacon_chain/eth1/eth1_monitor.nim Co-authored-by: Etan Kissling <etan@status.im> * Update beacon_chain/nimbus_beacon_node.nim Co-authored-by: tersec <tersec@users.noreply.github.com> Co-authored-by: Etan Kissling <etan@status.im> Co-authored-by: tersec <tersec@users.noreply.github.com>
This commit is contained in:
parent
20e646a47f
commit
694b653757
|
@ -73,6 +73,7 @@ type
|
|||
restKeysCache*: Table[ValidatorPubKey, ValidatorIndex]
|
||||
validatorMonitor*: ref ValidatorMonitor
|
||||
stateTtlCache*: StateTtlCache
|
||||
nextExchangeTransitionConfTime*: Moment
|
||||
|
||||
const
|
||||
MaxEmptySlotCount* = uint64(10*60) div SECONDS_PER_SLOT
|
||||
|
|
|
@ -118,6 +118,9 @@ type
|
|||
depositsChain: Eth1Chain
|
||||
eth1Progress: AsyncEvent
|
||||
|
||||
terminalBlockHash*: Option[BlockHash]
|
||||
terminalBlockNumber*: Option[Quantity]
|
||||
|
||||
runFut: Future[void]
|
||||
stopFut: Future[void]
|
||||
getBeaconTime: GetBeaconTimeFn
|
||||
|
@ -517,6 +520,50 @@ proc forkchoiceUpdated*(p: Eth1Monitor,
|
|||
prevRandao: FixedBytes[32] randomData,
|
||||
suggestedFeeRecipient: suggestedFeeRecipient)))
|
||||
|
||||
# TODO can't be defined within exchangeTransitionConfiguration
|
||||
proc `==`(x, y: Quantity): bool {.borrow, noSideEffect.}
|
||||
|
||||
proc exchangeTransitionConfiguration*(p: Eth1Monitor): Future[void] {.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
|
||||
|
||||
let ccTransitionConfiguration = TransitionConfigurationV1(
|
||||
terminalTotalDifficulty: p.depositsChain.cfg.TERMINAL_TOTAL_DIFFICULTY,
|
||||
terminalBlockHash:
|
||||
if p.terminalBlockHash.isSome:
|
||||
p.terminalBlockHash.get
|
||||
else:
|
||||
# TODO can't use static(default(...)) in this context
|
||||
default(BlockHash),
|
||||
terminalBlockNumber:
|
||||
if p.terminalBlockNumber.isSome:
|
||||
p.terminalBlockNumber.get
|
||||
else:
|
||||
# TODO can't use static(default(...)) in this context
|
||||
default(Quantity))
|
||||
let ecTransitionConfiguration =
|
||||
await p.dataProvider.web3.provider.engine_exchangeTransitionConfigurationV1(
|
||||
ccTransitionConfiguration)
|
||||
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
|
||||
|
||||
template readJsonField(j: JsonNode, fieldName: string, ValueType: type): untyped =
|
||||
var res: ValueType
|
||||
fromJson(j[fieldName], fieldName, res)
|
||||
|
@ -949,6 +996,12 @@ proc createInitialDepositSnapshot*(
|
|||
|
||||
return ok DepositContractSnapshot(eth1Block: knownStartBlockHash)
|
||||
|
||||
proc currentEpoch(m: Eth1Monitor): Epoch =
|
||||
if m.getBeaconTime != nil:
|
||||
m.getBeaconTime().slotOrZero.epoch
|
||||
else:
|
||||
Epoch 0
|
||||
|
||||
proc init*(T: type Eth1Monitor,
|
||||
cfg: RuntimeConfig,
|
||||
db: BeaconChainDB,
|
||||
|
@ -1323,7 +1376,10 @@ proc startEth1Syncing(m: Eth1Monitor, delayBeforeStart: Duration) {.async.} =
|
|||
await m.dataProvider.onBlockHeaders(newBlockHeadersHandler,
|
||||
subscriptionErrorHandler)
|
||||
|
||||
if m.depositsChain.blocks.len == 0:
|
||||
let shouldProcessDeposits = not m.depositContractAddress.isZeroMemory
|
||||
var scratchMerkleizer: ref DepositsMerkleizer
|
||||
var eth1SyncedTo: Eth1BlockNumber
|
||||
if shouldProcessDeposits and m.depositsChain.blocks.len == 0:
|
||||
let startBlock = awaitWithRetries(
|
||||
m.dataProvider.getBlockByHash(m.depositsChain.finalizedBlockHash.asBlockHash))
|
||||
|
||||
|
@ -1334,15 +1390,16 @@ proc startEth1Syncing(m: Eth1Monitor, delayBeforeStart: Duration) {.async.} =
|
|||
m.depositsChain.finalizedBlockHash,
|
||||
m.depositsChain.finalizedDepositsMerkleizer))
|
||||
|
||||
var eth1SyncedTo = Eth1BlockNumber m.depositsChain.blocks.peekLast.number
|
||||
eth1_synced_head.set eth1SyncedTo.toGaugeValue
|
||||
eth1_finalized_head.set eth1SyncedTo.toGaugeValue
|
||||
eth1_finalized_deposits.set(
|
||||
m.depositsChain.finalizedDepositsMerkleizer.getChunkCount.toGaugeValue)
|
||||
eth1SyncedTo = Eth1BlockNumber startBlock.number
|
||||
|
||||
var scratchMerkleizer = newClone(copy m.finalizedDepositsMerkleizer)
|
||||
eth1_synced_head.set eth1SyncedTo.toGaugeValue
|
||||
eth1_finalized_head.set eth1SyncedTo.toGaugeValue
|
||||
eth1_finalized_deposits.set(
|
||||
m.depositsChain.finalizedDepositsMerkleizer.getChunkCount.toGaugeValue)
|
||||
|
||||
debug "Starting Eth1 syncing", `from` = shortLog(m.depositsChain.blocks[0])
|
||||
scratchMerkleizer = newClone(copy m.finalizedDepositsMerkleizer)
|
||||
|
||||
debug "Starting Eth1 syncing", `from` = shortLog(m.depositsChain.blocks[0])
|
||||
|
||||
while true:
|
||||
if bnStatus == BeaconNodeStatus.Stopping:
|
||||
|
@ -1361,7 +1418,7 @@ proc startEth1Syncing(m: Eth1Monitor, delayBeforeStart: Duration) {.async.} =
|
|||
m.startIdx = 0
|
||||
return
|
||||
|
||||
if mustUsePolling:
|
||||
let nextBlock = if mustUsePolling:
|
||||
let blk = awaitWithRetries(
|
||||
m.dataProvider.web3.provider.eth_getBlockByNumber(blockId("latest"), false))
|
||||
|
||||
|
@ -1373,26 +1430,56 @@ proc startEth1Syncing(m: Eth1Monitor, delayBeforeStart: Duration) {.async.} =
|
|||
continue
|
||||
|
||||
m.latestEth1Block = some fullBlockId
|
||||
blk
|
||||
else:
|
||||
awaitWithTimeout(m.eth1Progress.wait(), 5.minutes):
|
||||
raise newException(CorruptDataProvider, "No eth1 chain progress for too long")
|
||||
|
||||
m.eth1Progress.clear()
|
||||
|
||||
if m.latestEth1BlockNumber <= m.cfg.ETH1_FOLLOW_DISTANCE:
|
||||
continue
|
||||
if m.latestEth1Block.isNone:
|
||||
# It should not be possible for `latestEth1Block` to be none here.
|
||||
# Firing the `eth1Progress` event is always done after assinging
|
||||
# a value for it.
|
||||
continue
|
||||
|
||||
let targetBlock = m.latestEth1BlockNumber - m.cfg.ETH1_FOLLOW_DISTANCE
|
||||
if targetBlock <= eth1SyncedTo:
|
||||
continue
|
||||
awaitWithRetries m.dataProvider.getBlockByHash(m.latestEth1Block.get.hash)
|
||||
|
||||
let earliestBlockOfInterest = m.earliestBlockOfInterest()
|
||||
await m.syncBlockRange(scratchMerkleizer,
|
||||
eth1SyncedTo + 1,
|
||||
targetBlock,
|
||||
earliestBlockOfInterest)
|
||||
eth1SyncedTo = targetBlock
|
||||
eth1_synced_head.set eth1SyncedTo.toGaugeValue
|
||||
if m.currentEpoch >= m.cfg.BELLATRIX_FORK_EPOCH and m.terminalBlockHash.isNone:
|
||||
var terminalBlockCandidate = nextBlock
|
||||
|
||||
info "startEth1Syncing: checking for merge terminal block",
|
||||
currentEpoch = m.currentEpoch,
|
||||
BELLATRIX_FORK_EPOCH = m.cfg.BELLATRIX_FORK_EPOCH,
|
||||
totalDifficulty = nextBlock.totalDifficulty,
|
||||
ttd = m.cfg.TERMINAL_TOTAL_DIFFICULTY,
|
||||
terminalBlockHash = m.terminalBlockHash
|
||||
|
||||
if terminalBlockCandidate.totalDifficulty >= m.cfg.TERMINAL_TOTAL_DIFFICULTY:
|
||||
while not terminalBlockCandidate.parentHash.isZeroMemory:
|
||||
var parentBlock = awaitWithRetries(
|
||||
m.dataProvider.getBlockByHash(terminalBlockCandidate.parentHash))
|
||||
if parentBlock.totalDifficulty < m.cfg.TERMINAL_TOTAL_DIFFICULTY:
|
||||
break
|
||||
terminalBlockCandidate = parentBlock
|
||||
m.terminalBlockHash = some terminalBlockCandidate.hash
|
||||
m.terminalBlockNumber = some terminalBlockCandidate.number
|
||||
|
||||
if shouldProcessDeposits:
|
||||
if m.latestEth1BlockNumber <= m.cfg.ETH1_FOLLOW_DISTANCE:
|
||||
continue
|
||||
|
||||
let targetBlock = m.latestEth1BlockNumber - m.cfg.ETH1_FOLLOW_DISTANCE
|
||||
if targetBlock <= eth1SyncedTo:
|
||||
continue
|
||||
|
||||
let earliestBlockOfInterest = m.earliestBlockOfInterest()
|
||||
await m.syncBlockRange(scratchMerkleizer,
|
||||
eth1SyncedTo + 1,
|
||||
targetBlock,
|
||||
earliestBlockOfInterest)
|
||||
eth1SyncedTo = targetBlock
|
||||
eth1_synced_head.set eth1SyncedTo.toGaugeValue
|
||||
|
||||
proc start(m: Eth1Monitor, delayBeforeStart: Duration) {.gcsafe.} =
|
||||
if m.runFut.isNil:
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
import
|
||||
std/[os, random, sequtils, terminal, times],
|
||||
bearssl, chronicles, chronos,
|
||||
bearssl, chronos, chronicles, chronicles/chronos_tools,
|
||||
metrics, metrics/chronos_httpserver,
|
||||
stew/[byteutils, io2],
|
||||
eth/p2p/discoveryv5/[enr, random2],
|
||||
|
@ -559,8 +559,8 @@ proc init*(T: type BeaconNode,
|
|||
dag = loadChainDag(
|
||||
config, cfg, db, eventBus,
|
||||
validatorMonitor, networkGenesisValidatorsRoot)
|
||||
beaconClock = BeaconClock.init(
|
||||
getStateField(dag.headState, genesis_time))
|
||||
genesisTime = getStateField(dag.headState, genesis_time)
|
||||
beaconClock = BeaconClock.init(genesisTime)
|
||||
getBeaconTime = beaconClock.getBeaconTimeFn()
|
||||
|
||||
if config.weakSubjectivityCheckpoint.isSome:
|
||||
|
@ -665,6 +665,13 @@ proc init*(T: type BeaconNode,
|
|||
else:
|
||||
nil
|
||||
|
||||
bellatrixEpochTime =
|
||||
genesisTime + cfg.BELLATRIX_FORK_EPOCH * SLOTS_PER_EPOCH * SECONDS_PER_SLOT
|
||||
|
||||
nextExchangeTransitionConfTime =
|
||||
max(Moment.init(int64 bellatrixEpochTime, Second),
|
||||
Moment.now)
|
||||
|
||||
let node = BeaconNode(
|
||||
nickname: nickname,
|
||||
graffitiBytes: if config.graffiti.isSome: config.graffiti.get
|
||||
|
@ -683,7 +690,8 @@ proc init*(T: type BeaconNode,
|
|||
gossipState: {},
|
||||
beaconClock: beaconClock,
|
||||
validatorMonitor: validatorMonitor,
|
||||
stateTtlCache: stateTtlCache)
|
||||
stateTtlCache: stateTtlCache,
|
||||
nextExchangeTransitionConfTime: nextExchangeTransitionConfTime)
|
||||
|
||||
node.initLightClient(
|
||||
rng, cfg, dag.forkDigests, getBeaconTime, dag.genesis_validators_root)
|
||||
|
@ -1255,7 +1263,7 @@ proc handleMissingBlocks(node: BeaconNode) =
|
|||
debug "Requesting detected missing blocks", blocks = shortLog(missingBlocks)
|
||||
node.requestManager.fetchAncestorBlocks(missingBlocks)
|
||||
|
||||
proc onSecond(node: BeaconNode) =
|
||||
proc onSecond(node: BeaconNode, time: Moment) =
|
||||
## This procedure will be called once per second.
|
||||
if not(node.syncManager.inProgress):
|
||||
node.handleMissingBlocks()
|
||||
|
@ -1263,6 +1271,12 @@ proc onSecond(node: BeaconNode) =
|
|||
# Nim GC metrics (for the main thread)
|
||||
updateThreadMetrics()
|
||||
|
||||
## This procedure will be called once per minute.
|
||||
# https://github.com/ethereum/execution-apis/blob/v1.0.0-alpha.9/src/engine/specification.md#engine_exchangetransitionconfigurationv1
|
||||
if time > node.nextExchangeTransitionConfTime and not node.eth1Monitor.isNil:
|
||||
node.nextExchangeTransitionConfTime = time + chronos.minutes(1)
|
||||
traceAsyncErrors node.eth1Monitor.exchangeTransitionConfiguration()
|
||||
|
||||
if node.config.stopAtSyncedEpoch != 0 and
|
||||
node.dag.head.slot.epoch >= node.config.stopAtSyncedEpoch:
|
||||
notice "Shutting down after having reached the target synced epoch"
|
||||
|
@ -1276,7 +1290,7 @@ proc runOnSecondLoop(node: BeaconNode) {.async.} =
|
|||
await chronos.sleepAsync(sleepTime)
|
||||
let afterSleep = chronos.now(chronos.Moment)
|
||||
let sleepTime = afterSleep - start
|
||||
node.onSecond()
|
||||
node.onSecond(start)
|
||||
let finished = chronos.now(chronos.Moment)
|
||||
let processingTime = finished - afterSleep
|
||||
ticks_delay.set(sleepTime.nanoseconds.float / nanosecondsIn1s)
|
||||
|
|
|
@ -571,11 +571,16 @@ proc getExecutionPayload(
|
|||
const GETPAYLOAD_TIMEOUT = 1.seconds
|
||||
|
||||
let
|
||||
terminalBlockHash =
|
||||
if node.eth1Monitor.terminalBlockHash.isSome:
|
||||
node.eth1Monitor.terminalBlockHash.get.asEth2Digest
|
||||
else:
|
||||
default(Eth2Digest)
|
||||
latestHead =
|
||||
if not node.dag.head.executionBlockRoot.isZero:
|
||||
node.dag.head.executionBlockRoot
|
||||
else:
|
||||
default(Eth2Digest)
|
||||
terminalBlockHash
|
||||
latestFinalized = node.dag.finalizedHead.blck.executionBlockRoot
|
||||
payload_id = (await forkchoice_updated(
|
||||
proposalState.bellatrixData.data, latestHead, latestFinalized,
|
||||
|
@ -653,9 +658,10 @@ proc makeBeaconBlockForHeadAndSlot*(node: BeaconNode,
|
|||
else:
|
||||
node.syncCommitteeMsgPool[].produceSyncAggregate(head.root),
|
||||
if slot.epoch < node.dag.cfg.BELLATRIX_FORK_EPOCH or
|
||||
# TODO when Eth1Monitor TTD following comes in, actually detect
|
||||
# transition block directly
|
||||
not is_merge_transition_complete(proposalState.bellatrixData.data):
|
||||
not (
|
||||
is_merge_transition_complete(proposalState.bellatrixData.data) or
|
||||
((not node.eth1Monitor.isNil) and
|
||||
node.eth1Monitor.terminalBlockHash.isSome)):
|
||||
default(bellatrix.ExecutionPayload)
|
||||
else:
|
||||
let pubkey = node.dag.validatorKey(validator_index)
|
||||
|
|
Loading…
Reference in New Issue