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:
parent
c8083f2c32
commit
8fa6064b9a
|
@ -123,6 +123,7 @@ type
|
|||
ListFeeRecipientResponse |
|
||||
PrepareBeaconProposer |
|
||||
ProduceBlockResponseV2 |
|
||||
ProduceBlindedBlockResponse |
|
||||
RestIndexedErrorMessage |
|
||||
RestErrorMessage |
|
||||
RestValidator |
|
||||
|
@ -886,29 +887,31 @@ template unrecognizedFieldWarning =
|
|||
## ForkedBeaconBlock
|
||||
template prepareForkedBlockReading(
|
||||
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):
|
||||
case fieldName
|
||||
of "version":
|
||||
if version.isSome():
|
||||
reader.raiseUnexpectedField("Multiple version fields found",
|
||||
"ForkedBeaconBlock")
|
||||
let vres = reader.readValue(string)
|
||||
blockTypeName)
|
||||
let vres = reader.readValue(string).toLowerAscii()
|
||||
case vres
|
||||
of "PHASE0", "phase0":
|
||||
of "phase0":
|
||||
version = some(BeaconBlockFork.Phase0)
|
||||
of "ALTAIR", "altair":
|
||||
of "altair":
|
||||
version = some(BeaconBlockFork.Altair)
|
||||
of "BELLATRIX", "bellatrix":
|
||||
of "bellatrix":
|
||||
version = some(BeaconBlockFork.Bellatrix)
|
||||
of "CAPELLA", "capella":
|
||||
of "capella":
|
||||
version = some(BeaconBlockFork.Bellatrix)
|
||||
else:
|
||||
reader.raiseUnexpectedValue("Incorrect version field value")
|
||||
of "block", "block_header", "data":
|
||||
if data.isSome():
|
||||
reader.raiseUnexpectedField("Multiple block or block_header fields found",
|
||||
"ForkedBeaconBlock")
|
||||
blockTypeName)
|
||||
data = some(reader.readValue(JsonString))
|
||||
else:
|
||||
unrecognizedFieldWarning()
|
||||
|
@ -925,7 +928,7 @@ proc readValue*[BlockType: ForkedBeaconBlock](
|
|||
version: Option[BeaconBlockFork]
|
||||
data: Option[JsonString]
|
||||
|
||||
prepareForkedBlockReading(reader, value, version, data)
|
||||
prepareForkedBlockReading(reader, value, version, data, "ForkedBeaconBlock")
|
||||
|
||||
case version.get():
|
||||
of BeaconBlockFork.Phase0:
|
||||
|
@ -967,6 +970,57 @@ proc readValue*[BlockType: ForkedBeaconBlock](
|
|||
of BeaconBlockFork.Capella:
|
||||
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](
|
||||
reader: var JsonReader[RestJson],
|
||||
value: var BlockType) {.raises: [IOError, SerializationError, Defect].} =
|
||||
|
@ -974,7 +1028,8 @@ proc readValue*[BlockType: Web3SignerForkedBeaconBlock](
|
|||
version: Option[BeaconBlockFork]
|
||||
data: Option[JsonString]
|
||||
|
||||
prepareForkedBlockReading(reader, value, version, data)
|
||||
prepareForkedBlockReading(reader, value, version, data,
|
||||
"Web3SignerForkedBeaconBlock")
|
||||
|
||||
case version.get():
|
||||
of BeaconBlockFork.Phase0:
|
||||
|
|
|
@ -13,6 +13,7 @@ import
|
|||
chronos, presto/client, chronicles,
|
||||
".."/".."/validators/slashing_protection_common,
|
||||
".."/datatypes/[phase0, altair, bellatrix],
|
||||
".."/mev/bellatrix_mev,
|
||||
".."/[helpers, forks, keystore, eth2_ssz_serialization],
|
||||
"."/[rest_types, rest_common, eth2_rest_serialization]
|
||||
|
||||
|
@ -135,6 +136,33 @@ proc publishSszBlock*(
|
|||
extraHeaders = @[("eth-consensus-version", consensus)])
|
||||
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 {.
|
||||
rest, endpoint: "/eth/v2/beacon/blocks/{block_id}",
|
||||
accept: preferSSZ,
|
||||
|
|
|
@ -604,6 +604,7 @@ type
|
|||
ProduceAttestationDataResponse* = DataEnclosedObject[AttestationData]
|
||||
ProduceBlockResponse* = DataEnclosedObject[phase0.BeaconBlock]
|
||||
ProduceBlockResponseV2* = ForkedBeaconBlock
|
||||
ProduceBlindedBlockResponse* = ForkedBlindedBeaconBlock
|
||||
ProduceSyncCommitteeContributionResponse* = DataEnclosedObject[SyncCommitteeContribution]
|
||||
SubmitBlindedBlockResponse* = DataEnclosedObject[bellatrix.ExecutionPayload]
|
||||
GetValidatorsActivityResponse* = DataEnclosedObject[seq[RestActivityItem]]
|
||||
|
|
|
@ -41,6 +41,13 @@ proc produceBlockV2*(slot: Slot, randao_reveal: ValidatorSig,
|
|||
meth: MethodGet.}
|
||||
## 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,
|
||||
committee_index: CommitteeIndex
|
||||
): RestResponse[ProduceAttestationDataResponse] {.
|
||||
|
|
|
@ -22,7 +22,7 @@ import
|
|||
# it sequentially
|
||||
export
|
||||
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
|
||||
# deal with forks:
|
||||
|
@ -159,6 +159,18 @@ type
|
|||
of BeaconBlockFork.Bellatrix: bellatrixData*: bellatrix.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* =
|
||||
phase0.SigVerifiedSignedBeaconBlock |
|
||||
altair.SigVerifiedSignedBeaconBlock |
|
||||
|
@ -265,8 +277,8 @@ template init*(T: type ForkedSignedBeaconBlock, blck: bellatrix.SignedBeaconBloc
|
|||
template init*(T: type ForkedSignedBeaconBlock, blck: capella.SignedBeaconBlock): T =
|
||||
T(kind: BeaconBlockFork.Capella, capellaData: blck)
|
||||
|
||||
template init*(T: type ForkedSignedBeaconBlock, forked: ForkedBeaconBlock,
|
||||
blockRoot: Eth2Digest, signature: ValidatorSig): T =
|
||||
func init*(T: type ForkedSignedBeaconBlock, forked: ForkedBeaconBlock,
|
||||
blockRoot: Eth2Digest, signature: ValidatorSig): T =
|
||||
case forked.kind
|
||||
of BeaconBlockFork.Phase0:
|
||||
T(kind: BeaconBlockFork.Phase0,
|
||||
|
@ -289,6 +301,29 @@ template init*(T: type ForkedSignedBeaconBlock, forked: ForkedBeaconBlock,
|
|||
root: blockRoot,
|
||||
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 =
|
||||
T(kind: BeaconBlockFork.Phase0, phase0Data: blck)
|
||||
template init*(T: type ForkedMsgTrustedSignedBeaconBlock, blck: altair.MsgTrustedSignedBeaconBlock): T =
|
||||
|
@ -536,7 +571,8 @@ template asTrusted*(
|
|||
template withBlck*(
|
||||
x: ForkedBeaconBlock | Web3SignerForkedBeaconBlock |
|
||||
ForkedSignedBeaconBlock | ForkedMsgTrustedSignedBeaconBlock |
|
||||
ForkedTrustedSignedBeaconBlock,
|
||||
ForkedTrustedSignedBeaconBlock | ForkedBlindedBeaconBlock |
|
||||
ForkedSignedBlindedBeaconBlock,
|
||||
body: untyped): untyped =
|
||||
case x.kind
|
||||
of BeaconBlockFork.Phase0:
|
||||
|
@ -576,7 +612,8 @@ template getForkedBlockField*(
|
|||
of BeaconBlockFork.Capella: unsafeAddr x.capellaData.message.y)[]
|
||||
|
||||
template signature*(x: ForkedSignedBeaconBlock |
|
||||
ForkedMsgTrustedSignedBeaconBlock): ValidatorSig =
|
||||
ForkedMsgTrustedSignedBeaconBlock |
|
||||
ForkedSignedBlindedBeaconBlock): ValidatorSig =
|
||||
withBlck(x): blck.signature
|
||||
|
||||
template signature*(x: ForkedTrustedSignedBeaconBlock): TrustedSig =
|
||||
|
@ -592,12 +629,13 @@ template slot*(x: ForkedSignedBeaconBlock |
|
|||
ForkedTrustedSignedBeaconBlock): Slot =
|
||||
withBlck(x): blck.message.slot
|
||||
|
||||
template shortLog*(x: ForkedBeaconBlock): auto =
|
||||
template shortLog*(x: ForkedBeaconBlock | ForkedBlindedBeaconBlock): auto =
|
||||
withBlck(x): shortLog(blck)
|
||||
|
||||
template shortLog*(x: ForkedSignedBeaconBlock |
|
||||
ForkedMsgTrustedSignedBeaconBlock |
|
||||
ForkedTrustedSignedBeaconBlock): auto =
|
||||
ForkedTrustedSignedBeaconBlock |
|
||||
ForkedSignedBlindedBeaconBlock): auto =
|
||||
withBlck(x): shortLog(blck)
|
||||
|
||||
chronicles.formatIt ForkedBeaconBlock: it.shortLog
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import ".."/datatypes/[altair, bellatrix]
|
||||
from stew/byteutils import to0xHex
|
||||
|
||||
when (NimMajor, NimMinor) < (1, 4):
|
||||
{.push raises: [Defect].}
|
||||
|
@ -76,6 +77,18 @@ func shortLog*(v: BlindedBeaconBlock): auto =
|
|||
proposer_index: v.proposer_index,
|
||||
parent_root: shortLog(v.parent_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 =
|
||||
|
|
|
@ -1772,6 +1772,200 @@ proc publishBlock*(
|
|||
|
||||
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*(
|
||||
vc: ValidatorClientRef,
|
||||
data: seq[RestCommitteeSubscription],
|
||||
|
|
|
@ -13,6 +13,82 @@ import
|
|||
|
||||
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,
|
||||
validator: AttachedValidator) {.async.} =
|
||||
let
|
||||
|
@ -25,123 +101,204 @@ proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot,
|
|||
fork = vc.forkAtEpoch(slot.epoch)
|
||||
vindex = validator.index.get()
|
||||
|
||||
logScope:
|
||||
validator = shortLog(validator)
|
||||
validator_index = vindex
|
||||
slot = slot
|
||||
wall_slot = currentSlot
|
||||
|
||||
if not(vc.doppelgangerCheck(validator)):
|
||||
info "Block has not been produced (doppelganger check still active)",
|
||||
slot = slot, validator = shortLog(validator),
|
||||
validator_index = vindex
|
||||
info "Block has not been produced (doppelganger check still active)"
|
||||
return
|
||||
|
||||
debug "Publishing block", validator = shortLog(validator),
|
||||
delay = vc.getDelay(slot.block_deadline()),
|
||||
wall_slot = currentSlot,
|
||||
debug "Publishing block", delay = vc.getDelay(slot.block_deadline()),
|
||||
genesis_root = genesisRoot,
|
||||
graffiti = graffiti, fork = fork, slot = slot,
|
||||
wall_slot = currentSlot
|
||||
graffiti = graffiti, fork = fork
|
||||
let randaoReveal =
|
||||
try:
|
||||
let res = await validator.getEpochSignature(fork, genesisRoot, slot.epoch)
|
||||
if res.isErr():
|
||||
error "Unable to generate randao reveal usint remote signer",
|
||||
validator = shortLog(validator), error_msg = res.error()
|
||||
error "Unable to generate randao reveal using remote signer",
|
||||
error_msg = res.error()
|
||||
return
|
||||
res.get()
|
||||
except CancelledError as exc:
|
||||
error "Randao reveal processing was interrupted"
|
||||
error "Randao reveal production has been interrupted"
|
||||
raise exc
|
||||
except CatchableError as exc:
|
||||
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
|
||||
|
||||
let beaconBlock =
|
||||
try:
|
||||
await vc.produceBlockV2(slot, randaoReveal, graffiti,
|
||||
ApiStrategyKind.Best)
|
||||
except ValidatorApiError:
|
||||
error "Unable to retrieve block data", slot = slot,
|
||||
wall_slot = currentSlot, validator = shortLog(validator)
|
||||
return
|
||||
except CancelledError as exc:
|
||||
error "Producing block processing was interrupted"
|
||||
raise exc
|
||||
except CatchableError as exc:
|
||||
error "An unexpected error occurred while getting block data",
|
||||
err_name = exc.name, err_msg = exc.msg
|
||||
return
|
||||
var beaconBlocks =
|
||||
block:
|
||||
let blindedBlockFut =
|
||||
if vc.config.payloadBuilderEnable:
|
||||
vc.produceBlindedBlock(currentSlot, slot, randaoReveal, graffiti,
|
||||
validator)
|
||||
else:
|
||||
nil
|
||||
let normalBlockFut = vc.produceBlock(currentSlot, slot, randaoReveal,
|
||||
graffiti, validator)
|
||||
let blindedBlock =
|
||||
if isNil(blindedBlockFut):
|
||||
Opt.none(PreparedBlindedBeaconBlock)
|
||||
else:
|
||||
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)
|
||||
# TODO: signingRoot is recomputed in getBlockSignature just after
|
||||
let signingRoot = compute_block_signing_root(fork, genesisRoot, slot,
|
||||
blockRoot)
|
||||
let notSlashable = vc.attachedValidators[]
|
||||
.slashingProtection
|
||||
.registerBlock(vindex, validator.pubkey, slot, signingRoot)
|
||||
let normalBlock =
|
||||
if blindedBlock.isNone():
|
||||
try:
|
||||
await normalBlockFut
|
||||
except CancelledError as exc:
|
||||
raise exc
|
||||
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():
|
||||
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
|
||||
if blindedBlock.isNone() and normalBlock.isNone():
|
||||
return
|
||||
|
||||
debug "Sending block",
|
||||
blockRoot = shortLog(blockRoot), blck = shortLog(beaconBlock),
|
||||
signature = shortLog(signature), validator = shortLog(validator)
|
||||
(blindedBlock: blindedBlock, normalBlock: normalBlock)
|
||||
|
||||
let res =
|
||||
try:
|
||||
let signedBlock = ForkedSignedBeaconBlock.init(beaconBlock, blockRoot,
|
||||
signature)
|
||||
await vc.publishBlock(signedBlock, ApiStrategyKind.First)
|
||||
except ValidatorApiError:
|
||||
error "Unable to publish block",
|
||||
blockRoot = shortLog(blockRoot),
|
||||
blck = shortLog(beaconBlock),
|
||||
signature = shortLog(signature),
|
||||
validator = shortLog(validator),
|
||||
validator_index = validator.index.get(),
|
||||
wall_slot = currentSlot
|
||||
return
|
||||
except CancelledError as exc:
|
||||
debug "Publishing block processing was interrupted"
|
||||
raise exc
|
||||
except CatchableError as exc:
|
||||
error "An unexpected error occurred while publishing block",
|
||||
err_name = exc.name, err_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", blockRoot = shortLog(blockRoot),
|
||||
blck = shortLog(beaconBlock), signature = shortLog(signature),
|
||||
validator = shortLog(validator)
|
||||
if beaconBlocks.blindedBlock.isSome():
|
||||
let
|
||||
preparedBlock = beaconBlocks.blindedBlock.get()
|
||||
signingRoot = compute_block_signing_root(fork, genesisRoot, slot,
|
||||
preparedBlock.blockRoot)
|
||||
notSlashable = vc.attachedValidators[]
|
||||
.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 blinded block proposal using remote signer",
|
||||
error_msg = res.error()
|
||||
return
|
||||
res.get()
|
||||
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:
|
||||
warn "Block was not accepted by beacon node",
|
||||
blockRoot = shortLog(blockRoot),
|
||||
blck = shortLog(beaconBlock),
|
||||
signature = shortLog(signature),
|
||||
validator = shortLog(validator),
|
||||
wall_slot = currentSlot
|
||||
warn "Slashing protection activated for blinded block proposal"
|
||||
else:
|
||||
warn "Slashing protection activated for block proposal",
|
||||
blockRoot = shortLog(blockRoot), blck = shortLog(beaconBlock),
|
||||
signingRoot = shortLog(signingRoot),
|
||||
validator = shortLog(validator),
|
||||
wall_slot = currentSlot,
|
||||
existingProposal = notSlashable.error
|
||||
let
|
||||
preparedBlock = beaconBlocks.normalBlock.get()
|
||||
signingRoot = compute_block_signing_root(fork, genesisRoot, slot,
|
||||
preparedBlock.blockRoot)
|
||||
notSlashable = vc.attachedValidators[]
|
||||
.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,
|
||||
proposerKey: ValidatorPubKey) {.async.} =
|
||||
|
@ -291,4 +448,3 @@ proc waitForBlockPublished*(vc: ValidatorClientRef, slot: Slot) {.async.} =
|
|||
pending.add(future.cancelAndWait())
|
||||
await allFutures(pending)
|
||||
raise exc
|
||||
|
||||
|
|
|
@ -326,7 +326,8 @@ proc signData(v: AttachedValidator,
|
|||
proc getBlockSignature*(v: AttachedValidator, fork: Fork,
|
||||
genesis_validators_root: Eth2Digest, slot: Slot,
|
||||
block_root: Eth2Digest,
|
||||
blck: ForkedBeaconBlock | BlindedBeaconBlock
|
||||
blck: ForkedBeaconBlock | ForkedBlindedBeaconBlock |
|
||||
BlindedBeaconBlock
|
||||
): Future[SignatureResult] {.async.} =
|
||||
return
|
||||
case v.kind
|
||||
|
@ -336,7 +337,29 @@ proc getBlockSignature*(v: AttachedValidator, fork: Fork,
|
|||
fork, genesis_validators_root, slot, block_root,
|
||||
v.data.privateKey).toValidatorSig())
|
||||
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(
|
||||
fork, genesis_validators_root,
|
||||
Web3SignerForkedBeaconBlock(
|
||||
|
@ -519,4 +542,3 @@ proc getBuilderSignature*(v: AttachedValidator, fork: Fork,
|
|||
let request = Web3SignerRequest.init(
|
||||
fork, ZERO_HASH, validatorRegistration)
|
||||
await v.signData(request)
|
||||
|
||||
|
|
Loading…
Reference in New Issue