handle INVALIDATED forkchoiceUpdated better (#4081)
This commit is contained in:
parent
0191225896
commit
cd46af17e9
|
@ -211,17 +211,21 @@ proc runForkchoiceUpdatedDiscardResult*(
|
||||||
discard await eth1Monitor.runForkchoiceUpdated(
|
discard await eth1Monitor.runForkchoiceUpdated(
|
||||||
headBlockRoot, safeBlockRoot, finalizedBlockRoot)
|
headBlockRoot, safeBlockRoot, finalizedBlockRoot)
|
||||||
|
|
||||||
proc updateExecutionClientHead(self: ref ConsensusManager, newHead: BeaconHead)
|
from ../beacon_clock import GetBeaconTimeFn
|
||||||
{.async.} =
|
from ../fork_choice/fork_choice import mark_root_invalid
|
||||||
|
|
||||||
|
proc updateExecutionClientHead(
|
||||||
|
self: ref ConsensusManager, newHead: BeaconHead):
|
||||||
|
Future[Opt[void]] {.async.} =
|
||||||
if self.eth1Monitor.isNil:
|
if self.eth1Monitor.isNil:
|
||||||
return
|
return Opt[void].ok()
|
||||||
|
|
||||||
let headExecutionPayloadHash = self.dag.loadExecutionBlockRoot(newHead.blck)
|
let headExecutionPayloadHash = self.dag.loadExecutionBlockRoot(newHead.blck)
|
||||||
|
|
||||||
if headExecutionPayloadHash.isZero:
|
if headExecutionPayloadHash.isZero:
|
||||||
# Blocks without execution payloads can't be optimistic.
|
# Blocks without execution payloads can't be optimistic.
|
||||||
self.dag.markBlockVerified(self.quarantine[], newHead.blck.root)
|
self.dag.markBlockVerified(self.quarantine[], newHead.blck.root)
|
||||||
return
|
return Opt[void].ok()
|
||||||
|
|
||||||
# Can't use dag.head here because it hasn't been updated yet
|
# Can't use dag.head here because it hasn't been updated yet
|
||||||
let payloadExecutionStatus = await self.eth1Monitor.runForkchoiceUpdated(
|
let payloadExecutionStatus = await self.eth1Monitor.runForkchoiceUpdated(
|
||||||
|
@ -235,9 +239,12 @@ proc updateExecutionClientHead(self: ref ConsensusManager, newHead: BeaconHead)
|
||||||
of PayloadExecutionStatus.invalid, PayloadExecutionStatus.invalid_block_hash:
|
of PayloadExecutionStatus.invalid, PayloadExecutionStatus.invalid_block_hash:
|
||||||
self.dag.markBlockInvalid(newHead.blck.root)
|
self.dag.markBlockInvalid(newHead.blck.root)
|
||||||
self.quarantine[].addUnviable(newHead.blck.root)
|
self.quarantine[].addUnviable(newHead.blck.root)
|
||||||
|
return Opt.none(void)
|
||||||
of PayloadExecutionStatus.accepted, PayloadExecutionStatus.syncing:
|
of PayloadExecutionStatus.accepted, PayloadExecutionStatus.syncing:
|
||||||
self.dag.optimisticRoots.incl newHead.blck.root
|
self.dag.optimisticRoots.incl newHead.blck.root
|
||||||
|
|
||||||
|
return Opt[void].ok()
|
||||||
|
|
||||||
proc updateHead*(self: var ConsensusManager, newHead: BlockRef) =
|
proc updateHead*(self: var ConsensusManager, newHead: BlockRef) =
|
||||||
## Trigger fork choice and update the DAG with the new head block
|
## Trigger fork choice and update the DAG with the new head block
|
||||||
## This does not automatically prune the DAG after finalization
|
## This does not automatically prune the DAG after finalization
|
||||||
|
@ -352,8 +359,8 @@ proc runProposalForkchoiceUpdated*(
|
||||||
error "Engine API fork-choice update failed", err = err.msg
|
error "Engine API fork-choice update failed", err = err.msg
|
||||||
|
|
||||||
proc updateHeadWithExecution*(
|
proc updateHeadWithExecution*(
|
||||||
self: ref ConsensusManager, newHead: BeaconHead, wallSlot: Slot)
|
self: ref ConsensusManager, initialNewHead: BeaconHead,
|
||||||
{.async.} =
|
getBeaconTimeFn: GetBeaconTimeFn) {.async.} =
|
||||||
## Trigger fork choice and update the DAG with the new head block
|
## Trigger fork choice and update the DAG with the new head block
|
||||||
## This does not automatically prune the DAG after finalization
|
## This does not automatically prune the DAG after finalization
|
||||||
## `pruneFinalized` must be called for pruning.
|
## `pruneFinalized` must be called for pruning.
|
||||||
|
@ -361,7 +368,28 @@ proc updateHeadWithExecution*(
|
||||||
# Grab the new head according to our latest attestation data
|
# Grab the new head according to our latest attestation data
|
||||||
try:
|
try:
|
||||||
# Ensure dag.updateHead has most current information
|
# Ensure dag.updateHead has most current information
|
||||||
await self.updateExecutionClientHead(newHead)
|
var
|
||||||
|
attempts = 0
|
||||||
|
newHead = initialNewHead
|
||||||
|
while (await self.updateExecutionClientHead(newHead)).isErr:
|
||||||
|
# This proc is called on every new block; guarantee timely return
|
||||||
|
inc attempts
|
||||||
|
const maxAttempts = 3
|
||||||
|
if attempts >= maxAttempts:
|
||||||
|
warn "updateHeadWithExecution: too many attempts to recover from invalid payload",
|
||||||
|
attempts, maxAttempts, newHead, initialNewHead
|
||||||
|
break
|
||||||
|
|
||||||
|
# Select new head for next attempt
|
||||||
|
let
|
||||||
|
wallTime = getBeaconTimeFn()
|
||||||
|
nextHead = self.attestationPool[].selectOptimisticHead(wallTime).valueOr:
|
||||||
|
warn "Head selection failed after invalid block, using previous head",
|
||||||
|
newHead, wallSlot = wallTime.slotOrZero
|
||||||
|
break
|
||||||
|
warn "updateHeadWithExecution: attempting to recover from invalid payload",
|
||||||
|
attempts, maxAttempts, newHead, initialNewHead, nextHead
|
||||||
|
newHead = nextHead
|
||||||
|
|
||||||
# Store the new head in the chain DAG - this may cause epochs to be
|
# Store the new head in the chain DAG - this may cause epochs to be
|
||||||
# justified and finalized
|
# justified and finalized
|
||||||
|
@ -373,7 +401,7 @@ proc updateHeadWithExecution*(
|
||||||
# needs while runProposalForkchoiceUpdated requires RANDAO information
|
# needs while runProposalForkchoiceUpdated requires RANDAO information
|
||||||
# from the head state corresponding to the `newHead` block, which only
|
# from the head state corresponding to the `newHead` block, which only
|
||||||
# self.dag.updateHead(...) sets up.
|
# self.dag.updateHead(...) sets up.
|
||||||
await self.runProposalForkchoiceUpdated(wallSlot)
|
await self.runProposalForkchoiceUpdated(getBeaconTimeFn().slotOrZero)
|
||||||
|
|
||||||
self[].checkExpectedBlock()
|
self[].checkExpectedBlock()
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
|
|
|
@ -346,7 +346,7 @@ proc storeBlock*(
|
||||||
wallSlot)
|
wallSlot)
|
||||||
else:
|
else:
|
||||||
asyncSpawn self.consensusManager.updateHeadWithExecution(
|
asyncSpawn self.consensusManager.updateHeadWithExecution(
|
||||||
newHead.get, wallSlot)
|
newHead.get, self.getBeaconTime)
|
||||||
else:
|
else:
|
||||||
warn "Head selection failed, using previous head",
|
warn "Head selection failed, using previous head",
|
||||||
head = shortLog(self.consensusManager.dag.head), wallSlot
|
head = shortLog(self.consensusManager.dag.head), wallSlot
|
||||||
|
|
Loading…
Reference in New Issue