Initial commit.

Add publishBlockV2 client call implementation.
Add support of 415 error.
This commit is contained in:
cheatfate 2024-05-03 07:29:37 +03:00
parent 484f48953b
commit 86af562deb
No known key found for this signature in database
GPG Key ID: 46ADD633A7201F95
4 changed files with 202 additions and 28 deletions

View File

@ -165,44 +165,80 @@ proc publishSszBlock*(
extraHeaders = @[("eth-consensus-version", consensus)])
return resp
proc publishBlockV2Plain(body: phase0.SignedBeaconBlock): RestPlainResponse {.
rest, endpoint: "/eth/v2/beacon/blocks",
meth: MethodPost.}
proc publishBlockV2Plain(
broadcast_validation: Option[BroadcastValidationType],
body: phase0.SignedBeaconBlock
): RestPlainResponse {.
rest, endpoint: "/eth/v2/beacon/blocks", meth: MethodPost.}
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2
proc publishBlockV2Plain(body: altair.SignedBeaconBlock): RestPlainResponse {.
rest, endpoint: "/eth/v2/beacon/blocks",
meth: MethodPost.}
proc publishBlockV2Plain(
broadcast_validation: Option[BroadcastValidationType],
body: altair.SignedBeaconBlock
): RestPlainResponse {.
rest, endpoint: "/eth/v2/beacon/blocks", meth: MethodPost.}
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2
proc publishBlockV2Plain(body: bellatrix.SignedBeaconBlock): RestPlainResponse {.
rest, endpoint: "/eth/v2/beacon/blocks",
meth: MethodPost.}
proc publishBlockV2Plain(
broadcast_validation: Option[BroadcastValidationType],
body: bellatrix.SignedBeaconBlock
): RestPlainResponse {.
rest, endpoint: "/eth/v2/beacon/blocks", meth: MethodPost.}
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2
proc publishBlockV2Plain(body: capella.SignedBeaconBlock): RestPlainResponse {.
rest, endpoint: "/eth/v2/beacon/blocks",
meth: MethodPost.}
proc publishBlockV2Plain(
broadcast_validation: Option[BroadcastValidationType],
body: capella.SignedBeaconBlock
): RestPlainResponse {.
rest, endpoint: "/eth/v2/beacon/blocks", meth: MethodPost.}
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2
proc publishBlockV2Plain(body: DenebSignedBlockContents): RestPlainResponse {.
rest, endpoint: "/eth/v2/beacon/blocks",
meth: MethodPost.}
proc publishBlockV2Plain(
broadcast_validation: Option[BroadcastValidationType],
body: DenebSignedBlockContents
): RestPlainResponse {.
rest, endpoint: "/eth/v2/beacon/blocks", meth: MethodPost.}
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2
proc publishBlockV2Plain(
broadcast_validation: Option[BroadcastValidationType],
body: ElectraSignedBlockContents
): RestPlainResponse {.
rest, endpoint: "/eth/v2/beacon/blocks", meth: MethodPost.}
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2
proc publishBlockV2*(
client: RestClientRef,
blck: phase0.SignedBeaconBlock | altair.SignedBeaconBlock |
bellatrix.SignedBeaconBlock | capella.SignedBeaconBlock |
deneb.SignedBeaconBlock
): Future[RestPlainResponse] {.async} =
client: RestClientRef,
blck: phase0.SignedBeaconBlock | altair.SignedBeaconBlock |
bellatrix.SignedBeaconBlock | capella.SignedBeaconBlock |
DenebSignedBlockContents | ElectraSignedBlockContents,
validation: Opt[BroadcastValidationType] =
Opt.none(BroadcastValidationType),
contentType: Opt[MediaType] = Opt.none(MediaType)
): Future[RestPlainResponse] {.
async: (raises: [CancelledError, RestEncodingError, RestDnsResolveError,
RestCommunicationError])} =
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2
let
consensus = typeof(blck).kind.toString()
resp = await client.publishBlockV2Plain(
blck, extraHeaders = @[
("eth-consensus-version", consensus),
("broadcast_validation", "gossip")])
return resp
consensus =
when blck is DenebSignedBlockContents:
toString(ConsensusFork.Deneb)
elif blck is ElectraSignedBlockContents:
toString(ConsensusFork.Electra)
else:
typeof(blck).kind.toString()
mediaType = contentType.valueOr:
ApplicationJsonMediaType
broadcastValidation =
if validation.isSome():
some[BroadcastValidationType](validation.get())
else:
none[BroadcastValidationType]()
await client.publishBlockV2Plain(
broadcastValidation,
blck,
restContentType = $mediaType,
extraHeaders = @[("eth-consensus-version", consensus)])
proc publishBlindedBlock*(body: phase0.SignedBeaconBlock): RestPlainResponse {.
rest, endpoint: "/eth/v1/beacon/blinded_blocks",

View File

@ -25,6 +25,8 @@ const
ResponseECNotInSyncError* = "Execution client not in sync"
ResponseNotImplementedError =
"Received endpoint not implemented error response"
ResponseUnsupportedContentTypeError =
"Server does not support provided content type"
type
ApiResponse*[T] = Result[T, string]
@ -761,6 +763,11 @@ template handle404(): untyped {.dirty.} =
node.updateStatus(RestBeaconNodeStatus.Incompatible, failure)
failures.add(failure)
template handle415(): untyped {.dirty.} =
let failure = ApiNodeFailure.init(ApiFailure.NotSupportedContentType,
RequestName, strategy, node, response.status, response.getErrorMessage())
failures.add(failure)
template handle500(): untyped {.dirty.} =
let failure = ApiNodeFailure.init(ApiFailure.Internal, RequestName,
strategy, node, response.status, response.getErrorMessage())
@ -2284,6 +2291,9 @@ proc publishBlock*(
of 400:
handle400()
ApiResponse[bool].err(ResponseInvalidError)
of 415:
handle415()
ApiResponse[bool].err(ResponseUnsupportedContentTypeError)
of 500:
handle500()
ApiResponse[bool].err(ResponseInternalError)
@ -2333,6 +2343,127 @@ proc publishBlock*(
of 400:
handle400()
false
of 415:
handle415()
false
of 500:
handle500()
false
of 503:
handle503()
false
else:
handleUnexpectedCode()
false
raise (ref ValidatorApiError)(
msg: "Failed to publish block", data: failures)
proc publishBlockV2*(
vc: ValidatorClientRef,
data: RestPublishedSignedBlockContents,
validation: BroadcastValidationType,
contentType: MediaType,
strategy: ApiStrategyKind
): Future[bool] {.async.} =
const
RequestName = "publishBlockV2"
BlockBroadcasted = "Block not passed validation, but still published"
var failures: seq[ApiNodeFailure]
case strategy
of ApiStrategyKind.First, ApiStrategyKind.Best:
let res = block:
vc.firstSuccessParallel(RestPlainResponse,
bool,
SlotDuration,
ViableNodeStatus,
{BeaconNodeRole.BlockProposalPublish}):
case data.kind
of ConsensusFork.Phase0:
publishBlockV2(it, data.phase0Data)
of ConsensusFork.Altair:
publishBlockV2(it, data.altairData)
of ConsensusFork.Bellatrix:
publishBlockV2(it, data.bellatrixData)
of ConsensusFork.Capella:
publishBlockV2(it, data.capellaData)
of ConsensusFork.Deneb:
publishBlockV2(it, data.denebData)
of ConsensusFork.Electra:
publishBlockV2(it, data.electraData)
do:
if apiResponse.isErr():
handleCommunicationError()
ApiResponse[bool].err(apiResponse.error)
else:
let response = apiResponse.get()
case response.status:
of 200:
ApiResponse[bool].ok(true)
of 202:
debug BlockBroadcasted, node = node,
blck = shortLog(ForkedSignedBeaconBlock.init(data))
ApiResponse[bool].ok(true)
of 400:
handle400()
ApiResponse[bool].err(ResponseInvalidError)
of 415:
handle415()
ApiResponse[bool].err(ResponseUnsupportedContentTypeError)
of 500:
handle500()
ApiResponse[bool].err(ResponseInternalError)
of 503:
handle503()
ApiResponse[bool].err(ResponseNoSyncError)
else:
handleUnexpectedCode()
ApiResponse[bool].err(ResponseUnexpectedError)
if res.isErr():
raise (ref ValidatorApiError)(msg: res.error, data: failures)
return res.get()
of ApiStrategyKind.Priority:
vc.firstSuccessSequential(RestPlainResponse,
SlotDuration,
ViableNodeStatus,
{BeaconNodeRole.BlockProposalPublish}):
case data.kind
of ConsensusFork.Phase0:
publishBlock(it, data.phase0Data)
of ConsensusFork.Altair:
publishBlock(it, data.altairData)
of ConsensusFork.Bellatrix:
publishBlock(it, data.bellatrixData)
of ConsensusFork.Capella:
publishBlock(it, data.capellaData)
of ConsensusFork.Deneb:
publishBlock(it, data.denebData)
of ConsensusFork.Electra:
publishBlock(it, data.electraData)
do:
if apiResponse.isErr():
handleCommunicationError()
false
else:
let response = apiResponse.get()
case response.status:
of 200:
return true
of 202:
debug BlockBroadcasted, node = node,
blck = shortLog(ForkedSignedBeaconBlock.init(data))
return true
of 400:
handle400()
false
of 415:
handle415()
false
of 500:
handle500()
false

View File

@ -393,7 +393,12 @@ proc publishBlockV3(vc: ValidatorClientRef, currentSlot, slot: Slot,
res =
try:
debug "Sending block"
await vc.publishBlock(signedBlockContents, ApiStrategyKind.First)
await vc.publishBlockV2(
signedBlockContents,
BroadcastValidationType.Gossip,
ApplicationJsonMediaType,
ApiStrategyKind.First
)
except ValidatorApiError as exc:
warn "Unable to publish block", reason = exc.getFailureReason()
return false

View File

@ -242,7 +242,8 @@ type
ApiFailure* {.pure.} = enum
Communication, Invalid, NotFound, OptSynced, NotSynced, Internal,
NotImplemented, UnexpectedCode, UnexpectedResponse, NoError
NotImplemented, UnexpectedCode, UnexpectedResponse, NotSupportedContentType,
NoError
ApiNodeFailure* = object
node*: BeaconNodeServerRef
@ -377,6 +378,7 @@ proc `$`*(failure: ApiFailure): string =
of ApiFailure.UnexpectedCode: "unexpected-code"
of ApiFailure.UnexpectedResponse: "unexpected-data"
of ApiFailure.NoError: "status-update"
of ApiFailure.NotSupportedContentType: "not-supported-content-type"
proc getNodeCounts*(vc: ValidatorClientRef): BeaconNodesCounters =
var res = BeaconNodesCounters()