Sign the blinded blocks only if they provide better value than the EL block (#4894)

This commit is contained in:
zah 2023-05-06 11:32:30 +03:00 committed by GitHub
parent 1ebcd8b473
commit ae46be7020
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 21 additions and 45 deletions

View File

@ -571,31 +571,28 @@ proc blindedBlockCheckSlashingAndSign[T](
return ok blindedBlock return ok blindedBlock
proc getBlindedBeaconBlock[ proc getUnsignedBlindedBeaconBlock[
T: bellatrix_mev.SignedBlindedBeaconBlock | T: bellatrix_mev.SignedBlindedBeaconBlock |
capella_mev.SignedBlindedBeaconBlock]( capella_mev.SignedBlindedBeaconBlock](
node: BeaconNode, slot: Slot, validator: AttachedValidator, node: BeaconNode, slot: Slot, validator: AttachedValidator,
validator_index: ValidatorIndex, forkedBlock: ForkedBeaconBlock, validator_index: ValidatorIndex, forkedBlock: ForkedBeaconBlock,
executionPayloadHeader: bellatrix.ExecutionPayloadHeader | executionPayloadHeader: bellatrix.ExecutionPayloadHeader |
capella.ExecutionPayloadHeader): capella.ExecutionPayloadHeader): Result[T, string] =
Future[Result[T, string]] {.async.} =
withBlck(forkedBlock): withBlck(forkedBlock):
when consensusFork >= ConsensusFork.Deneb: when consensusFork >= ConsensusFork.Deneb:
debugRaiseAssert $denebImplementationMissing & ": getBlindedBeaconBlock" debugRaiseAssert $denebImplementationMissing & ": getUnsignedBlindedBeaconBlock"
return err("getBlindedBeaconBlock: Deneb blinded block creation not implemented") return err("getUnsignedBlindedBeaconBlock: Deneb blinded block creation not implemented")
elif consensusFork >= ConsensusFork.Bellatrix: elif consensusFork >= ConsensusFork.Bellatrix:
when not ( when not (
(T is bellatrix_mev.SignedBlindedBeaconBlock and (T is bellatrix_mev.SignedBlindedBeaconBlock and
consensusFork == ConsensusFork.Bellatrix) or consensusFork == ConsensusFork.Bellatrix) or
(T is capella_mev.SignedBlindedBeaconBlock and (T is capella_mev.SignedBlindedBeaconBlock and
consensusFork == ConsensusFork.Capella)): consensusFork == ConsensusFork.Capella)):
return err("getBlindedBeaconBlock: mismatched block/payload types") return err("getUnsignedBlindedBeaconBlock: mismatched block/payload types")
else: else:
return await blindedBlockCheckSlashingAndSign( return ok constructSignableBlindedBlock[T](blck, executionPayloadHeader)
node, slot, validator, validator_index,
constructSignableBlindedBlock[T](blck, executionPayloadHeader))
else: else:
return err("getBlindedBeaconBlock: attempt to construct pre-Bellatrix blinded block") return err("getUnsignedBlindedBeaconBlock: attempt to construct pre-Bellatrix blinded block")
proc getBlindedBlockParts[EPH: ForkyExecutionPayloadHeader]( proc getBlindedBlockParts[EPH: ForkyExecutionPayloadHeader](
node: BeaconNode, head: BlockRef, pubkey: ValidatorPubKey, node: BeaconNode, head: BlockRef, pubkey: ValidatorPubKey,
@ -678,7 +675,8 @@ proc getBuilderBid[
node: BeaconNode, head: BlockRef, validator: AttachedValidator, slot: Slot, node: BeaconNode, head: BlockRef, validator: AttachedValidator, slot: Slot,
randao: ValidatorSig, validator_index: ValidatorIndex): randao: ValidatorSig, validator_index: ValidatorIndex):
Future[BlindedBlockResult[SBBB]] {.async.} = Future[BlindedBlockResult[SBBB]] {.async.} =
# Used by the BN's own validators, but not the REST server ## Returns the unsigned blinded block obtained from the Builder API.
## Used by the BN's own validators, but not the REST server
when SBBB is bellatrix_mev.SignedBlindedBeaconBlock: when SBBB is bellatrix_mev.SignedBlindedBeaconBlock:
type EPH = bellatrix.ExecutionPayloadHeader type EPH = bellatrix.ExecutionPayloadHeader
elif SBBB is capella_mev.SignedBlindedBeaconBlock: elif SBBB is capella_mev.SignedBlindedBeaconBlock:
@ -698,38 +696,14 @@ proc getBuilderBid[
# proposal through the relay network. # proposal through the relay network.
let (executionPayloadHeader, bidValue, forkedBlck) = blindedBlockParts.get let (executionPayloadHeader, bidValue, forkedBlck) = blindedBlockParts.get
# This is only substantively asynchronous with a remote key signer, whereas let unsignedBlindedBlock = getUnsignedBlindedBeaconBlock[SBBB](
# using local key signing, the await can't stall indefinitely any more than node, slot, validator, validator_index, forkedBlck,
# any other await. However, by imposing an arbitrary timeout, it risks that executionPayloadHeader)
# getBlindedBeaconBlock will check slashing conditions, register that block
# in the database to avoid future slashing, then take long enough to exceed
# any specific timeout provided. It's always better to at least try to send
# this proposal. Furthermore, because one attempt to propose on that slot's
# already been registered, the EL fallback will refuses to function, so the
# timeout ensures missing both by builder and engine APIs.
#
# When using web3signer or some other remote signer, this is to some extent
# difficult to avoid entirely, because some timeout should exist, so Nimbus
# can still fall back to EL block production in time. For local signing, it
# simply therefore uses `await` and avoids this potential race.
let blindedBlock =
case validator.kind
of ValidatorKind.Local:
await getBlindedBeaconBlock[SBBB](
node, slot, validator, validator_index, forkedBlck,
executionPayloadHeader)
of ValidatorKind.Remote:
awaitWithTimeout(
getBlindedBeaconBlock[SBBB](
node, slot, validator, validator_index, forkedBlck,
executionPayloadHeader),
1.seconds):
Result[SBBB, string].err("getBlindedBlock timed out")
if blindedBlock.isErr: if unsignedBlindedBlock.isErr:
return err blindedBlock.error() return err unsignedBlindedBlock.error()
return ok (blindedBlock.get, bidValue) return ok (unsignedBlindedBlock.get, bidValue)
proc proposeBlockMEV(node: BeaconNode, blindedBlock: auto): proc proposeBlockMEV(node: BeaconNode, blindedBlock: auto):
Future[Result[BlockRef, string]] {.async.} = Future[Result[BlockRef, string]] {.async.} =
@ -916,18 +890,20 @@ proc proposeBlockAux(
if useBuilderBlock: if useBuilderBlock:
let let
blindedBlock = payloadBuilderBidFut.read blindedBlock = (await blindedBlockCheckSlashingAndSign(
node, slot, validator, validator_index,
payloadBuilderBidFut.read.get.blindedBlckPart)).valueOr:
return head
# Before proposeBlockMEV, can fall back to EL; after, cannot without # Before proposeBlockMEV, can fall back to EL; after, cannot without
# risking slashing. # risking slashing.
maybeUnblindedBlock = await proposeBlockMEV( maybeUnblindedBlock = await proposeBlockMEV(node, blindedBlock)
node, blindedBlock.get.blindedBlckPart)
return maybeUnblindedBlock.valueOr: return maybeUnblindedBlock.valueOr:
warn "Blinded block proposal incomplete", warn "Blinded block proposal incomplete",
head = shortLog(head), slot, validator_index, head = shortLog(head), slot, validator_index,
validator = shortLog(validator), validator = shortLog(validator),
err = maybeUnblindedBlock.error, err = maybeUnblindedBlock.error,
blindedBlck = shortLog(blindedBlock.get().blindedBlckPart) blindedBlck = shortLog(blindedBlock)
beacon_block_builder_missed_without_fallback.inc() beacon_block_builder_missed_without_fallback.inc()
return head return head