VC: blinded block publishing support (#4332)

* Add blind REST API declarations and implementations.

* shortLog is still not stable.

* Fix shortLog issues.

* Enable disabled logging statements.

* Address review comments.

* Avoid templates suffering from double evaluation of their params

* Address review comments.

* Fix compilation issue.

Co-authored-by: Zahary Karadjov <zahary@status.im>
This commit is contained in:
Eugene Kabanov 2022-11-24 11:14:05 +02:00 committed by GitHub
parent c8083f2c32
commit 8fa6064b9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 629 additions and 115 deletions

View File

@ -123,6 +123,7 @@ type
ListFeeRecipientResponse | ListFeeRecipientResponse |
PrepareBeaconProposer | PrepareBeaconProposer |
ProduceBlockResponseV2 | ProduceBlockResponseV2 |
ProduceBlindedBlockResponse |
RestIndexedErrorMessage | RestIndexedErrorMessage |
RestErrorMessage | RestErrorMessage |
RestValidator | RestValidator |
@ -886,29 +887,31 @@ template unrecognizedFieldWarning =
## ForkedBeaconBlock ## ForkedBeaconBlock
template prepareForkedBlockReading( template prepareForkedBlockReading(
reader: var JsonReader[RestJson], value: untyped, reader: var JsonReader[RestJson], value: untyped,
version: var Option[BeaconBlockFork], data: var Option[JsonString]) = version: var Option[BeaconBlockFork],
data: var Option[JsonString],
blockTypeName: cstring) =
for fieldName {.inject.} in readObjectFields(reader): for fieldName {.inject.} in readObjectFields(reader):
case fieldName case fieldName
of "version": of "version":
if version.isSome(): if version.isSome():
reader.raiseUnexpectedField("Multiple version fields found", reader.raiseUnexpectedField("Multiple version fields found",
"ForkedBeaconBlock") blockTypeName)
let vres = reader.readValue(string) let vres = reader.readValue(string).toLowerAscii()
case vres case vres
of "PHASE0", "phase0": of "phase0":
version = some(BeaconBlockFork.Phase0) version = some(BeaconBlockFork.Phase0)
of "ALTAIR", "altair": of "altair":
version = some(BeaconBlockFork.Altair) version = some(BeaconBlockFork.Altair)
of "BELLATRIX", "bellatrix": of "bellatrix":
version = some(BeaconBlockFork.Bellatrix) version = some(BeaconBlockFork.Bellatrix)
of "CAPELLA", "capella": of "capella":
version = some(BeaconBlockFork.Bellatrix) version = some(BeaconBlockFork.Bellatrix)
else: else:
reader.raiseUnexpectedValue("Incorrect version field value") reader.raiseUnexpectedValue("Incorrect version field value")
of "block", "block_header", "data": of "block", "block_header", "data":
if data.isSome(): if data.isSome():
reader.raiseUnexpectedField("Multiple block or block_header fields found", reader.raiseUnexpectedField("Multiple block or block_header fields found",
"ForkedBeaconBlock") blockTypeName)
data = some(reader.readValue(JsonString)) data = some(reader.readValue(JsonString))
else: else:
unrecognizedFieldWarning() unrecognizedFieldWarning()
@ -925,7 +928,7 @@ proc readValue*[BlockType: ForkedBeaconBlock](
version: Option[BeaconBlockFork] version: Option[BeaconBlockFork]
data: Option[JsonString] data: Option[JsonString]
prepareForkedBlockReading(reader, value, version, data) prepareForkedBlockReading(reader, value, version, data, "ForkedBeaconBlock")
case version.get(): case version.get():
of BeaconBlockFork.Phase0: of BeaconBlockFork.Phase0:
@ -967,6 +970,57 @@ proc readValue*[BlockType: ForkedBeaconBlock](
of BeaconBlockFork.Capella: of BeaconBlockFork.Capella:
reader.raiseUnexpectedValue($capellaImplementationMissing) reader.raiseUnexpectedValue($capellaImplementationMissing)
proc readValue*[BlockType: ForkedBlindedBeaconBlock](
reader: var JsonReader[RestJson],
value: var BlockType
) {.raises: [IOError, SerializationError, Defect].} =
var
version: Option[BeaconBlockFork]
data: Option[JsonString]
prepareForkedBlockReading(reader, value, version, data,
"ForkedBlindedBeaconBlock")
case version.get():
of BeaconBlockFork.Phase0:
let res =
try:
RestJson.decode(string(data.get()),
phase0.BeaconBlock,
requireAllFields = true,
allowUnknownFields = true)
except SerializationError as exc:
reader.raiseUnexpectedValue("Incorrect phase0 block format, [" &
exc.formatMsg("BlindedBlock") & "]")
value = ForkedBlindedBeaconBlock(kind: BeaconBlockFork.Phase0,
phase0Data: res)
of BeaconBlockFork.Altair:
let res =
try:
RestJson.decode(string(data.get()),
altair.BeaconBlock,
requireAllFields = true,
allowUnknownFields = true)
except SerializationError as exc:
reader.raiseUnexpectedValue("Incorrect altair block format, [" &
exc.formatMsg("BlindedBlock") & "]")
value = ForkedBlindedBeaconBlock(kind: BeaconBlockFork.Altair,
altairData: res)
of BeaconBlockFork.Bellatrix:
let res =
try:
RestJson.decode(string(data.get()),
BlindedBeaconBlock,
requireAllFields = true,
allowUnknownFields = true)
except SerializationError as exc:
reader.raiseUnexpectedValue("Incorrect bellatrix block format, [" &
exc.formatMsg("BlindedBlock") & "]")
value = ForkedBlindedBeaconBlock(kind: BeaconBlockFork.Bellatrix,
bellatrixData: res)
of BeaconBlockFork.Capella:
reader.raiseUnexpectedValue($capellaImplementationMissing)
proc readValue*[BlockType: Web3SignerForkedBeaconBlock]( proc readValue*[BlockType: Web3SignerForkedBeaconBlock](
reader: var JsonReader[RestJson], reader: var JsonReader[RestJson],
value: var BlockType) {.raises: [IOError, SerializationError, Defect].} = value: var BlockType) {.raises: [IOError, SerializationError, Defect].} =
@ -974,7 +1028,8 @@ proc readValue*[BlockType: Web3SignerForkedBeaconBlock](
version: Option[BeaconBlockFork] version: Option[BeaconBlockFork]
data: Option[JsonString] data: Option[JsonString]
prepareForkedBlockReading(reader, value, version, data) prepareForkedBlockReading(reader, value, version, data,
"Web3SignerForkedBeaconBlock")
case version.get(): case version.get():
of BeaconBlockFork.Phase0: of BeaconBlockFork.Phase0:

View File

@ -13,6 +13,7 @@ import
chronos, presto/client, chronicles, chronos, presto/client, chronicles,
".."/".."/validators/slashing_protection_common, ".."/".."/validators/slashing_protection_common,
".."/datatypes/[phase0, altair, bellatrix], ".."/datatypes/[phase0, altair, bellatrix],
".."/mev/bellatrix_mev,
".."/[helpers, forks, keystore, eth2_ssz_serialization], ".."/[helpers, forks, keystore, eth2_ssz_serialization],
"."/[rest_types, rest_common, eth2_rest_serialization] "."/[rest_types, rest_common, eth2_rest_serialization]
@ -135,6 +136,33 @@ proc publishSszBlock*(
extraHeaders = @[("eth-consensus-version", consensus)]) extraHeaders = @[("eth-consensus-version", consensus)])
return resp return resp
proc publishBlindedBlock*(body: phase0.SignedBeaconBlock): RestPlainResponse {.
rest, endpoint: "/eth/v1/beacon/blinded_blocks",
meth: MethodPost.}
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlindedBlock
proc publishBlindedBlock*(body: altair.SignedBeaconBlock): RestPlainResponse {.
rest, endpoint: "/eth/v1/beacon/blinded_blocks",
meth: MethodPost.}
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlindedBlock
proc publishBlindedBlock*(body: SignedBlindedBeaconBlock): RestPlainResponse {.
rest, endpoint: "/eth/v1/beacon/blinded_blocks",
meth: MethodPost.}
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlindedBlock
proc publishSszBlindedBlock*(
client: RestClientRef,
blck: ForkySignedBeaconBlock
): Future[RestPlainResponse] {.async.} =
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlindedBlock
let
consensus = typeof(blck).toFork.toString()
resp = await client.publishBlindedBlock(
blck, restContentType = $OctetStreamMediaType,
extraHeaders = @[("eth-consensus-version", consensus)])
return resp
proc getBlockV2Plain*(block_id: BlockIdent): RestPlainResponse {. proc getBlockV2Plain*(block_id: BlockIdent): RestPlainResponse {.
rest, endpoint: "/eth/v2/beacon/blocks/{block_id}", rest, endpoint: "/eth/v2/beacon/blocks/{block_id}",
accept: preferSSZ, accept: preferSSZ,

View File

@ -604,6 +604,7 @@ type
ProduceAttestationDataResponse* = DataEnclosedObject[AttestationData] ProduceAttestationDataResponse* = DataEnclosedObject[AttestationData]
ProduceBlockResponse* = DataEnclosedObject[phase0.BeaconBlock] ProduceBlockResponse* = DataEnclosedObject[phase0.BeaconBlock]
ProduceBlockResponseV2* = ForkedBeaconBlock ProduceBlockResponseV2* = ForkedBeaconBlock
ProduceBlindedBlockResponse* = ForkedBlindedBeaconBlock
ProduceSyncCommitteeContributionResponse* = DataEnclosedObject[SyncCommitteeContribution] ProduceSyncCommitteeContributionResponse* = DataEnclosedObject[SyncCommitteeContribution]
SubmitBlindedBlockResponse* = DataEnclosedObject[bellatrix.ExecutionPayload] SubmitBlindedBlockResponse* = DataEnclosedObject[bellatrix.ExecutionPayload]
GetValidatorsActivityResponse* = DataEnclosedObject[seq[RestActivityItem]] GetValidatorsActivityResponse* = DataEnclosedObject[seq[RestActivityItem]]

View File

@ -41,6 +41,13 @@ proc produceBlockV2*(slot: Slot, randao_reveal: ValidatorSig,
meth: MethodGet.} meth: MethodGet.}
## https://ethereum.github.io/beacon-APIs/#/Validator/produceBlockV2 ## https://ethereum.github.io/beacon-APIs/#/Validator/produceBlockV2
proc produceBlindedBlock*(slot: Slot, randao_reveal: ValidatorSig,
graffiti: GraffitiBytes
): RestResponse[ProduceBlindedBlockResponse] {.
rest, endpoint: "/eth/v1/validator/blinded_blocks/{slot}",
meth: MethodGet.}
## https://ethereum.github.io/beacon-APIs/#/Validator/produceBlindedBlock
proc produceAttestationData*(slot: Slot, proc produceAttestationData*(slot: Slot,
committee_index: CommitteeIndex committee_index: CommitteeIndex
): RestResponse[ProduceAttestationDataResponse] {. ): RestResponse[ProduceAttestationDataResponse] {.

View File

@ -22,7 +22,7 @@ import
# it sequentially # it sequentially
export export
extras, block_id, phase0, altair, bellatrix, eth2_merkleization, extras, block_id, phase0, altair, bellatrix, eth2_merkleization,
eth2_ssz_serialization, presets eth2_ssz_serialization, presets, bellatrix_mev
# This file contains helpers for dealing with forks - we have two ways we can # This file contains helpers for dealing with forks - we have two ways we can
# deal with forks: # deal with forks:
@ -159,6 +159,18 @@ type
of BeaconBlockFork.Bellatrix: bellatrixData*: bellatrix.SignedBeaconBlock of BeaconBlockFork.Bellatrix: bellatrixData*: bellatrix.SignedBeaconBlock
of BeaconBlockFork.Capella: capellaData*: capella.SignedBeaconBlock of BeaconBlockFork.Capella: capellaData*: capella.SignedBeaconBlock
ForkySignedBlindedBeaconBlock* =
phase0.SignedBeaconBlock |
altair.SignedBeaconBlock |
SignedBlindedBeaconBlock
ForkedSignedBlindedBeaconBlock* = object
case kind*: BeaconBlockFork
of BeaconBlockFork.Phase0: phase0Data*: phase0.SignedBeaconBlock
of BeaconBlockFork.Altair: altairData*: altair.SignedBeaconBlock
of BeaconBlockFork.Bellatrix: bellatrixData*: SignedBlindedBeaconBlock
of BeaconBlockFork.Capella: capellaData*: SignedBlindedBeaconBlock
ForkySigVerifiedSignedBeaconBlock* = ForkySigVerifiedSignedBeaconBlock* =
phase0.SigVerifiedSignedBeaconBlock | phase0.SigVerifiedSignedBeaconBlock |
altair.SigVerifiedSignedBeaconBlock | altair.SigVerifiedSignedBeaconBlock |
@ -265,8 +277,8 @@ template init*(T: type ForkedSignedBeaconBlock, blck: bellatrix.SignedBeaconBloc
template init*(T: type ForkedSignedBeaconBlock, blck: capella.SignedBeaconBlock): T = template init*(T: type ForkedSignedBeaconBlock, blck: capella.SignedBeaconBlock): T =
T(kind: BeaconBlockFork.Capella, capellaData: blck) T(kind: BeaconBlockFork.Capella, capellaData: blck)
template init*(T: type ForkedSignedBeaconBlock, forked: ForkedBeaconBlock, func init*(T: type ForkedSignedBeaconBlock, forked: ForkedBeaconBlock,
blockRoot: Eth2Digest, signature: ValidatorSig): T = blockRoot: Eth2Digest, signature: ValidatorSig): T =
case forked.kind case forked.kind
of BeaconBlockFork.Phase0: of BeaconBlockFork.Phase0:
T(kind: BeaconBlockFork.Phase0, T(kind: BeaconBlockFork.Phase0,
@ -289,6 +301,29 @@ template init*(T: type ForkedSignedBeaconBlock, forked: ForkedBeaconBlock,
root: blockRoot, root: blockRoot,
signature: signature)) signature: signature))
func init*(T: type ForkedSignedBlindedBeaconBlock,
forked: ForkedBlindedBeaconBlock, blockRoot: Eth2Digest,
signature: ValidatorSig): T =
case forked.kind
of BeaconBlockFork.Phase0:
T(kind: BeaconBlockFork.Phase0,
phase0Data: phase0.SignedBeaconBlock(message: forked.phase0Data,
root: blockRoot,
signature: signature))
of BeaconBlockFork.Altair:
T(kind: BeaconBlockFork.Altair,
altairData: altair.SignedBeaconBlock(message: forked.altairData,
root: blockRoot,
signature: signature))
of BeaconBlockFork.Bellatrix:
T(kind: BeaconBlockFork.Bellatrix,
bellatrixData: SignedBlindedBeaconBlock(message: forked.bellatrixData,
signature: signature))
of BeaconBlockFork.Capella:
T(kind: BeaconBlockFork.Capella,
capellaData: SignedBlindedBeaconBlock(message: forked.capellaData,
signature: signature))
template init*(T: type ForkedMsgTrustedSignedBeaconBlock, blck: phase0.MsgTrustedSignedBeaconBlock): T = template init*(T: type ForkedMsgTrustedSignedBeaconBlock, blck: phase0.MsgTrustedSignedBeaconBlock): T =
T(kind: BeaconBlockFork.Phase0, phase0Data: blck) T(kind: BeaconBlockFork.Phase0, phase0Data: blck)
template init*(T: type ForkedMsgTrustedSignedBeaconBlock, blck: altair.MsgTrustedSignedBeaconBlock): T = template init*(T: type ForkedMsgTrustedSignedBeaconBlock, blck: altair.MsgTrustedSignedBeaconBlock): T =
@ -536,7 +571,8 @@ template asTrusted*(
template withBlck*( template withBlck*(
x: ForkedBeaconBlock | Web3SignerForkedBeaconBlock | x: ForkedBeaconBlock | Web3SignerForkedBeaconBlock |
ForkedSignedBeaconBlock | ForkedMsgTrustedSignedBeaconBlock | ForkedSignedBeaconBlock | ForkedMsgTrustedSignedBeaconBlock |
ForkedTrustedSignedBeaconBlock, ForkedTrustedSignedBeaconBlock | ForkedBlindedBeaconBlock |
ForkedSignedBlindedBeaconBlock,
body: untyped): untyped = body: untyped): untyped =
case x.kind case x.kind
of BeaconBlockFork.Phase0: of BeaconBlockFork.Phase0:
@ -576,7 +612,8 @@ template getForkedBlockField*(
of BeaconBlockFork.Capella: unsafeAddr x.capellaData.message.y)[] of BeaconBlockFork.Capella: unsafeAddr x.capellaData.message.y)[]
template signature*(x: ForkedSignedBeaconBlock | template signature*(x: ForkedSignedBeaconBlock |
ForkedMsgTrustedSignedBeaconBlock): ValidatorSig = ForkedMsgTrustedSignedBeaconBlock |
ForkedSignedBlindedBeaconBlock): ValidatorSig =
withBlck(x): blck.signature withBlck(x): blck.signature
template signature*(x: ForkedTrustedSignedBeaconBlock): TrustedSig = template signature*(x: ForkedTrustedSignedBeaconBlock): TrustedSig =
@ -592,12 +629,13 @@ template slot*(x: ForkedSignedBeaconBlock |
ForkedTrustedSignedBeaconBlock): Slot = ForkedTrustedSignedBeaconBlock): Slot =
withBlck(x): blck.message.slot withBlck(x): blck.message.slot
template shortLog*(x: ForkedBeaconBlock): auto = template shortLog*(x: ForkedBeaconBlock | ForkedBlindedBeaconBlock): auto =
withBlck(x): shortLog(blck) withBlck(x): shortLog(blck)
template shortLog*(x: ForkedSignedBeaconBlock | template shortLog*(x: ForkedSignedBeaconBlock |
ForkedMsgTrustedSignedBeaconBlock | ForkedMsgTrustedSignedBeaconBlock |
ForkedTrustedSignedBeaconBlock): auto = ForkedTrustedSignedBeaconBlock |
ForkedSignedBlindedBeaconBlock): auto =
withBlck(x): shortLog(blck) withBlck(x): shortLog(blck)
chronicles.formatIt ForkedBeaconBlock: it.shortLog chronicles.formatIt ForkedBeaconBlock: it.shortLog

View File

@ -6,6 +6,7 @@
# at your option. This file may not be copied, modified, or distributed except according to those terms. # at your option. This file may not be copied, modified, or distributed except according to those terms.
import ".."/datatypes/[altair, bellatrix] import ".."/datatypes/[altair, bellatrix]
from stew/byteutils import to0xHex
when (NimMajor, NimMinor) < (1, 4): when (NimMajor, NimMinor) < (1, 4):
{.push raises: [Defect].} {.push raises: [Defect].}
@ -76,6 +77,18 @@ func shortLog*(v: BlindedBeaconBlock): auto =
proposer_index: v.proposer_index, proposer_index: v.proposer_index,
parent_root: shortLog(v.parent_root), parent_root: shortLog(v.parent_root),
state_root: shortLog(v.state_root), state_root: shortLog(v.state_root),
eth1data: v.body.eth1_data,
graffiti: $v.body.graffiti,
proposer_slashings_len: v.body.proposer_slashings.len(),
attester_slashings_len: v.body.attester_slashings.len(),
attestations_len: v.body.attestations.len(),
deposits_len: v.body.deposits.len(),
voluntary_exits_len: v.body.voluntary_exits.len(),
sync_committee_participants:
countOnes(v.body.sync_aggregate.sync_committee_bits),
block_number: v.body.execution_payload_header.block_number,
# TODO checksum hex? shortlog?
fee_recipient: to0xHex(v.body.execution_payload_header.fee_recipient.data),
) )
func shortLog*(v: SignedBlindedBeaconBlock): auto = func shortLog*(v: SignedBlindedBeaconBlock): auto =

View File

@ -1772,6 +1772,200 @@ proc publishBlock*(
raise newException(ValidatorApiError, ErrorMessage) raise newException(ValidatorApiError, ErrorMessage)
proc produceBlindedBlock*(
vc: ValidatorClientRef,
slot: Slot,
randao_reveal: ValidatorSig,
graffiti: GraffitiBytes,
strategy: ApiStrategyKind
): Future[ProduceBlindedBlockResponse] {.async.} =
logScope:
request = "produceBlindedBlock"
strategy = $strategy
const ErrorMessage = "Unable to retrieve block data"
case strategy
of ApiStrategyKind.First, ApiStrategyKind.Best:
let res = vc.firstSuccessParallel(
RestResponse[ProduceBlindedBlockResponse],
SlotDuration, {BeaconNodeRole.BlockProposalData},
produceBlindedBlock(it, slot, randao_reveal, graffiti)):
if apiResponse.isErr():
debug ErrorMessage, endpoint = node, error = apiResponse.error()
RestBeaconNodeStatus.Offline
else:
let response = apiResponse.get()
case response.status:
of 200:
trace ResponseSuccess, endpoint = node
RestBeaconNodeStatus.Online
of 400:
debug ResponseInvalidError, response_code = response.status,
endpoint = node
RestBeaconNodeStatus.Incompatible
of 500:
debug ResponseInternalError, response_code = response.status,
endpoint = node
RestBeaconNodeStatus.Offline
of 503:
debug ResponseNoSyncError, response_code = response.status,
endpoint = node
RestBeaconNodeStatus.NotSynced
else:
debug ResponseUnexpectedError, response_code = response.status,
endpoint = node
RestBeaconNodeStatus.Offline
if res.isErr():
raise newException(ValidatorApiError, res.error())
return res.get().data
of ApiStrategyKind.Priority:
vc.firstSuccessSequential(
RestResponse[ProduceBlindedBlockResponse],
SlotDuration, {BeaconNodeRole.BlockProposalData},
produceBlindedBlock(it, slot, randao_reveal, graffiti)):
if apiResponse.isErr():
debug ErrorMessage, endpoint = node, error = apiResponse.error()
RestBeaconNodeStatus.Offline
else:
let response = apiResponse.get()
case response.status:
of 200:
trace ResponseSuccess, endpoint = node
return response.data
of 400:
debug ResponseInvalidError, response_code = response.status,
endpoint = node
RestBeaconNodeStatus.Incompatible
of 500:
debug ResponseInternalError, response_code = response.status,
endpoint = node
RestBeaconNodeStatus.Offline
of 503:
debug ResponseNoSyncError, response_code = response.status,
endpoint = node
RestBeaconNodeStatus.NotSynced
else:
debug ResponseUnexpectedError, response_code = response.status,
endpoint = node
RestBeaconNodeStatus.Offline
raise newException(ValidatorApiError, ErrorMessage)
proc publishBlindedBlock*(
vc: ValidatorClientRef,
data: ForkedSignedBlindedBeaconBlock,
strategy: ApiStrategyKind
): Future[bool] {.async.} =
logScope:
request = "publishBlindedBlock"
strategy = $strategy
const
BlockPublished = "Block was successfully published"
BlockBroadcasted = "Block not passed validation, but still published"
ErrorMessage = "Unable to publish block"
case strategy
of ApiStrategyKind.First, ApiStrategyKind.Best:
let res = block:
vc.firstSuccessParallel(RestPlainResponse, SlotDuration,
{BeaconNodeRole.BlockProposalPublish}):
case data.kind
of BeaconBlockFork.Phase0:
publishBlindedBlock(it, data.phase0Data)
of BeaconBlockFork.Altair:
publishBlindedBlock(it, data.altairData)
of BeaconBlockFork.Bellatrix:
publishBlindedBlock(it, data.bellatrixData)
of BeaconBlockFork.Capella:
raiseAssert $capellaImplementationMissing
do:
if apiResponse.isErr():
debug ErrorMessage, endpoint = node, error = apiResponse.error()
RestBeaconNodeStatus.Offline
else:
let response = apiResponse.get()
case response.status:
of 200:
trace BlockPublished, endpoint = node
RestBeaconNodeStatus.Online
of 202:
debug BlockBroadcasted, endpoint = node
RestBeaconNodeStatus.Online
of 400:
debug ResponseInvalidError, response_code = response.status,
endpoint = node,
response_error = response.getErrorMessage()
RestBeaconNodeStatus.Incompatible
of 500:
debug ResponseInternalError, response_code = response.status,
endpoint = node,
response_error = response.getErrorMessage()
RestBeaconNodeStatus.Offline
of 503:
debug ResponseNoSyncError, response_code = response.status,
endpoint = node,
response_error = response.getErrorMessage()
RestBeaconNodeStatus.NotSynced
else:
debug ResponseUnexpectedError, response_code = response.status,
endpoint = node,
response_error = response.getErrorMessage()
RestBeaconNodeStatus.Offline
if res.isErr():
raise newException(ValidatorApiError, res.error())
return true
of ApiStrategyKind.Priority:
vc.firstSuccessSequential(RestPlainResponse, SlotDuration,
{BeaconNodeRole.BlockProposalPublish}):
case data.kind
of BeaconBlockFork.Phase0:
publishBlindedBlock(it, data.phase0Data)
of BeaconBlockFork.Altair:
publishBlindedBlock(it, data.altairData)
of BeaconBlockFork.Bellatrix:
publishBlindedBlock(it, data.bellatrixData)
of BeaconBlockFork.Capella:
raiseAssert $capellaImplementationMissing
do:
if apiResponse.isErr():
debug ErrorMessage, endpoint = node, error = apiResponse.error()
RestBeaconNodeStatus.Offline
else:
let response = apiResponse.get()
case response.status:
of 200:
trace BlockPublished, endpoint = node
return true
of 202:
debug BlockBroadcasted, endpoint = node
return true
of 400:
debug ResponseInvalidError, response_code = response.status,
endpoint = node,
response_error = response.getErrorMessage()
RestBeaconNodeStatus.Incompatible
of 500:
debug ResponseInternalError, response_code = response.status,
endpoint = node,
response_error = response.getErrorMessage()
RestBeaconNodeStatus.Offline
of 503:
debug ResponseNoSyncError, response_code = response.status,
endpoint = node,
response_error = response.getErrorMessage()
RestBeaconNodeStatus.NotSynced
else:
debug ResponseUnexpectedError, response_code = response.status,
endpoint = node,
response_error = response.getErrorMessage()
RestBeaconNodeStatus.Offline
raise newException(ValidatorApiError, ErrorMessage)
proc prepareBeaconCommitteeSubnet*( proc prepareBeaconCommitteeSubnet*(
vc: ValidatorClientRef, vc: ValidatorClientRef,
data: seq[RestCommitteeSubscription], data: seq[RestCommitteeSubscription],

View File

@ -13,6 +13,82 @@ import
logScope: service = "block_service" logScope: service = "block_service"
type
PreparedBeaconBlock = object
blockRoot*: Eth2Digest
data*: ForkedBeaconBlock
PreparedBlindedBeaconBlock = object
blockRoot*: Eth2Digest
data*: ForkedBlindedBeaconBlock
proc produceBlock(
vc: ValidatorClientRef,
currentSlot, slot: Slot,
randao_reveal: ValidatorSig,
graffiti: GraffitiBytes,
validator: AttachedValidator
): Future[Opt[PreparedBeaconBlock]] {.async.} =
logScope:
slot = slot
wall_slot = currentSlot
validator = shortLog(validator)
let
beaconBlock =
try:
await vc.produceBlockV2(slot, randao_reveal, graffiti,
ApiStrategyKind.Best)
except ValidatorApiError:
error "Unable to retrieve block data"
return Opt.none(PreparedBeaconBlock)
except CancelledError as exc:
error "Block data production has been interrupted"
raise exc
except CatchableError as exc:
error "An unexpected error occurred while getting block data",
error_name = exc.name, error_msg = exc.msg
return Opt.none(PreparedBeaconBlock)
blockRoot = withBlck(beaconBlock): hash_tree_root(blck)
return Opt.some(PreparedBeaconBlock(blockRoot: blockRoot, data: beaconBlock))
proc produceBlindedBlock(
vc: ValidatorClientRef,
currentSlot, slot: Slot,
randao_reveal: ValidatorSig,
graffiti: GraffitiBytes,
validator: AttachedValidator
): Future[Opt[PreparedBlindedBeaconBlock]] {.async.} =
logScope:
slot = slot
wall_slot = currentSlot
validator = shortLog(validator)
let
beaconBlock =
try:
await vc.produceBlindedBlock(slot, randao_reveal, graffiti,
ApiStrategyKind.Best)
except ValidatorApiError:
error "Unable to retrieve blinded block data"
return Opt.none(PreparedBlindedBeaconBlock)
except CancelledError as exc:
error "Blinded block data production has been interrupted"
raise exc
except CatchableError as exc:
error "An unexpected error occurred while getting blinded block data",
error_name = exc.name, error_msg = exc.msg
return Opt.none(PreparedBlindedBeaconBlock)
blockRoot = withBlck(beaconBlock): hash_tree_root(blck)
return Opt.some(
PreparedBlindedBeaconBlock(blockRoot: blockRoot, data: beaconBlock))
proc lazyWait[T](fut: Future[T]) {.async.} =
try:
discard await fut
except CatchableError:
discard
proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot, proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot,
validator: AttachedValidator) {.async.} = validator: AttachedValidator) {.async.} =
let let
@ -25,123 +101,204 @@ proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot,
fork = vc.forkAtEpoch(slot.epoch) fork = vc.forkAtEpoch(slot.epoch)
vindex = validator.index.get() vindex = validator.index.get()
logScope:
validator = shortLog(validator)
validator_index = vindex
slot = slot
wall_slot = currentSlot
if not(vc.doppelgangerCheck(validator)): if not(vc.doppelgangerCheck(validator)):
info "Block has not been produced (doppelganger check still active)", info "Block has not been produced (doppelganger check still active)"
slot = slot, validator = shortLog(validator),
validator_index = vindex
return return
debug "Publishing block", validator = shortLog(validator), debug "Publishing block", delay = vc.getDelay(slot.block_deadline()),
delay = vc.getDelay(slot.block_deadline()),
wall_slot = currentSlot,
genesis_root = genesisRoot, genesis_root = genesisRoot,
graffiti = graffiti, fork = fork, slot = slot, graffiti = graffiti, fork = fork
wall_slot = currentSlot
let randaoReveal = let randaoReveal =
try: try:
let res = await validator.getEpochSignature(fork, genesisRoot, slot.epoch) let res = await validator.getEpochSignature(fork, genesisRoot, slot.epoch)
if res.isErr(): if res.isErr():
error "Unable to generate randao reveal usint remote signer", error "Unable to generate randao reveal using remote signer",
validator = shortLog(validator), error_msg = res.error() error_msg = res.error()
return return
res.get() res.get()
except CancelledError as exc: except CancelledError as exc:
error "Randao reveal processing was interrupted" error "Randao reveal production has been interrupted"
raise exc raise exc
except CatchableError as exc: except CatchableError as exc:
error "An unexpected error occurred while receiving randao data", error "An unexpected error occurred while receiving randao data",
err_name = exc.name, err_msg = exc.msg error_name = exc.name, error_msg = exc.msg
return return
let beaconBlock = var beaconBlocks =
try: block:
await vc.produceBlockV2(slot, randaoReveal, graffiti, let blindedBlockFut =
ApiStrategyKind.Best) if vc.config.payloadBuilderEnable:
except ValidatorApiError: vc.produceBlindedBlock(currentSlot, slot, randaoReveal, graffiti,
error "Unable to retrieve block data", slot = slot, validator)
wall_slot = currentSlot, validator = shortLog(validator) else:
return nil
except CancelledError as exc: let normalBlockFut = vc.produceBlock(currentSlot, slot, randaoReveal,
error "Producing block processing was interrupted" graffiti, validator)
raise exc let blindedBlock =
except CatchableError as exc: if isNil(blindedBlockFut):
error "An unexpected error occurred while getting block data", Opt.none(PreparedBlindedBeaconBlock)
err_name = exc.name, err_msg = exc.msg else:
return try:
await blindedBlockFut
except CancelledError as exc:
if not(normalBlockFut.finished()):
await normalBlockFut.cancelAndWait()
raise exc
except CatchableError as exc:
# This should not be happened, because all the exceptions handled.
Opt.none(PreparedBlindedBeaconBlock)
let blockRoot = withBlck(beaconBlock): hash_tree_root(blck) let normalBlock =
# TODO: signingRoot is recomputed in getBlockSignature just after if blindedBlock.isNone():
let signingRoot = compute_block_signing_root(fork, genesisRoot, slot, try:
blockRoot) await normalBlockFut
let notSlashable = vc.attachedValidators[] except CancelledError as exc:
.slashingProtection raise exc
.registerBlock(vindex, validator.pubkey, slot, signingRoot) except CatchableError as exc:
# This should not be happened, because all the exceptions handled.
Opt.none(PreparedBeaconBlock)
else:
if not(normalBlockFut.finished()):
asyncSpawn lazyWait(normalBlockFut)
Opt.none(PreparedBeaconBlock)
if notSlashable.isOk(): if blindedBlock.isNone() and normalBlock.isNone():
let signature =
try:
let res = await validator.getBlockSignature(fork, genesisRoot,
slot, blockRoot,
beaconBlock)
if res.isErr():
error "Unable to sign block proposal using remote signer",
validator = shortLog(validator), error_msg = res.error()
return
res.get()
except CancelledError as exc:
debug "Block signature processing was interrupted"
raise exc
except CatchableError as exc:
error "An unexpected error occurred while signing block",
err_name = exc.name, err_msg = exc.msg
return return
debug "Sending block", (blindedBlock: blindedBlock, normalBlock: normalBlock)
blockRoot = shortLog(blockRoot), blck = shortLog(beaconBlock),
signature = shortLog(signature), validator = shortLog(validator)
let res = if beaconBlocks.blindedBlock.isSome():
try: let
let signedBlock = ForkedSignedBeaconBlock.init(beaconBlock, blockRoot, preparedBlock = beaconBlocks.blindedBlock.get()
signature) signingRoot = compute_block_signing_root(fork, genesisRoot, slot,
await vc.publishBlock(signedBlock, ApiStrategyKind.First) preparedBlock.blockRoot)
except ValidatorApiError: notSlashable = vc.attachedValidators[]
error "Unable to publish block", .slashingProtection
blockRoot = shortLog(blockRoot), .registerBlock(vindex, validator.pubkey, slot, signingRoot)
blck = shortLog(beaconBlock),
signature = shortLog(signature), logScope:
validator = shortLog(validator), blck = shortLog(preparedBlock.data)
validator_index = validator.index.get(), block_root = shortLog(preparedBlock.blockRoot)
wall_slot = currentSlot signing_root = shortLog(signingRoot)
return
except CancelledError as exc: if notSlashable.isOk():
debug "Publishing block processing was interrupted" let
raise exc signature =
except CatchableError as exc: try:
error "An unexpected error occurred while publishing block", let res = await validator.getBlockSignature(fork, genesisRoot,
err_name = exc.name, err_msg = exc.msg slot,
return preparedBlock.blockRoot,
if res: preparedBlock.data)
let delay = vc.getDelay(slot.block_deadline()) if res.isErr():
beacon_blocks_sent.inc() error "Unable to sign blinded block proposal using remote signer",
beacon_blocks_sent_delay.observe(delay.toFloatSeconds()) error_msg = res.error()
notice "Block published", blockRoot = shortLog(blockRoot), return
blck = shortLog(beaconBlock), signature = shortLog(signature), res.get()
validator = shortLog(validator) except CancelledError as exc:
debug "Blinded block signature process has been interrupted"
raise exc
except CatchableError as exc:
error "An unexpected error occurred while signing blinded block",
error_name = exc.name, error_msg = exc.msg
return
logScope:
signature = shortLog(signature)
let
signedBlock = ForkedSignedBlindedBeaconBlock.init(preparedBlock.data,
preparedBlock.blockRoot, signature)
res =
try:
debug "Sending blinded block"
await vc.publishBlindedBlock(signedBlock, ApiStrategyKind.First)
except ValidatorApiError:
error "Unable to publish blinded block"
return
except CancelledError as exc:
debug "Blinded block publication has been interrupted"
raise exc
except CatchableError as exc:
error "An unexpected error occurred while publishing blinded block",
error_name = exc.name, error_msg = exc.msg
return
if res:
let delay = vc.getDelay(slot.block_deadline())
beacon_blocks_sent.inc()
beacon_blocks_sent_delay.observe(delay.toFloatSeconds())
notice "Blinded block published", delay = delay
else:
warn "Blinded block was not accepted by beacon node"
else: else:
warn "Block was not accepted by beacon node", warn "Slashing protection activated for blinded block proposal"
blockRoot = shortLog(blockRoot),
blck = shortLog(beaconBlock),
signature = shortLog(signature),
validator = shortLog(validator),
wall_slot = currentSlot
else: else:
warn "Slashing protection activated for block proposal", let
blockRoot = shortLog(blockRoot), blck = shortLog(beaconBlock), preparedBlock = beaconBlocks.normalBlock.get()
signingRoot = shortLog(signingRoot), signingRoot = compute_block_signing_root(fork, genesisRoot, slot,
validator = shortLog(validator), preparedBlock.blockRoot)
wall_slot = currentSlot, notSlashable = vc.attachedValidators[]
existingProposal = notSlashable.error .slashingProtection
.registerBlock(vindex, validator.pubkey, slot, signingRoot)
logScope:
blck = shortLog(preparedBlock.data)
block_root = shortLog(preparedBlock.blockRoot)
signing_root = shortLog(signingRoot)
if notSlashable.isOk():
let
signature =
try:
let res = await validator.getBlockSignature(fork,
genesisRoot, slot,
preparedBlock.blockRoot,
preparedBlock.data)
if res.isErr():
error "Unable to sign block proposal using remote signer",
error_msg = res.error()
return
res.get()
except CancelledError as exc:
debug "Block signature process has been interrupted"
raise exc
except CatchableError as exc:
error "An unexpected error occurred while signing block",
error_name = exc.name, error_msg = exc.msg
return
signedBlock = ForkedSignedBeaconBlock.init(preparedBlock.data,
preparedBlock.blockRoot,
signature)
res =
try:
debug "Sending block"
await vc.publishBlock(signedBlock, ApiStrategyKind.First)
except ValidatorApiError:
error "Unable to publish block"
return
except CancelledError as exc:
debug "Block publication has been interrupted"
raise exc
except CatchableError as exc:
error "An unexpected error occurred while publishing block",
error_name = exc.name, error_msg = exc.msg
return
if res:
let delay = vc.getDelay(slot.block_deadline())
beacon_blocks_sent.inc()
beacon_blocks_sent_delay.observe(delay.toFloatSeconds())
notice "Block published", delay = delay
else:
warn "Block was not accepted by beacon node"
else:
warn "Slashing protection activated for block proposal"
proc proposeBlock(vc: ValidatorClientRef, slot: Slot, proc proposeBlock(vc: ValidatorClientRef, slot: Slot,
proposerKey: ValidatorPubKey) {.async.} = proposerKey: ValidatorPubKey) {.async.} =
@ -291,4 +448,3 @@ proc waitForBlockPublished*(vc: ValidatorClientRef, slot: Slot) {.async.} =
pending.add(future.cancelAndWait()) pending.add(future.cancelAndWait())
await allFutures(pending) await allFutures(pending)
raise exc raise exc

View File

@ -326,7 +326,8 @@ proc signData(v: AttachedValidator,
proc getBlockSignature*(v: AttachedValidator, fork: Fork, proc getBlockSignature*(v: AttachedValidator, fork: Fork,
genesis_validators_root: Eth2Digest, slot: Slot, genesis_validators_root: Eth2Digest, slot: Slot,
block_root: Eth2Digest, block_root: Eth2Digest,
blck: ForkedBeaconBlock | BlindedBeaconBlock blck: ForkedBeaconBlock | ForkedBlindedBeaconBlock |
BlindedBeaconBlock
): Future[SignatureResult] {.async.} = ): Future[SignatureResult] {.async.} =
return return
case v.kind case v.kind
@ -336,7 +337,29 @@ proc getBlockSignature*(v: AttachedValidator, fork: Fork,
fork, genesis_validators_root, slot, block_root, fork, genesis_validators_root, slot, block_root,
v.data.privateKey).toValidatorSig()) v.data.privateKey).toValidatorSig())
of ValidatorKind.Remote: of ValidatorKind.Remote:
when blck is BlindedBeaconBlock: when blck is ForkedBlindedBeaconBlock:
let
web3SignerBlock =
case blck.kind
of BeaconBlockFork.Phase0:
Web3SignerForkedBeaconBlock(
kind: BeaconBlockFork.Phase0,
phase0Data: blck.phase0Data)
of BeaconBlockFork.Altair:
Web3SignerForkedBeaconBlock(
kind: BeaconBlockFork.Altair,
altairData: blck.altairData)
of BeaconBlockFork.Bellatrix:
Web3SignerForkedBeaconBlock(
kind: BeaconBlockFork.Bellatrix,
bellatrixData: blck.bellatrixData.toBeaconBlockHeader)
of BeaconBlockFork.Capella:
raiseAssert $capellaImplementationMissing
request = Web3SignerRequest.init(
fork, genesis_validators_root, web3SignerBlock)
await v.signData(request)
elif blck is BlindedBeaconBlock:
let request = Web3SignerRequest.init( let request = Web3SignerRequest.init(
fork, genesis_validators_root, fork, genesis_validators_root,
Web3SignerForkedBeaconBlock( Web3SignerForkedBeaconBlock(
@ -519,4 +542,3 @@ proc getBuilderSignature*(v: AttachedValidator, fork: Fork,
let request = Web3SignerRequest.init( let request = Web3SignerRequest.init(
fork, ZERO_HASH, validatorRegistration) fork, ZERO_HASH, validatorRegistration)
await v.signData(request) await v.signData(request)