Add Deneb beacon API types (#5060)
* Add Deneb beacon API types - Introduce the `ProduceBlockResponseV2` type for handling responses to `GET /eth/v2/validator/blocks/{slot}` (https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Validator/produceBlockV2) - Introduce the `RestPublishedSignedBlockContents` type for handling the request body to `POST /eth/v1/beacon/blocks` (https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/ValidatorRequiredApi/publishBlock) And a few other small changes towards Deneb block production support. * Address review feedback * Post-rebase fixes
This commit is contained in:
parent
4da9517f25
commit
0f8866d672
|
@ -89,9 +89,6 @@ const
|
|||
# https://github.com/ethereum/execution-apis/blob/v1.0.0-beta.3/src/engine/shanghai.md#request-2
|
||||
GETPAYLOAD_TIMEOUT = 1.seconds
|
||||
|
||||
# https://github.com/ethereum/execution-apis/blob/v1.0.0-beta.3/src/engine/experimental/blob-extension.md#request-2
|
||||
GETBLOBS_TIMEOUT = 1.seconds
|
||||
|
||||
connectionStateChangeHysteresisThreshold = 15
|
||||
## How many unsuccesful/successful requests we must see
|
||||
## before declaring the connection as degraded/restored
|
||||
|
@ -909,12 +906,7 @@ proc getPayload*(m: ELManager,
|
|||
randomData, suggestedFeeRecipient, engineApiWithdrawals)
|
||||
|
||||
let
|
||||
timeout = when PayloadType is deneb.ExecutionPayloadForSigning:
|
||||
# TODO We should follow the spec and track the timeouts of
|
||||
# the individual engine API calls inside `getPayloadFromSingleEL`.
|
||||
GETPAYLOAD_TIMEOUT + GETBLOBS_TIMEOUT
|
||||
else:
|
||||
GETPAYLOAD_TIMEOUT
|
||||
timeout = GETPAYLOAD_TIMEOUT
|
||||
deadline = sleepAsync(timeout)
|
||||
requests = m.elConnections.mapIt(it.getPayloadFromSingleEL(
|
||||
EngineApiResponseType(PayloadType),
|
||||
|
|
|
@ -837,13 +837,13 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
body = contentBody.get()
|
||||
version = request.headers.getString("eth-consensus-version")
|
||||
var
|
||||
restBlock = decodeBody(RestPublishedSignedBeaconBlock, body,
|
||||
restBlock = decodeBody(RestPublishedSignedBlockContents, body,
|
||||
version).valueOr:
|
||||
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError,
|
||||
$error)
|
||||
forked = ForkedSignedBeaconBlock(restBlock)
|
||||
forked = ForkedSignedBeaconBlock.init(restBlock)
|
||||
|
||||
if forked.kind != node.dag.cfg.consensusForkAtEpoch(
|
||||
if restBlock.kind != node.dag.cfg.consensusForkAtEpoch(
|
||||
getForkedBlockField(forked, slot).epoch):
|
||||
doAssert strictVerification notin node.dag.updateFlags
|
||||
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError)
|
||||
|
@ -861,6 +861,9 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
|
||||
return RestApiResponse.jsonMsgResponse(BlockValidationSuccess)
|
||||
|
||||
# TODO
|
||||
# Add POST /eth/v2/beacon/blocks
|
||||
|
||||
# https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlindedBlock
|
||||
# https://github.com/ethereum/beacon-APIs/blob/v2.4.0/apis/beacon/blocks/blinded_blocks.yaml
|
||||
router.api(MethodPost, "/eth/v1/beacon/blinded_blocks") do (
|
||||
|
|
|
@ -162,7 +162,7 @@ type
|
|||
|
||||
RestBlockTypes* = phase0.BeaconBlock | altair.BeaconBlock |
|
||||
bellatrix.BeaconBlock | capella.BeaconBlock |
|
||||
deneb.BeaconBlock | bellatrix_mev.BlindedBeaconBlock |
|
||||
DenebBlockContents | bellatrix_mev.BlindedBeaconBlock |
|
||||
capella_mev.BlindedBeaconBlock
|
||||
|
||||
{.push raises: [].}
|
||||
|
@ -1037,6 +1037,82 @@ proc readValue*[BlockType: ForkedBeaconBlock](
|
|||
reader.raiseUnexpectedValue("Incorrect deneb block format")
|
||||
value = ForkedBeaconBlock.init(res.get()).BlockType
|
||||
|
||||
proc readValue*[BlockType: ProduceBlockResponseV2](
|
||||
reader: var JsonReader[RestJson],
|
||||
value: var BlockType) {.raises: [IOError, SerializationError, Defect].} =
|
||||
var
|
||||
version: Option[ConsensusFork]
|
||||
data: Option[JsonString]
|
||||
|
||||
prepareForkedBlockReading(reader, version, data, "ProduceBlockResponseV2")
|
||||
|
||||
case version.get():
|
||||
of ConsensusFork.Phase0:
|
||||
let res =
|
||||
try:
|
||||
some(RestJson.decode(string(data.get()),
|
||||
phase0.BeaconBlock,
|
||||
requireAllFields = true,
|
||||
allowUnknownFields = true))
|
||||
except SerializationError:
|
||||
none[phase0.BeaconBlock]()
|
||||
if res.isNone():
|
||||
reader.raiseUnexpectedValue("Incorrect phase0 block format")
|
||||
value = ProduceBlockResponseV2(kind: ConsensusFork.Phase0,
|
||||
phase0Data: res.get())
|
||||
of ConsensusFork.Altair:
|
||||
let res =
|
||||
try:
|
||||
some(RestJson.decode(string(data.get()),
|
||||
altair.BeaconBlock,
|
||||
requireAllFields = true,
|
||||
allowUnknownFields = true))
|
||||
except SerializationError:
|
||||
none[altair.BeaconBlock]()
|
||||
if res.isNone():
|
||||
reader.raiseUnexpectedValue("Incorrect altair block format")
|
||||
value = ProduceBlockResponseV2(kind: ConsensusFork.Altair,
|
||||
altairData: res.get())
|
||||
of ConsensusFork.Bellatrix:
|
||||
let res =
|
||||
try:
|
||||
some(RestJson.decode(string(data.get()),
|
||||
bellatrix.BeaconBlock,
|
||||
requireAllFields = true,
|
||||
allowUnknownFields = true))
|
||||
except SerializationError:
|
||||
none[bellatrix.BeaconBlock]()
|
||||
if res.isNone():
|
||||
reader.raiseUnexpectedValue("Incorrect bellatrix block format")
|
||||
value = ProduceBlockResponseV2(kind: ConsensusFork.Bellatrix,
|
||||
bellatrixData: res.get())
|
||||
of ConsensusFork.Capella:
|
||||
let res =
|
||||
try:
|
||||
some(RestJson.decode(string(data.get()),
|
||||
capella.BeaconBlock,
|
||||
requireAllFields = true,
|
||||
allowUnknownFields = true))
|
||||
except SerializationError:
|
||||
none[capella.BeaconBlock]()
|
||||
if res.isNone():
|
||||
reader.raiseUnexpectedValue("Incorrect capella block format")
|
||||
value = ProduceBlockResponseV2(kind: ConsensusFork.Capella,
|
||||
capellaData: res.get())
|
||||
of ConsensusFork.Deneb:
|
||||
let res =
|
||||
try:
|
||||
some(RestJson.decode(string(data.get()),
|
||||
DenebBlockContents,
|
||||
requireAllFields = true,
|
||||
allowUnknownFields = true))
|
||||
except SerializationError:
|
||||
none[DenebBlockContents]()
|
||||
if res.isNone():
|
||||
reader.raiseUnexpectedValue("Incorrect deneb block format")
|
||||
value = ProduceBlockResponseV2(kind: ConsensusFork.Deneb,
|
||||
denebData: res.get())
|
||||
|
||||
proc readValue*[BlockType: ForkedBlindedBeaconBlock](
|
||||
reader: var JsonReader[RestJson],
|
||||
value: var BlockType
|
||||
|
@ -1268,6 +1344,7 @@ proc readValue*(reader: var JsonReader[RestJson],
|
|||
sync_aggregate: Option[SyncAggregate]
|
||||
execution_payload: Option[RestExecutionPayload]
|
||||
bls_to_execution_changes: Option[SignedBLSToExecutionChangeList]
|
||||
blob_kzg_commitments: Option[KzgCommitments]
|
||||
|
||||
for fieldName in readObjectFields(reader):
|
||||
case fieldName
|
||||
|
@ -1332,6 +1409,11 @@ proc readValue*(reader: var JsonReader[RestJson],
|
|||
reader.raiseUnexpectedField("Multiple `bls_to_execution_changes` fields found",
|
||||
"RestPublishedBeaconBlockBody")
|
||||
bls_to_execution_changes = some(reader.readValue(SignedBLSToExecutionChangeList))
|
||||
of "blob_kzg_commitments_changes":
|
||||
if blob_kzg_commitments.isSome():
|
||||
reader.raiseUnexpectedField("Multiple `blob_kzg_commitments` fields found",
|
||||
"RestPublishedBeaconBlockBody")
|
||||
blob_kzg_commitments = some(reader.readValue(KzgCommitments))
|
||||
else:
|
||||
unrecognizedFieldWarning()
|
||||
|
||||
|
@ -1354,6 +1436,10 @@ proc readValue*(reader: var JsonReader[RestJson],
|
|||
|
||||
let bodyKind =
|
||||
if execution_payload.isSome() and
|
||||
execution_payload.get().data_gas_used.isSome() and
|
||||
blob_kzg_commitments.isSome():
|
||||
ConsensusFork.Deneb
|
||||
elif execution_payload.isSome() and
|
||||
execution_payload.get().withdrawals.isSome() and
|
||||
bls_to_execution_changes.isSome() and
|
||||
sync_aggregate.isSome():
|
||||
|
@ -1624,6 +1710,129 @@ proc readValue*(reader: var JsonReader[RestJson],
|
|||
)
|
||||
)
|
||||
|
||||
proc readValue*(reader: var JsonReader[RestJson],
|
||||
value: var RestPublishedSignedBlockContents) {.
|
||||
raises: [IOError, SerializationError, Defect].} =
|
||||
var signature: Option[ValidatorSig]
|
||||
var message: Option[RestPublishedBeaconBlock]
|
||||
var signed_message: Option[RestPublishedSignedBeaconBlock]
|
||||
var signed_block_data: Option[JsonString]
|
||||
var signed_blob_sidecars: Option[List[SignedBlobSidecar,
|
||||
Limit MAX_BLOBS_PER_BLOCK]]
|
||||
|
||||
# Pre-Deneb, there were always the same two top-level fields
|
||||
# ('signature' and 'message'). For Deneb, there's a different set of
|
||||
# a top-level fields: 'signed_block' 'signed_blob_sidecars'. The
|
||||
# former is the same as the pre-Deneb object.
|
||||
for fieldName in readObjectFields(reader):
|
||||
case fieldName
|
||||
of "message":
|
||||
if message.isSome():
|
||||
reader.raiseUnexpectedField("Multiple `message` fields found",
|
||||
"RestPublishedSignedBlockContents")
|
||||
message = some(reader.readValue(RestPublishedBeaconBlock))
|
||||
of "signature":
|
||||
if signature.isSome():
|
||||
reader.raiseUnexpectedField("Multiple `signature` fields found",
|
||||
"RestPublishedSignedBlockContents")
|
||||
signature = some(reader.readValue(ValidatorSig))
|
||||
of "signed_block":
|
||||
if signed_block_data.isSome():
|
||||
reader.raiseUnexpectedField("Multiple `signed_block` fields found",
|
||||
"RestPublishedSignedBlockContents")
|
||||
signed_block_data = some(reader.readValue(JsonString))
|
||||
if message.isSome() or signature.isSome():
|
||||
reader.raiseUnexpectedField(
|
||||
"Found `signed_block` field alongside message or signature fields",
|
||||
"RestPublishedSignedBlockContents")
|
||||
signed_message =
|
||||
try:
|
||||
some(RestJson.decode(string(signed_block_data.get()),
|
||||
RestPublishedSignedBeaconBlock,
|
||||
requireAllFields = true,
|
||||
allowUnknownFields = true))
|
||||
except SerializationError:
|
||||
none[RestPublishedSignedBeaconBlock]()
|
||||
if signed_message.isNone():
|
||||
reader.raiseUnexpectedValue("Incorrect signed_block format")
|
||||
let blck = ForkedSignedBeaconBlock(signed_message.get())
|
||||
message = some(RestPublishedBeaconBlock(
|
||||
case blck.kind
|
||||
of ConsensusFork.Phase0, ConsensusFork.Altair, ConsensusFork.Bellatrix,
|
||||
ConsensusFork.Capella:
|
||||
reader.raiseUnexpectedValue("Incorrect signed_block format")
|
||||
of ConsensusFork.Deneb:
|
||||
ForkedBeaconBlock.init(blck.denebData.message)
|
||||
))
|
||||
|
||||
signature = some(forks.signature(
|
||||
ForkedSignedBeaconBlock(signed_message.get())))
|
||||
of "signed_blob_sidecars":
|
||||
if signed_blob_sidecars.isSome():
|
||||
reader.raiseUnexpectedField(
|
||||
"Multiple `signed_blob_sidecars` fields found",
|
||||
"RestPublishedSignedBlockContents")
|
||||
if signature.isSome():
|
||||
reader.raiseUnexpectedField(
|
||||
"Found `signed_block` field alongside message or signature fields",
|
||||
"RestPublishedSignedBlockContents")
|
||||
signed_blob_sidecars = some(reader.readValue(
|
||||
List[SignedBlobSidecar, Limit MAX_BLOBS_PER_BLOCK]))
|
||||
|
||||
else:
|
||||
unrecognizedFieldWarning()
|
||||
|
||||
if signature.isNone():
|
||||
reader.raiseUnexpectedValue("Field `signature` is missing")
|
||||
if message.isNone():
|
||||
reader.raiseUnexpectedValue("Field `message` is missing")
|
||||
|
||||
let blck = ForkedBeaconBlock(message.get())
|
||||
case blck.kind
|
||||
of ConsensusFork.Phase0:
|
||||
value = RestPublishedSignedBlockContents(
|
||||
kind: ConsensusFork.Phase0,
|
||||
phase0Data: phase0.SignedBeaconBlock(
|
||||
message: blck.phase0Data,
|
||||
signature: signature.get()
|
||||
)
|
||||
)
|
||||
of ConsensusFork.Altair:
|
||||
value = RestPublishedSignedBlockContents(
|
||||
kind: ConsensusFork.Altair,
|
||||
altairData: altair.SignedBeaconBlock(
|
||||
message: blck.altairData,
|
||||
signature: signature.get()
|
||||
)
|
||||
)
|
||||
of ConsensusFork.Bellatrix:
|
||||
value = RestPublishedSignedBlockContents(
|
||||
kind: ConsensusFork.Bellatrix,
|
||||
bellatrixData: bellatrix.SignedBeaconBlock(
|
||||
message: blck.bellatrixData,
|
||||
signature: signature.get()
|
||||
)
|
||||
)
|
||||
of ConsensusFork.Capella:
|
||||
value = RestPublishedSignedBlockContents(
|
||||
kind: ConsensusFork.Capella,
|
||||
capellaData: capella.SignedBeaconBlock(
|
||||
message: blck.capellaData,
|
||||
signature: signature.get()
|
||||
)
|
||||
)
|
||||
of ConsensusFork.Deneb:
|
||||
value = RestPublishedSignedBlockContents(
|
||||
kind: ConsensusFork.Deneb,
|
||||
denebData: DenebSignedBlockContents(
|
||||
signed_block: deneb.SignedBeaconBlock(
|
||||
message: blck.denebData,
|
||||
signature: signature.get()
|
||||
),
|
||||
signed_blob_sidecars: signed_blob_sidecars.get()
|
||||
)
|
||||
)
|
||||
|
||||
## ForkedSignedBeaconBlock
|
||||
proc readValue*(reader: var JsonReader[RestJson],
|
||||
value: var ForkedSignedBeaconBlock) {.
|
||||
|
@ -2859,6 +3068,81 @@ proc decodeBody*(
|
|||
else:
|
||||
return err("Unsupported or invalid content media type")
|
||||
|
||||
proc decodeBody*(
|
||||
t: typedesc[RestPublishedSignedBlockContents],
|
||||
body: ContentBody,
|
||||
version: string
|
||||
): Result[RestPublishedSignedBlockContents, cstring] =
|
||||
if body.contentType == ApplicationJsonMediaType:
|
||||
let data =
|
||||
try:
|
||||
RestJson.decode(body.data, RestPublishedSignedBlockContents,
|
||||
requireAllFields = true,
|
||||
allowUnknownFields = true)
|
||||
except SerializationError as exc:
|
||||
debug "Failed to deserialize REST JSON data",
|
||||
err = exc.formatMsg("<data>"),
|
||||
data = string.fromBytes(body.data)
|
||||
return err("Unable to deserialize data")
|
||||
except CatchableError:
|
||||
return err("Unexpected deserialization error")
|
||||
ok(data)
|
||||
elif body.contentType == OctetStreamMediaType:
|
||||
let consensusFork = ? ConsensusFork.decodeString(version)
|
||||
case consensusFork
|
||||
of ConsensusFork.Phase0:
|
||||
let blck =
|
||||
try:
|
||||
SSZ.decode(body.data, phase0.SignedBeaconBlock)
|
||||
except SerializationError:
|
||||
return err("Unable to deserialize data")
|
||||
except CatchableError:
|
||||
return err("Unexpected deserialization error")
|
||||
ok(RestPublishedSignedBlockContents(
|
||||
kind: ConsensusFork.Phase0, phase0Data: blck))
|
||||
of ConsensusFork.Altair:
|
||||
let blck =
|
||||
try:
|
||||
SSZ.decode(body.data, altair.SignedBeaconBlock)
|
||||
except SerializationError:
|
||||
return err("Unable to deserialize data")
|
||||
except CatchableError:
|
||||
return err("Unexpected deserialization error")
|
||||
ok(RestPublishedSignedBlockContents(
|
||||
kind: ConsensusFork.Altair, altairData: blck))
|
||||
of ConsensusFork.Bellatrix:
|
||||
let blck =
|
||||
try:
|
||||
SSZ.decode(body.data, bellatrix.SignedBeaconBlock)
|
||||
except SerializationError:
|
||||
return err("Unable to deserialize data")
|
||||
except CatchableError:
|
||||
return err("Unexpected deserialization error")
|
||||
ok(RestPublishedSignedBlockContents(
|
||||
kind: ConsensusFork.Bellatrix, bellatrixData: blck))
|
||||
of ConsensusFork.Capella:
|
||||
let blck =
|
||||
try:
|
||||
SSZ.decode(body.data, capella.SignedBeaconBlock)
|
||||
except SerializationError:
|
||||
return err("Unable to deserialize data")
|
||||
except CatchableError:
|
||||
return err("Unexpected deserialization error")
|
||||
ok(RestPublishedSignedBlockContents(
|
||||
kind: ConsensusFork.Capella, capellaData: blck))
|
||||
of ConsensusFork.Deneb:
|
||||
let blckContents =
|
||||
try:
|
||||
SSZ.decode(body.data, DenebSignedBlockContents)
|
||||
except SerializationError:
|
||||
return err("Unable to deserialize data")
|
||||
except CatchableError:
|
||||
return err("Unexpected deserialization error")
|
||||
ok(RestPublishedSignedBlockContents(
|
||||
kind: ConsensusFork.Deneb, denebData: blckContents))
|
||||
else:
|
||||
return err("Unsupported or invalid content media type")
|
||||
|
||||
proc decodeBody*[T](t: typedesc[T],
|
||||
body: ContentBody): Result[T, cstring] =
|
||||
if body.contentType != ApplicationJsonMediaType:
|
||||
|
@ -3010,20 +3294,25 @@ proc decodeBytes*[T: DecodeConsensysTypes](
|
|||
return err("Invalid or Unsupported consensus version")
|
||||
case fork
|
||||
of ConsensusFork.Deneb:
|
||||
let blck = ? readSszResBytes(deneb.BeaconBlock, value)
|
||||
ok(ProduceBlockResponseV2(ForkedBeaconBlock.init(blck)))
|
||||
let blckContents = ? readSszResBytes(DenebBlockContents, value)
|
||||
ok(ProduceBlockResponseV2(kind: ConsensusFork.Deneb,
|
||||
denebData: blckContents))
|
||||
of ConsensusFork.Capella:
|
||||
let blck = ? readSszResBytes(capella.BeaconBlock, value)
|
||||
ok(ProduceBlockResponseV2(ForkedBeaconBlock.init(blck)))
|
||||
ok(ProduceBlockResponseV2(kind: ConsensusFork.Capella,
|
||||
capellaData: blck))
|
||||
of ConsensusFork.Bellatrix:
|
||||
let blck = ? readSszResBytes(bellatrix.BeaconBlock, value)
|
||||
ok(ProduceBlockResponseV2(ForkedBeaconBlock.init(blck)))
|
||||
ok(ProduceBlockResponseV2(kind: ConsensusFork.Bellatrix,
|
||||
bellatrixData: blck))
|
||||
of ConsensusFork.Altair:
|
||||
let blck = ? readSszResBytes(altair.BeaconBlock, value)
|
||||
ok(ProduceBlockResponseV2(ForkedBeaconBlock.init(blck)))
|
||||
ok(ProduceBlockResponseV2(kind: ConsensusFork.Altair,
|
||||
altairData: blck))
|
||||
of ConsensusFork.Phase0:
|
||||
let blck = ? readSszResBytes(phase0.BeaconBlock, value)
|
||||
ok(ProduceBlockResponseV2(ForkedBeaconBlock.init(blck)))
|
||||
ok(ProduceBlockResponseV2(kind: ConsensusFork.Phase0,
|
||||
phase0Data: blck))
|
||||
elif t is ProduceBlindedBlockResponse:
|
||||
let fork = decodeEthConsensusVersion(consensusVersion).valueOr:
|
||||
return err("Invalid or Unsupported consensus version")
|
||||
|
|
|
@ -303,6 +303,9 @@ type
|
|||
transactions*: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD]
|
||||
withdrawals*: Option[List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD]]
|
||||
## [New in Capella]
|
||||
data_gas_used*: Option[uint64] ## [New in Deneb]
|
||||
excess_data_gas*: Option[uint64] ## [New in Deneb]
|
||||
|
||||
|
||||
PrepareBeaconProposer* = object
|
||||
validator_index*: ValidatorIndex
|
||||
|
@ -310,6 +313,18 @@ type
|
|||
|
||||
RestPublishedSignedBeaconBlock* = distinct ForkedSignedBeaconBlock
|
||||
|
||||
DenebSignedBlockContents* = object
|
||||
signed_block*: deneb.SignedBeaconBlock
|
||||
signed_blob_sidecars*: List[SignedBlobSidecar, Limit MAX_BLOBS_PER_BLOCK]
|
||||
|
||||
RestPublishedSignedBlockContents* = object
|
||||
case kind*: ConsensusFork
|
||||
of ConsensusFork.Phase0: phase0Data*: phase0.SignedBeaconBlock
|
||||
of ConsensusFork.Altair: altairData*: altair.SignedBeaconBlock
|
||||
of ConsensusFork.Bellatrix: bellatrixData*: bellatrix.SignedBeaconBlock
|
||||
of ConsensusFork.Capella: capellaData*: capella.SignedBeaconBlock
|
||||
of ConsensusFork.Deneb: denebData*: DenebSignedBlockContents
|
||||
|
||||
RestPublishedBeaconBlock* = distinct ForkedBeaconBlock
|
||||
|
||||
RestPublishedBeaconBlockBody* = object
|
||||
|
@ -320,6 +335,18 @@ type
|
|||
of ConsensusFork.Capella: capellaBody*: capella.BeaconBlockBody
|
||||
of ConsensusFork.Deneb: denebBody*: deneb.BeaconBlockBody
|
||||
|
||||
DenebBlockContents* = object
|
||||
`block`*: deneb.BeaconBlock
|
||||
blob_sidecars*: List[SignedBlobSidecar, Limit MAX_BLOBS_PER_BLOCK]
|
||||
|
||||
ProduceBlockResponseV2* = object
|
||||
case kind*: ConsensusFork
|
||||
of ConsensusFork.Phase0: phase0Data*: phase0.BeaconBlock
|
||||
of ConsensusFork.Altair: altairData*: altair.BeaconBlock
|
||||
of ConsensusFork.Bellatrix: bellatrixData*: bellatrix.BeaconBlock
|
||||
of ConsensusFork.Capella: capellaData*: capella.BeaconBlock
|
||||
of ConsensusFork.Deneb: denebData*: DenebBlockContents
|
||||
|
||||
RestSpec* = object
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-alpha.1/presets/mainnet/phase0.yaml
|
||||
MAX_COMMITTEES_PER_SLOT*: uint64
|
||||
|
@ -676,7 +703,6 @@ type
|
|||
GetVersionResponse* = DataEnclosedObject[RestNodeVersion]
|
||||
GetEpochSyncCommitteesResponse* = DataEnclosedObject[RestEpochSyncCommittee]
|
||||
ProduceAttestationDataResponse* = DataEnclosedObject[AttestationData]
|
||||
ProduceBlockResponseV2* = ForkedBeaconBlock
|
||||
ProduceBlindedBlockResponse* = ForkedBlindedBeaconBlock
|
||||
ProduceSyncCommitteeContributionResponse* = DataEnclosedObject[SyncCommitteeContribution]
|
||||
SubmitBlindedBlockResponseBellatrix* = DataEnclosedObject[bellatrix.ExecutionPayload]
|
||||
|
@ -720,6 +746,21 @@ type
|
|||
func `==`*(a, b: RestValidatorIndex): bool =
|
||||
uint64(a) == uint64(b)
|
||||
|
||||
func init*(T: type ForkedSignedBeaconBlock,
|
||||
contents: RestPublishedSignedBlockContents): T =
|
||||
return
|
||||
case contents.kind
|
||||
of ConsensusFork.Phase0:
|
||||
ForkedSignedBeaconBlock.init(contents.phase0Data)
|
||||
of ConsensusFork.Altair:
|
||||
ForkedSignedBeaconBlock.init(contents.altairData)
|
||||
of ConsensusFork.Bellatrix:
|
||||
ForkedSignedBeaconBlock.init(contents.bellatrixData)
|
||||
of ConsensusFork.Capella:
|
||||
ForkedSignedBeaconBlock.init(contents.capellaData)
|
||||
of ConsensusFork.Deneb:
|
||||
ForkedSignedBeaconBlock.init(contents.denebData.signed_block)
|
||||
|
||||
func init*(t: typedesc[StateIdent], v: StateIdentType): StateIdent =
|
||||
StateIdent(kind: StateQueryKind.Named, value: v)
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ proc produceBlock(
|
|||
wall_slot = currentSlot
|
||||
validator = shortLog(validator)
|
||||
let
|
||||
beaconBlock =
|
||||
produceBlockResponse =
|
||||
try:
|
||||
await vc.produceBlockV2(slot, randao_reveal, graffiti,
|
||||
ApiStrategyKind.Best)
|
||||
|
@ -55,9 +55,30 @@ proc produceBlock(
|
|||
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)
|
||||
case produceBlockResponse.kind
|
||||
of ConsensusFork.Phase0:
|
||||
let blck = produceBlockResponse.phase0Data
|
||||
return Opt.some(PreparedBeaconBlock(blockRoot: hash_tree_root(blck),
|
||||
data: ForkedBeaconBlock.init(blck)))
|
||||
of ConsensusFork.Altair:
|
||||
let blck = produceBlockResponse.altairData
|
||||
return Opt.some(PreparedBeaconBlock(blockRoot: hash_tree_root(blck),
|
||||
data: ForkedBeaconBlock.init(blck)))
|
||||
of ConsensusFork.Bellatrix:
|
||||
let blck = produceBlockResponse.bellatrixData
|
||||
return Opt.some(PreparedBeaconBlock(blockRoot: hash_tree_root(blck),
|
||||
data: ForkedBeaconBlock.init(blck)))
|
||||
of ConsensusFork.Capella:
|
||||
let blck = produceBlockResponse.capellaData
|
||||
return Opt.some(PreparedBeaconBlock(blockRoot: hash_tree_root(blck),
|
||||
data: ForkedBeaconBlock.init(blck)))
|
||||
of ConsensusFork.Deneb:
|
||||
debugRaiseAssert $denebImplementationMissing
|
||||
# TODO return blobs as well
|
||||
let blck = produceBlockResponse.denebData.`block`
|
||||
return Opt.some(PreparedBeaconBlock(blockRoot: hash_tree_root(blck),
|
||||
data: ForkedBeaconBlock.init(blck)))
|
||||
|
||||
return Opt.some(PreparedBeaconBlock(blockRoot: blockRoot, data: beaconBlock))
|
||||
|
||||
proc produceBlindedBlock(
|
||||
vc: ValidatorClientRef,
|
||||
|
|
|
@ -93,6 +93,9 @@ proc routeSignedBeaconBlock*(
|
|||
let res = validateBeaconBlock(
|
||||
router[].dag, router[].quarantine, blck, wallTime, {})
|
||||
|
||||
# TODO blob gossip validation
|
||||
debugRaiseAssert $denebImplementationMissing
|
||||
|
||||
if not res.isGoodForSending():
|
||||
warn "Block failed validation",
|
||||
blockRoot = shortLog(blck.root), blck = shortLog(blck.message),
|
||||
|
|
|
@ -64,7 +64,7 @@ const
|
|||
AltairBlock = "{\"version\":\"altair\",\"data\":{\"slot\":\"5297696\",\"proposer_index\":\"153094\",\"parent_root\":\"0xe6106533af9be918120ead7440a8006c7f123cc3cb7daf1f11d951864abea014\",\"state_root\":\"0xf86196d34500ca25d1f4e7431d4d52f6f85540bcaf97dd0d2ad9ecdb3eebcdf0\",\"body\":{\"randao_reveal\":\"0xa7efee3d5ddceb60810b23e3b5d39734696418f41dfd13a0851c7be7a72acbdceaa61e1db27513801917d72519d1c1040ccfed829faf06abe06d9964949554bf4369134b66de715ea49eb4fecf3e2b7e646f1764a1993e31e53dbc6557929c12\",\"eth1_data\":{\"deposit_root\":\"0x8ec87d7219a3c873fff3bfe206b4f923d1b471ce4ff9d6d6ecc162ef07825e14\",\"deposit_count\":\"259476\",\"block_hash\":\"0x877b6f8332c7397251ff3f0c5cecec105ff7d4cb78251b47f91fd15a86a565ab\"},\"graffiti\":\"\",\"proposer_slashings\":[],\"attester_slashings\":[],\"attestations\":[],\"deposits\":[],\"voluntary_exits\":[],\"sync_aggregate\":{\"sync_committee_bits\":\"0x733dfda7f5ffde5ade73367fcbf7fffeef7fe43777ffdffab9dbad6f7eed5fff9bfec4affdefbfaddf35bf5efbff9ffff9dfd7dbf97fbfcdfaddfeffbf95f75f\",\"sync_committee_signature\":\"0x81fdf76e797f81b0116a1c1ae5200b613c8041115223cd89e8bd5477aab13de6097a9ebf42b130c59527bbb4c96811b809353a17c717549f82d4bd336068ef0b99b1feebd4d2432a69fa77fac12b78f1fcc9d7b59edbeb381adf10b15bc4a520\"}}}}"
|
||||
BellatrixBlock = "{\"version\":\"bellatrix\",\"data\":{\"slot\":\"5297696\",\"proposer_index\":\"153094\",\"parent_root\":\"0xe6106533af9be918120ead7440a8006c7f123cc3cb7daf1f11d951864abea014\",\"state_root\":\"0xf86196d34500ca25d1f4e7431d4d52f6f85540bcaf97dd0d2ad9ecdb3eebcdf0\",\"body\":{\"randao_reveal\":\"0xa7efee3d5ddceb60810b23e3b5d39734696418f41dfd13a0851c7be7a72acbdceaa61e1db27513801917d72519d1c1040ccfed829faf06abe06d9964949554bf4369134b66de715ea49eb4fecf3e2b7e646f1764a1993e31e53dbc6557929c12\",\"eth1_data\":{\"deposit_root\":\"0x8ec87d7219a3c873fff3bfe206b4f923d1b471ce4ff9d6d6ecc162ef07825e14\",\"deposit_count\":\"259476\",\"block_hash\":\"0x877b6f8332c7397251ff3f0c5cecec105ff7d4cb78251b47f91fd15a86a565ab\"},\"graffiti\":\"\",\"proposer_slashings\":[],\"attester_slashings\":[],\"attestations\":[],\"deposits\":[],\"voluntary_exits\":[],\"sync_aggregate\":{\"sync_committee_bits\":\"0x733dfda7f5ffde5ade73367fcbf7fffeef7fe43777ffdffab9dbad6f7eed5fff9bfec4affdefbfaddf35bf5efbff9ffff9dfd7dbf97fbfcdfaddfeffbf95f75f\",\"sync_committee_signature\":\"0x81fdf76e797f81b0116a1c1ae5200b613c8041115223cd89e8bd5477aab13de6097a9ebf42b130c59527bbb4c96811b809353a17c717549f82d4bd336068ef0b99b1feebd4d2432a69fa77fac12b78f1fcc9d7b59edbeb381adf10b15bc4a520\"},\"execution_payload\":{\"parent_hash\":\"0x14c2242a8cfbce559e84c391f5f16d10d7719751b8558873012dc88ae5a193e8\",\"fee_recipient\":\"$1\",\"state_root\":\"0xdf8d96b2c292736d39e72e25802c2744d34d3d3c616de5b362425cab01f72fa5\",\"receipts_root\":\"0x4938a2bf640846d213b156a1a853548b369cd02917fa63d8766ab665d7930bac\",\"logs_bloom\":\"0x298610600038408c201080013832408850a00bc8f801920121840030a015310010e2a0e0108628110552062811441c84802f43825c4fc82140b036c58025a28800054c80a44025c052090a0f2c209a0400058040019ea0008e589084078048050880930113a2894082e0112408b088382402a851621042212aa40018a408d07e178c68691486411aa9a2809043b000a04c040000065a030028018540b04b1820271d00821b00c29059095022322c10a530060223240416140190056608200063c82248274ba8f0098e402041cd9f451031481a1010b8220824833520490221071898802d206348449116812280014a10a2d1c210100a30010802490f0a221849\",\"prev_randao\":\"0xc061711e135cd40531ec3ee29d17d3824c0e5f80d07f721e792ab83240aa0ab5\",\"block_number\":\"8737497\",\"gas_limit\":\"30000000\",\"gas_used\":\"16367052\",\"timestamp\":\"1680080352\",\"extra_data\":\"0xd883010b05846765746888676f312e32302e32856c696e7578\",\"base_fee_per_gas\":\"231613172261\",\"block_hash\":\"0x5aa9fd22a9238925adb2b038fd6eafc77adabf554051db5bc16ae5168a52eff6\",\"transactions\":[],\"withdrawals\":[]}}}}"
|
||||
CapellaBlock = "{\"version\":\"capella\",\"data\":{\"slot\":\"5297696\",\"proposer_index\":\"153094\",\"parent_root\":\"0xe6106533af9be918120ead7440a8006c7f123cc3cb7daf1f11d951864abea014\",\"state_root\":\"0xf86196d34500ca25d1f4e7431d4d52f6f85540bcaf97dd0d2ad9ecdb3eebcdf0\",\"body\":{\"randao_reveal\":\"0xa7efee3d5ddceb60810b23e3b5d39734696418f41dfd13a0851c7be7a72acbdceaa61e1db27513801917d72519d1c1040ccfed829faf06abe06d9964949554bf4369134b66de715ea49eb4fecf3e2b7e646f1764a1993e31e53dbc6557929c12\",\"eth1_data\":{\"deposit_root\":\"0x8ec87d7219a3c873fff3bfe206b4f923d1b471ce4ff9d6d6ecc162ef07825e14\",\"deposit_count\":\"259476\",\"block_hash\":\"0x877b6f8332c7397251ff3f0c5cecec105ff7d4cb78251b47f91fd15a86a565ab\"},\"graffiti\":\"\",\"proposer_slashings\":[],\"attester_slashings\":[],\"attestations\":[],\"deposits\":[],\"voluntary_exits\":[],\"sync_aggregate\":{\"sync_committee_bits\":\"0x733dfda7f5ffde5ade73367fcbf7fffeef7fe43777ffdffab9dbad6f7eed5fff9bfec4affdefbfaddf35bf5efbff9ffff9dfd7dbf97fbfcdfaddfeffbf95f75f\",\"sync_committee_signature\":\"0x81fdf76e797f81b0116a1c1ae5200b613c8041115223cd89e8bd5477aab13de6097a9ebf42b130c59527bbb4c96811b809353a17c717549f82d4bd336068ef0b99b1feebd4d2432a69fa77fac12b78f1fcc9d7b59edbeb381adf10b15bc4a520\"},\"execution_payload\":{\"parent_hash\":\"0x14c2242a8cfbce559e84c391f5f16d10d7719751b8558873012dc88ae5a193e8\",\"fee_recipient\":\"$1\",\"state_root\":\"0xdf8d96b2c292736d39e72e25802c2744d34d3d3c616de5b362425cab01f72fa5\",\"receipts_root\":\"0x4938a2bf640846d213b156a1a853548b369cd02917fa63d8766ab665d7930bac\",\"logs_bloom\":\"0x298610600038408c201080013832408850a00bc8f801920121840030a015310010e2a0e0108628110552062811441c84802f43825c4fc82140b036c58025a28800054c80a44025c052090a0f2c209a0400058040019ea0008e589084078048050880930113a2894082e0112408b088382402a851621042212aa40018a408d07e178c68691486411aa9a2809043b000a04c040000065a030028018540b04b1820271d00821b00c29059095022322c10a530060223240416140190056608200063c82248274ba8f0098e402041cd9f451031481a1010b8220824833520490221071898802d206348449116812280014a10a2d1c210100a30010802490f0a221849\",\"prev_randao\":\"0xc061711e135cd40531ec3ee29d17d3824c0e5f80d07f721e792ab83240aa0ab5\",\"block_number\":\"8737497\",\"gas_limit\":\"30000000\",\"gas_used\":\"16367052\",\"timestamp\":\"1680080352\",\"extra_data\":\"0xd883010b05846765746888676f312e32302e32856c696e7578\",\"base_fee_per_gas\":\"231613172261\",\"block_hash\":\"0x5aa9fd22a9238925adb2b038fd6eafc77adabf554051db5bc16ae5168a52eff6\",\"transactions\":[],\"withdrawals\":[]},\"bls_to_execution_changes\":[]}}}"
|
||||
DenebBlock = "{\"version\":\"deneb\",\"data\":{\"slot\":\"5297696\",\"proposer_index\":\"153094\",\"parent_root\":\"0xe6106533af9be918120ead7440a8006c7f123cc3cb7daf1f11d951864abea014\",\"state_root\":\"0xf86196d34500ca25d1f4e7431d4d52f6f85540bcaf97dd0d2ad9ecdb3eebcdf0\",\"body\":{\"randao_reveal\":\"0xa7efee3d5ddceb60810b23e3b5d39734696418f41dfd13a0851c7be7a72acbdceaa61e1db27513801917d72519d1c1040ccfed829faf06abe06d9964949554bf4369134b66de715ea49eb4fecf3e2b7e646f1764a1993e31e53dbc6557929c12\",\"eth1_data\":{\"deposit_root\":\"0x8ec87d7219a3c873fff3bfe206b4f923d1b471ce4ff9d6d6ecc162ef07825e14\",\"deposit_count\":\"259476\",\"block_hash\":\"0x877b6f8332c7397251ff3f0c5cecec105ff7d4cb78251b47f91fd15a86a565ab\"},\"graffiti\":\"\",\"proposer_slashings\":[],\"attester_slashings\":[],\"attestations\":[],\"deposits\":[],\"voluntary_exits\":[],\"sync_aggregate\":{\"sync_committee_bits\":\"0x733dfda7f5ffde5ade73367fcbf7fffeef7fe43777ffdffab9dbad6f7eed5fff9bfec4affdefbfaddf35bf5efbff9ffff9dfd7dbf97fbfcdfaddfeffbf95f75f\",\"sync_committee_signature\":\"0x81fdf76e797f81b0116a1c1ae5200b613c8041115223cd89e8bd5477aab13de6097a9ebf42b130c59527bbb4c96811b809353a17c717549f82d4bd336068ef0b99b1feebd4d2432a69fa77fac12b78f1fcc9d7b59edbeb381adf10b15bc4a520\"},\"execution_payload\":{\"parent_hash\":\"0x14c2242a8cfbce559e84c391f5f16d10d7719751b8558873012dc88ae5a193e8\",\"fee_recipient\":\"$1\",\"state_root\":\"0xdf8d96b2c292736d39e72e25802c2744d34d3d3c616de5b362425cab01f72fa5\",\"receipts_root\":\"0x4938a2bf640846d213b156a1a853548b369cd02917fa63d8766ab665d7930bac\",\"logs_bloom\":\"0x298610600038408c201080013832408850a00bc8f801920121840030a015310010e2a0e0108628110552062811441c84802f43825c4fc82140b036c58025a28800054c80a44025c052090a0f2c209a0400058040019ea0008e589084078048050880930113a2894082e0112408b088382402a851621042212aa40018a408d07e178c68691486411aa9a2809043b000a04c040000065a030028018540b04b1820271d00821b00c29059095022322c10a530060223240416140190056608200063c82248274ba8f0098e402041cd9f451031481a1010b8220824833520490221071898802d206348449116812280014a10a2d1c210100a30010802490f0a221849\",\"prev_randao\":\"0xc061711e135cd40531ec3ee29d17d3824c0e5f80d07f721e792ab83240aa0ab5\",\"block_number\":\"8737497\",\"gas_limit\":\"30000000\",\"gas_used\":\"16367052\",\"timestamp\":\"1680080352\",\"extra_data\":\"0xd883010b05846765746888676f312e32302e32856c696e7578\",\"base_fee_per_gas\":\"231613172261\",\"block_hash\":\"0x5aa9fd22a9238925adb2b038fd6eafc77adabf554051db5bc16ae5168a52eff6\",\"transactions\":[],\"withdrawals\":[],\"data_gas_used\":\"2316131761\",\"excess_data_gas\":\"231613172261\"},\"bls_to_execution_changes\":[],\"blob_kzg_commitments\":[]}}}"
|
||||
DenebBlockContents = "{\"version\":\"deneb\",\"data\":{\"block\":{\"slot\":\"5297696\",\"proposer_index\":\"153094\",\"parent_root\":\"0xe6106533af9be918120ead7440a8006c7f123cc3cb7daf1f11d951864abea014\",\"state_root\":\"0xf86196d34500ca25d1f4e7431d4d52f6f85540bcaf97dd0d2ad9ecdb3eebcdf0\",\"body\":{\"randao_reveal\":\"0xa7efee3d5ddceb60810b23e3b5d39734696418f41dfd13a0851c7be7a72acbdceaa61e1db27513801917d72519d1c1040ccfed829faf06abe06d9964949554bf4369134b66de715ea49eb4fecf3e2b7e646f1764a1993e31e53dbc6557929c12\",\"eth1_data\":{\"deposit_root\":\"0x8ec87d7219a3c873fff3bfe206b4f923d1b471ce4ff9d6d6ecc162ef07825e14\",\"deposit_count\":\"259476\",\"block_hash\":\"0x877b6f8332c7397251ff3f0c5cecec105ff7d4cb78251b47f91fd15a86a565ab\"},\"graffiti\":\"\",\"proposer_slashings\":[],\"attester_slashings\":[],\"attestations\":[],\"deposits\":[],\"voluntary_exits\":[],\"sync_aggregate\":{\"sync_committee_bits\":\"0x733dfda7f5ffde5ade73367fcbf7fffeef7fe43777ffdffab9dbad6f7eed5fff9bfec4affdefbfaddf35bf5efbff9ffff9dfd7dbf97fbfcdfaddfeffbf95f75f\",\"sync_committee_signature\":\"0x81fdf76e797f81b0116a1c1ae5200b613c8041115223cd89e8bd5477aab13de6097a9ebf42b130c59527bbb4c96811b809353a17c717549f82d4bd336068ef0b99b1feebd4d2432a69fa77fac12b78f1fcc9d7b59edbeb381adf10b15bc4a520\"},\"execution_payload\":{\"parent_hash\":\"0x14c2242a8cfbce559e84c391f5f16d10d7719751b8558873012dc88ae5a193e8\",\"fee_recipient\":\"$1\",\"state_root\":\"0xdf8d96b2c292736d39e72e25802c2744d34d3d3c616de5b362425cab01f72fa5\",\"receipts_root\":\"0x4938a2bf640846d213b156a1a853548b369cd02917fa63d8766ab665d7930bac\",\"logs_bloom\":\"0x298610600038408c201080013832408850a00bc8f801920121840030a015310010e2a0e0108628110552062811441c84802f43825c4fc82140b036c58025a28800054c80a44025c052090a0f2c209a0400058040019ea0008e589084078048050880930113a2894082e0112408b088382402a851621042212aa40018a408d07e178c68691486411aa9a2809043b000a04c040000065a030028018540b04b1820271d00821b00c29059095022322c10a530060223240416140190056608200063c82248274ba8f0098e402041cd9f451031481a1010b8220824833520490221071898802d206348449116812280014a10a2d1c210100a30010802490f0a221849\",\"prev_randao\":\"0xc061711e135cd40531ec3ee29d17d3824c0e5f80d07f721e792ab83240aa0ab5\",\"block_number\":\"8737497\",\"gas_limit\":\"30000000\",\"gas_used\":\"16367052\",\"timestamp\":\"1680080352\",\"extra_data\":\"0xd883010b05846765746888676f312e32302e32856c696e7578\",\"base_fee_per_gas\":\"231613172261\",\"block_hash\":\"0x5aa9fd22a9238925adb2b038fd6eafc77adabf554051db5bc16ae5168a52eff6\",\"transactions\":[],\"withdrawals\":[],\"data_gas_used\":\"2316131761\",\"excess_data_gas\":\"231613172261\"},\"bls_to_execution_changes\":[],\"blob_kzg_commitments\":[]}},\"blob_sidecars\":[]}}"
|
||||
|
||||
SigningNodeAddress = "127.0.0.1"
|
||||
defaultSigningNodePort = 35333
|
||||
|
@ -79,6 +79,20 @@ proc getNodePort(basePort: int, rt: RemoteSignerType): int =
|
|||
of RemoteSignerType.VerifyingWeb3Signer:
|
||||
basePort + 1
|
||||
|
||||
|
||||
func init(T: type ForkedBeaconBlock, contents: ProduceBlockResponseV2): T =
|
||||
case contents.kind
|
||||
of ConsensusFork.Phase0:
|
||||
return ForkedBeaconBlock.init(contents.phase0Data)
|
||||
of ConsensusFork.Altair:
|
||||
return ForkedBeaconBlock.init(contents.altairData)
|
||||
of ConsensusFork.Bellatrix:
|
||||
return ForkedBeaconBlock.init(contents.bellatrixData)
|
||||
of ConsensusFork.Capella:
|
||||
return ForkedBeaconBlock.init(contents.capellaData)
|
||||
of ConsensusFork.Deneb:
|
||||
return ForkedBeaconBlock.init(contents.denebData.block)
|
||||
|
||||
proc getBlock(fork: ConsensusFork,
|
||||
feeRecipient = SigningExpectedFeeRecipient): ForkedBeaconBlock =
|
||||
let
|
||||
|
@ -88,14 +102,16 @@ proc getBlock(fork: ConsensusFork,
|
|||
of ConsensusFork.Altair: AltairBlock
|
||||
of ConsensusFork.Bellatrix: BellatrixBlock % [feeRecipient]
|
||||
of ConsensusFork.Capella: CapellaBlock % [feeRecipient]
|
||||
of ConsensusFork.Deneb: DenebBlock % [feeRecipient]
|
||||
of ConsensusFork.Deneb: DenebBlockContents % [feeRecipient]
|
||||
contentType = ContentTypeData(
|
||||
mediaType: MediaType.init("application/json"))
|
||||
|
||||
decodeBytes(ProduceBlockResponseV2,
|
||||
blckData.toOpenArrayByte(0, len(blckData) - 1),
|
||||
Opt.some(contentType),
|
||||
$fork).tryGet()
|
||||
|
||||
let b = decodeBytes(ProduceBlockResponseV2,
|
||||
blckData.toOpenArrayByte(0, len(blckData) - 1),
|
||||
Opt.some(contentType),
|
||||
$fork).tryGet()
|
||||
ForkedBeaconBlock.init(b)
|
||||
|
||||
proc init(t: typedesc[Web3SignerForkedBeaconBlock],
|
||||
forked: ForkedBeaconBlock): Web3SignerForkedBeaconBlock =
|
||||
|
|
Loading…
Reference in New Issue