remove block fork guessing/inference from REST JSON decoding (#6552)

* remove block fork guessing/inference from REST JSON decoding

* use template to avoid repetitive per-fork code

* consolidate RestPublishedSignedBeaconBlock and RestPublishedSignedBlockContents parsing fork handling
This commit is contained in:
tersec 2024-09-25 02:58:53 +00:00 committed by GitHub
parent 31b5c3e30e
commit f2d6166099
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 115 additions and 625 deletions

View File

@ -821,10 +821,9 @@ OK: 1/1 Fail: 0/1 Skip: 0/1
+ DenebSignedBlockContents decoding OK
+ KzgCommitment OK
+ KzgProof OK
+ RestPublishedSignedBlockContents decoding OK
+ Validator pubkey hack OK
```
OK: 6/6 Fail: 0/6 Skip: 0/6
OK: 5/5 Fail: 0/5 Skip: 0/5
## Remove keystore testing suite
```diff
+ Many remotes OK
@ -1126,4 +1125,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2
OK: 9/9 Fail: 0/9 Skip: 0/9
---TOTAL---
OK: 763/768 Fail: 0/768 Skip: 5/768
OK: 762/767 Fail: 0/767 Skip: 5/767

View File

@ -901,10 +901,23 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http400, EmptyRequestBodyError)
let
body = contentBody.get()
version = request.headers.getString("eth-consensus-version")
restBlock = decodeBody(
RestPublishedSignedBlockContents, body, version).valueOr:
return RestApiResponse.jsonError(error)
currentEpochFork =
node.dag.cfg.consensusForkAtEpoch(node.currentSlot().epoch())
rawVersion = request.headers.getString("eth-consensus-version")
# The V1 endpoint doesn't require the version to be specified but the
# only fork which works is the current gossip fork. Either it can use
# and broadcast a block in that fork or that broadcast will not prove
# useful anyway, so allow it to fail at the decoding stage.
version =
if rawVersion == "":
currentEpochFork.toString
else:
rawVersion
let restBlock = decodeBody(
RestPublishedSignedBlockContents, body, version).valueOr:
return RestApiResponse.jsonError(error)
withForkyBlck(restBlock):
if restBlock.kind != node.dag.cfg.consensusForkAtEpoch(
@ -1047,9 +1060,19 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
let
currentEpochFork =
node.dag.cfg.consensusForkAtEpoch(node.currentSlot().epoch())
version = request.headers.getString("eth-consensus-version")
rawVersion = request.headers.getString("eth-consensus-version")
body = contentBody.get()
# The V1 endpoint doesn't require the version to be specified but the
# only fork which works is the current gossip fork. Either it can use
# and broadcast a block in that fork or that broadcast will not prove
# useful anyway, so allow it to fail at the decoding stage.
version =
if rawVersion == "":
currentEpochFork.toString
else:
rawVersion
if (body.contentType == OctetStreamMediaType) and
(currentEpochFork.toString != version):
return RestApiResponse.jsonError(Http400, BlockIncorrectFork)

View File

@ -129,7 +129,6 @@ RestJson.useDefaultSerializationFor(
RestDepositContract,
RestEpochRandao,
RestEpochSyncCommittee,
RestExecutionPayload,
RestExtraData,
RestGenesis,
RestIndexedErrorMessage,
@ -1604,532 +1603,6 @@ proc writeValue*[BlockType: Web3SignerForkedBeaconBlock](
writer.writeField("block_header", value.data)
writer.endRecord()
## RestPublishedBeaconBlockBody
proc readValue*(reader: var JsonReader[RestJson],
value: var RestPublishedBeaconBlockBody) {.
raises: [IOError, SerializationError].} =
var
randao_reveal: Opt[ValidatorSig]
eth1_data: Opt[Eth1Data]
graffiti: Opt[GraffitiBytes]
proposer_slashings:
Opt[List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]]
attester_slashings:
Opt[List[phase0.AttesterSlashing, Limit MAX_ATTESTER_SLASHINGS]]
attestations: Opt[List[phase0.Attestation, Limit MAX_ATTESTATIONS]]
deposits: Opt[List[Deposit, Limit MAX_DEPOSITS]]
voluntary_exits: Opt[List[SignedVoluntaryExit, Limit MAX_VOLUNTARY_EXITS]]
sync_aggregate: Opt[SyncAggregate]
execution_payload: Opt[RestExecutionPayload]
bls_to_execution_changes: Opt[SignedBLSToExecutionChangeList]
blob_kzg_commitments: Opt[KzgCommitments]
for fieldName in readObjectFields(reader):
case fieldName
of "randao_reveal":
if randao_reveal.isSome():
reader.raiseUnexpectedField("Multiple `randao_reveal` fields found",
"RestPublishedBeaconBlockBody")
randao_reveal = Opt.some(reader.readValue(ValidatorSig))
of "eth1_data":
if eth1_data.isSome():
reader.raiseUnexpectedField("Multiple `eth1_data` fields found",
"RestPublishedBeaconBlockBody")
eth1_data = Opt.some(reader.readValue(Eth1Data))
of "graffiti":
if graffiti.isSome():
reader.raiseUnexpectedField("Multiple `graffiti` fields found",
"RestPublishedBeaconBlockBody")
graffiti = Opt.some(reader.readValue(GraffitiBytes))
of "proposer_slashings":
if proposer_slashings.isSome():
reader.raiseUnexpectedField(
"Multiple `proposer_slashings` fields found",
"RestPublishedBeaconBlockBody")
proposer_slashings = Opt.some(
reader.readValue(List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]))
of "attester_slashings":
if attester_slashings.isSome():
reader.raiseUnexpectedField(
"Multiple `attester_slashings` fields found",
"RestPublishedBeaconBlockBody")
attester_slashings = Opt.some(
reader.readValue(
List[phase0.AttesterSlashing, Limit MAX_ATTESTER_SLASHINGS]))
of "attestations":
if attestations.isSome():
reader.raiseUnexpectedField("Multiple `attestations` fields found",
"RestPublishedBeaconBlockBody")
attestations = Opt.some(
reader.readValue(List[phase0.Attestation, Limit MAX_ATTESTATIONS]))
of "deposits":
if deposits.isSome():
reader.raiseUnexpectedField("Multiple `deposits` fields found",
"RestPublishedBeaconBlockBody")
deposits = Opt.some(reader.readValue(List[Deposit, Limit MAX_DEPOSITS]))
of "voluntary_exits":
if voluntary_exits.isSome():
reader.raiseUnexpectedField("Multiple `voluntary_exits` fields found",
"RestPublishedBeaconBlockBody")
voluntary_exits = Opt.some(
reader.readValue(List[SignedVoluntaryExit, Limit MAX_VOLUNTARY_EXITS]))
of "sync_aggregate":
if sync_aggregate.isSome():
reader.raiseUnexpectedField("Multiple `sync_aggregate` fields found",
"RestPublishedBeaconBlockBody")
sync_aggregate = Opt.some(reader.readValue(SyncAggregate))
of "execution_payload":
if execution_payload.isSome():
reader.raiseUnexpectedField("Multiple `execution_payload` fields found",
"RestPublishedBeaconBlockBody")
execution_payload = Opt.some(reader.readValue(RestExecutionPayload))
of "bls_to_execution_changes":
if bls_to_execution_changes.isSome():
reader.raiseUnexpectedField("Multiple `bls_to_execution_changes` fields found",
"RestPublishedBeaconBlockBody")
bls_to_execution_changes = Opt.some(
reader.readValue(SignedBLSToExecutionChangeList))
of "blob_kzg_commitments":
if blob_kzg_commitments.isSome():
reader.raiseUnexpectedField("Multiple `blob_kzg_commitments` fields found",
"RestPublishedBeaconBlockBody")
blob_kzg_commitments = Opt.some(reader.readValue(KzgCommitments))
else:
unrecognizedFieldWarning(fieldName, typeof(value).name)
if randao_reveal.isNone():
reader.raiseUnexpectedValue("Field `randao_reveal` is missing")
if eth1_data.isNone():
reader.raiseUnexpectedValue("Field `eth1_data` is missing")
if graffiti.isNone():
reader.raiseUnexpectedValue("Field `graffiti` is missing")
if proposer_slashings.isNone():
reader.raiseUnexpectedValue("Field `proposer_slashings` is missing")
if attester_slashings.isNone():
reader.raiseUnexpectedValue("Field `attester_slashings` is missing")
if attestations.isNone():
reader.raiseUnexpectedValue("Field `attestations` is missing")
if deposits.isNone():
reader.raiseUnexpectedValue("Field `deposits` is missing")
if voluntary_exits.isNone():
reader.raiseUnexpectedValue("Field `voluntary_exits` is missing")
let bodyKind =
if execution_payload.isSome() and
execution_payload.get().blob_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():
ConsensusFork.Capella
elif execution_payload.isSome() and sync_aggregate.isSome():
ConsensusFork.Bellatrix
elif execution_payload.isNone() and sync_aggregate.isSome():
ConsensusFork.Altair
else:
ConsensusFork.Phase0
template ep_src: auto = execution_payload.get()
template copy_ep_bellatrix(ep_dst: auto) =
assign(ep_dst.parent_hash, ep_src.parent_hash)
assign(ep_dst.fee_recipient, ep_src.fee_recipient)
assign(ep_dst.state_root, ep_src.state_root)
assign(ep_dst.receipts_root, ep_src.receipts_root)
assign(ep_dst.logs_bloom, ep_src.logs_bloom)
assign(ep_dst.prev_randao, ep_src.prev_randao)
assign(ep_dst.block_number, ep_src.block_number)
assign(ep_dst.gas_limit, ep_src.gas_limit)
assign(ep_dst.gas_used, ep_src.gas_used)
assign(ep_dst.timestamp, ep_src.timestamp)
assign(ep_dst.extra_data, ep_src.extra_data)
assign(ep_dst.base_fee_per_gas, ep_src.base_fee_per_gas)
assign(ep_dst.block_hash, ep_src.block_hash)
assign(ep_dst.transactions, ep_src.transactions)
case bodyKind
of ConsensusFork.Phase0:
value = RestPublishedBeaconBlockBody(
kind: ConsensusFork.Phase0,
phase0Body: phase0.BeaconBlockBody(
randao_reveal: randao_reveal.get(),
eth1_data: eth1_data.get(),
graffiti: graffiti.get(),
proposer_slashings: proposer_slashings.get(),
attester_slashings: attester_slashings.get(),
attestations: attestations.get(),
deposits: deposits.get(),
voluntary_exits: voluntary_exits.get()
)
)
of ConsensusFork.Altair:
value = RestPublishedBeaconBlockBody(
kind: ConsensusFork.Altair,
altairBody: altair.BeaconBlockBody(
randao_reveal: randao_reveal.get(),
eth1_data: eth1_data.get(),
graffiti: graffiti.get(),
proposer_slashings: proposer_slashings.get(),
attester_slashings: attester_slashings.get(),
attestations: attestations.get(),
deposits: deposits.get(),
voluntary_exits: voluntary_exits.get(),
sync_aggregate: sync_aggregate.get()
)
)
of ConsensusFork.Bellatrix:
value = RestPublishedBeaconBlockBody(
kind: ConsensusFork.Bellatrix,
bellatrixBody: bellatrix.BeaconBlockBody(
randao_reveal: randao_reveal.get(),
eth1_data: eth1_data.get(),
graffiti: graffiti.get(),
proposer_slashings: proposer_slashings.get(),
attester_slashings: attester_slashings.get(),
attestations: attestations.get(),
deposits: deposits.get(),
voluntary_exits: voluntary_exits.get(),
sync_aggregate: sync_aggregate.get(),
)
)
copy_ep_bellatrix(value.bellatrixBody.execution_payload)
of ConsensusFork.Capella:
value = RestPublishedBeaconBlockBody(
kind: ConsensusFork.Capella,
capellaBody: capella.BeaconBlockBody(
randao_reveal: randao_reveal.get(),
eth1_data: eth1_data.get(),
graffiti: graffiti.get(),
proposer_slashings: proposer_slashings.get(),
attester_slashings: attester_slashings.get(),
attestations: attestations.get(),
deposits: deposits.get(),
voluntary_exits: voluntary_exits.get(),
sync_aggregate: sync_aggregate.get(),
bls_to_execution_changes: bls_to_execution_changes.get()
)
)
copy_ep_bellatrix(value.capellaBody.execution_payload)
assign(
value.capellaBody.execution_payload.withdrawals,
ep_src.withdrawals.get())
of ConsensusFork.Deneb:
value = RestPublishedBeaconBlockBody(
kind: ConsensusFork.Deneb,
denebBody: deneb.BeaconBlockBody(
randao_reveal: randao_reveal.get(),
eth1_data: eth1_data.get(),
graffiti: graffiti.get(),
proposer_slashings: proposer_slashings.get(),
attester_slashings: attester_slashings.get(),
attestations: attestations.get(),
deposits: deposits.get(),
voluntary_exits: voluntary_exits.get(),
sync_aggregate: sync_aggregate.get(),
bls_to_execution_changes: bls_to_execution_changes.get(),
blob_kzg_commitments: blob_kzg_commitments.get()
)
)
copy_ep_bellatrix(value.denebBody.execution_payload)
assign(
value.denebBody.execution_payload.withdrawals,
ep_src.withdrawals.get())
assign(
value.denebBody.execution_payload.blob_gas_used,
ep_src.blob_gas_used.get())
assign(
value.denebBody.execution_payload.excess_blob_gas,
ep_src.excess_blob_gas.get())
of ConsensusFork.Electra:
value = RestPublishedBeaconBlockBody(
kind: ConsensusFork.Electra,
electraBody: electra.BeaconBlockBody(
randao_reveal: randao_reveal.get(),
eth1_data: eth1_data.get(),
graffiti: graffiti.get(),
proposer_slashings: proposer_slashings.get(),
#attester_slashings: attester_slashings.get(),
#attestations: attestations.get(),
deposits: deposits.get(),
voluntary_exits: voluntary_exits.get(),
sync_aggregate: sync_aggregate.get(),
bls_to_execution_changes: bls_to_execution_changes.get(),
blob_kzg_commitments: blob_kzg_commitments.get()
)
)
copy_ep_bellatrix(value.electraBody.execution_payload)
assign(
value.electraBody.execution_payload.withdrawals,
ep_src.withdrawals.get())
assign(
value.electraBody.execution_payload.blob_gas_used,
ep_src.blob_gas_used.get())
assign(
value.electraBody.execution_payload.excess_blob_gas,
ep_src.excess_blob_gas.get())
debugComment "electra support missing, including attslashing/atts"
## RestPublishedBeaconBlock
proc readValue*(reader: var JsonReader[RestJson],
value: var RestPublishedBeaconBlock) {.
raises: [IOError, SerializationError].} =
var
slot: Opt[Slot]
proposer_index: Opt[uint64]
parent_root: Opt[Eth2Digest]
state_root: Opt[Eth2Digest]
blockBody: Opt[RestPublishedBeaconBlockBody]
for fieldName in readObjectFields(reader):
case fieldName
of "slot":
if slot.isSome():
reader.raiseUnexpectedField("Multiple `slot` fields found",
"RestPublishedBeaconBlock")
slot = Opt.some(reader.readValue(Slot))
of "proposer_index":
if proposer_index.isSome():
reader.raiseUnexpectedField("Multiple `proposer_index` fields found",
"RestPublishedBeaconBlock")
proposer_index = Opt.some(reader.readValue(uint64))
of "parent_root":
if parent_root.isSome():
reader.raiseUnexpectedField("Multiple `parent_root` fields found",
"RestPublishedBeaconBlock")
parent_root = Opt.some(reader.readValue(Eth2Digest))
of "state_root":
if state_root.isSome():
reader.raiseUnexpectedField("Multiple `state_root` fields found",
"RestPublishedBeaconBlock")
state_root = Opt.some(reader.readValue(Eth2Digest))
of "body":
if blockBody.isSome():
reader.raiseUnexpectedField("Multiple `body` fields found",
"RestPublishedBeaconBlock")
blockBody = Opt.some(reader.readValue(RestPublishedBeaconBlockBody))
else:
unrecognizedFieldWarning(fieldName, typeof(value).name)
if slot.isNone():
reader.raiseUnexpectedValue("Field `slot` is missing")
if proposer_index.isNone():
reader.raiseUnexpectedValue("Field `proposer_index` is missing")
if parent_root.isNone():
reader.raiseUnexpectedValue("Field `parent_root` is missing")
if state_root.isNone():
reader.raiseUnexpectedValue("Field `state_root` is missing")
if blockBody.isNone():
reader.raiseUnexpectedValue("Field `body` is missing")
let body = blockBody.get()
value = RestPublishedBeaconBlock(
case body.kind
of ConsensusFork.Phase0:
ForkedBeaconBlock.init(
phase0.BeaconBlock(
slot: slot.get(),
proposer_index: proposer_index.get(),
parent_root: parent_root.get(),
state_root: state_root.get(),
body: body.phase0Body
)
)
of ConsensusFork.Altair:
ForkedBeaconBlock.init(
altair.BeaconBlock(
slot: slot.get(),
proposer_index: proposer_index.get(),
parent_root: parent_root.get(),
state_root: state_root.get(),
body: body.altairBody
)
)
of ConsensusFork.Bellatrix:
ForkedBeaconBlock.init(
bellatrix.BeaconBlock(
slot: slot.get(),
proposer_index: proposer_index.get(),
parent_root: parent_root.get(),
state_root: state_root.get(),
body: body.bellatrixBody
)
)
of ConsensusFork.Capella:
ForkedBeaconBlock.init(
capella.BeaconBlock(
slot: slot.get(),
proposer_index: proposer_index.get(),
parent_root: parent_root.get(),
state_root: state_root.get(),
body: body.capellaBody
)
)
of ConsensusFork.Deneb:
ForkedBeaconBlock.init(
deneb.BeaconBlock(
slot: slot.get(),
proposer_index: proposer_index.get(),
parent_root: parent_root.get(),
state_root: state_root.get(),
body: body.denebBody
)
)
of ConsensusFork.Electra:
ForkedBeaconBlock.init(
electra.BeaconBlock(
slot: slot.get(),
proposer_index: proposer_index.get(),
parent_root: parent_root.get(),
state_root: state_root.get(),
body: body.electraBody
)
)
)
## RestPublishedSignedBeaconBlock
proc readValue*(reader: var JsonReader[RestJson],
value: var RestPublishedSignedBeaconBlock) {.
raises: [IOError, SerializationError].} =
var signature: Opt[ValidatorSig]
var message: Opt[RestPublishedBeaconBlock]
for fieldName in readObjectFields(reader):
case fieldName
of "message":
if message.isSome():
reader.raiseUnexpectedField("Multiple `message` fields found",
"RestPublishedSignedBeaconBlock")
message = Opt.some(reader.readValue(RestPublishedBeaconBlock))
of "signature":
if signature.isSome():
reader.raiseUnexpectedField("Multiple `signature` fields found",
"RestPublishedSignedBeaconBlock")
signature = Opt.some(reader.readValue(ValidatorSig))
else:
unrecognizedFieldWarning(fieldName, typeof(value).name)
if signature.isNone():
reader.raiseUnexpectedValue("Field `signature` is missing")
if message.isNone():
reader.raiseUnexpectedValue("Field `message` is missing")
let blck = ForkedBeaconBlock(message.get())
value = RestPublishedSignedBeaconBlock ForkedSignedBeaconBlock.init(
blck, blck.hash_tree_root(), signature.get())
proc readValue*(reader: var JsonReader[RestJson],
value: var RestPublishedSignedBlockContents) {.
raises: [IOError, SerializationError].} =
var signature: Opt[ValidatorSig]
var message: Opt[RestPublishedBeaconBlock]
var signed_message: Opt[RestPublishedSignedBeaconBlock]
var signed_block_data: Opt[JsonString]
var kzg_proofs: Opt[deneb.KzgProofs]
var blobs: Opt[deneb.Blobs]
# 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' 'kzg_proofs', `blobs`. 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 = Opt.some(reader.readValue(RestPublishedBeaconBlock))
of "signature":
if signature.isSome():
reader.raiseUnexpectedField("Multiple `signature` fields found",
"RestPublishedSignedBlockContents")
signature = Opt.some(reader.readValue(ValidatorSig))
of "signed_block":
if signed_block_data.isSome():
reader.raiseUnexpectedField("Multiple `signed_block` fields found",
"RestPublishedSignedBlockContents")
signed_block_data = Opt.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:
Opt.some(RestJson.decode(string(signed_block_data.get()),
RestPublishedSignedBeaconBlock,
requireAllFields = true,
allowUnknownFields = true))
except SerializationError:
Opt.none(RestPublishedSignedBeaconBlock)
if signed_message.isNone():
reader.raiseUnexpectedValue("Incorrect signed_block format")
of "kzg_proofs":
if kzg_proofs.isSome():
reader.raiseUnexpectedField(
"Multiple `kzg_proofs` fields found",
"RestPublishedSignedBlockContents")
if signature.isSome():
reader.raiseUnexpectedField(
"Found `kzg_proofs` field alongside signature field",
"RestPublishedSignedBlockContents")
kzg_proofs = Opt.some(reader.readValue(deneb.KzgProofs))
of "blobs":
if blobs.isSome():
reader.raiseUnexpectedField(
"Multiple `blobs` fields found",
"RestPublishedSignedBlockContents")
if signature.isSome():
reader.raiseUnexpectedField(
"Found `blobs` field alongside signature field",
"RestPublishedSignedBlockContents")
blobs = Opt.some(reader.readValue(deneb.Blobs))
else:
unrecognizedFieldWarning(fieldName, typeof(value).name)
if signed_message.isSome():
if message.isSome():
reader.raiseUnexpectedValue("Field `message` found but unsupported")
if signature.isSome():
reader.raiseUnexpectedValue("Field `signature` found but unsupported")
if kzg_proofs.isNone():
reader.raiseUnexpectedValue("Field `kzg_proofs` is missing")
if blobs.isNone():
reader.raiseUnexpectedValue("Field `blobs` is missing")
if kzg_proofs.get.len != blobs.get.len:
reader.raiseUnexpectedValue("Length mismatch of `kzg_proofs` and `blobs`")
withBlck(distinctBase(signed_message.get)):
when consensusFork >= ConsensusFork.Deneb:
template kzg_commitments: untyped =
forkyBlck.message.body.blob_kzg_commitments
if kzg_proofs.get().len != kzg_commitments.len:
reader.raiseUnexpectedValue(
"Length mismatch of `kzg_proofs` and `blob_kzg_commitments`")
value = RestPublishedSignedBlockContents.init(
consensusFork.BlockContents(
`block`: forkyBlck.message,
kzg_proofs: kzg_proofs.get(),
blobs: blobs.get()),
forkyBlck.root, forkyBlck.signature)
else:
reader.raiseUnexpectedValue("`signed_message` supported post-Deneb")
else:
if signature.isNone():
reader.raiseUnexpectedValue("Field `signature` is missing")
if message.isNone():
reader.raiseUnexpectedValue("Field `message` is missing")
if kzg_proofs.isSome():
reader.raiseUnexpectedValue("Field `kzg_proofs` found but unsupported")
if blobs.isSome():
reader.raiseUnexpectedValue("Field `blobs` found but unsupported")
withBlck(distinctBase(message.get)):
when consensusFork < ConsensusFork.Deneb:
value = RestPublishedSignedBlockContents.init(
forkyBlck, forkyBlck.hash_tree_root(), signature.get)
else:
reader.raiseUnexpectedValue("`message` support stopped at Deneb")
## ForkedSignedBeaconBlock
proc readValue*(reader: var JsonReader[RestJson],
value: var ForkedSignedBeaconBlock) {.
@ -3406,11 +2879,16 @@ proc decodeBody*(
version: string
): Result[RestPublishedSignedBeaconBlock, RestErrorMessage] =
if body.contentType == ApplicationJsonMediaType:
let data =
let consensusFork = ConsensusFork.decodeString(version).valueOr:
return err(RestErrorMessage.init(Http400, UnableDecodeVersionError,
[version, $error]))
template getBlck(blckType: untyped): untyped =
try:
RestJson.decode(body.data, RestPublishedSignedBeaconBlock,
requireAllFields = true,
allowUnknownFields = true)
RestPublishedSignedBeaconBlock(ForkedSignedBeaconBlock.init(
RestJson.decode(body.data, blckType,
requireAllFields = true,
allowUnknownFields = true)))
except SerializationError as exc:
debug "Failed to decode JSON data",
err = exc.formatMsg("<data>"),
@ -3420,7 +2898,10 @@ proc decodeBody*(
except CatchableError as exc:
return err(RestErrorMessage.init(Http400, UnexpectedDecodeError,
[version, $exc.msg]))
ok(data)
withConsensusFork(consensusFork):
ok(getBlck(consensusFork.SignedBeaconBlock))
elif body.contentType == OctetStreamMediaType:
let consensusFork = ConsensusFork.decodeString(version).valueOr:
return err(RestErrorMessage.init(Http400, UnableDecodeVersionError,
@ -3502,11 +2983,20 @@ proc decodeBody*(
version: string
): Result[RestPublishedSignedBlockContents, RestErrorMessage] =
if body.contentType == ApplicationJsonMediaType:
let data =
let consensusFork = ConsensusFork.decodeString(version).valueOr:
return err(RestErrorMessage.init(Http400, UnableDecodeVersionError,
[version, $error]))
template getBlck(blckType: untyped): untyped =
try:
RestJson.decode(body.data, RestPublishedSignedBlockContents,
requireAllFields = true,
allowUnknownFields = true)
var res = RestJson.decode(body.data, blckType,
requireAllFields = true,
allowUnknownFields = true)
when compiles(res.signed_block.messsage):
{.error: "Deneb and later forks handled in case statement".}
else:
RestPublishedSignedBlockContents.init(
res.message, hash_tree_root(res.message), res.signature)
except SerializationError as exc:
debug "Failed to decode JSON data",
err = exc.formatMsg("<data>"),
@ -3516,6 +3006,48 @@ proc decodeBody*(
except CatchableError as exc:
return err(RestErrorMessage.init(Http400, UnexpectedDecodeError,
[version, $exc.msg]))
let data =
case consensusFork
of ConsensusFork.Phase0: getBlck(phase0.SignedBeaconBlock)
of ConsensusFork.Altair: getBlck(altair.SignedBeaconBlock)
of ConsensusFork.Bellatrix: getBlck(bellatrix.SignedBeaconBlock)
of ConsensusFork.Capella: getBlck(capella.SignedBeaconBlock)
of ConsensusFork.Deneb:
try:
var res = RestJson.decode(body.data, DenebSignedBlockContents,
requireAllFields = true,
allowUnknownFields = true)
res.signed_block.root = hash_tree_root(res.signed_block.message)
RestPublishedSignedBlockContents(
kind: ConsensusFork.Deneb, denebData: res)
except SerializationError as exc:
debug "Failed to decode JSON data",
err = exc.formatMsg("<data>"),
data = string.fromBytes(body.data)
return err(RestErrorMessage.init(Http400, UnableDecodeError,
[version, exc.formatMsg("<data>")]))
except CatchableError as exc:
return err(RestErrorMessage.init(Http400, UnexpectedDecodeError,
[version, $exc.msg]))
of ConsensusFork.Electra:
try:
var res = RestJson.decode(body.data, ElectraSignedBlockContents,
requireAllFields = true,
allowUnknownFields = true)
res.signed_block.root = hash_tree_root(res.signed_block.message)
RestPublishedSignedBlockContents(
kind: ConsensusFork.Electra, electraData: res)
except SerializationError as exc:
debug "Failed to decode JSON data",
err = exc.formatMsg("<data>"),
data = string.fromBytes(body.data)
return err(RestErrorMessage.init(Http400, UnableDecodeError,
[version, exc.formatMsg("<data>")]))
except CatchableError as exc:
return err(RestErrorMessage.init(Http400, UnexpectedDecodeError,
[version, $exc.msg]))
ok(data)
elif body.contentType == OctetStreamMediaType:
let consensusFork = ConsensusFork.decodeString(version).valueOr:

View File

@ -292,35 +292,6 @@ type
RestWithdrawalPrefix* = distinct array[1, byte]
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/capella/beacon-chain.md#executionpayload
RestExecutionPayload* = object
# Execution block header fields
parent_hash*: Eth2Digest
fee_recipient*: ExecutionAddress
## 'beneficiary' in the yellow paper
state_root*: Eth2Digest
receipts_root*: Eth2Digest
logs_bloom*: BloomLogs
prev_randao*: Eth2Digest
## 'difficulty' in the yellow paper
block_number*: uint64
## 'number' in the yellow paper
gas_limit*: uint64
gas_used*: uint64
timestamp*: uint64
extra_data*: List[byte, MAX_EXTRA_DATA_BYTES]
base_fee_per_gas*: UInt256
# Extra payload fields
block_hash*: Eth2Digest
## Hash of execution block
transactions*: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD]
withdrawals*: Option[List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD]]
## [New in Capella]
blob_gas_used*: Option[uint64] ## [New in Deneb]
excess_blob_gas*: Option[uint64] ## [New in Deneb]
PrepareBeaconProposer* = object
validator_index*: ValidatorIndex
fee_recipient*: Eth1Address
@ -346,17 +317,6 @@ type
of ConsensusFork.Deneb: denebData*: DenebSignedBlockContents
of ConsensusFork.Electra: electraData*: ElectraSignedBlockContents
RestPublishedBeaconBlock* = distinct ForkedBeaconBlock
RestPublishedBeaconBlockBody* = object
case kind*: ConsensusFork
of ConsensusFork.Phase0: phase0Body*: phase0.BeaconBlockBody
of ConsensusFork.Altair: altairBody*: altair.BeaconBlockBody
of ConsensusFork.Bellatrix: bellatrixBody*: bellatrix.BeaconBlockBody
of ConsensusFork.Capella: capellaBody*: capella.BeaconBlockBody
of ConsensusFork.Deneb: denebBody*: deneb.BeaconBlockBody
of ConsensusFork.Electra: electraBody*: electra.BeaconBlockBody
ProduceBlockResponseV3* = ForkedMaybeBlindedBeaconBlock
VCRuntimeConfig* = Table[string, string]

View File

@ -229,18 +229,6 @@ suite "REST JSON encoding and decoding":
blck.kzg_proofs.len == 0
blck.blobs.len == 0
test "RestPublishedSignedBlockContents decoding":
let blck = RestJson.decode(
denebSignedContents, RestPublishedSignedBlockContents,
requireAllFields = true, allowUnknownFields = true).denebData
check:
hash_tree_root(blck.signed_block.message) == Eth2Digest.fromHex(
"0xc67166e600d76d9d129244d10e4f35279d75d800fb39a5ce35e98328d53939da")
blck.signed_block.signature == ValidatorSig.fromHex(
"0x8e2cd6cf4457825818eb380f1ea74f2fc99665041194ab5bcbdbf96f2e22bad4376d2a94f69d762c999ffd500e2525ab0561b01a79158456c83cf5bf0f2104e26f7b0d22f41dcc8f49a0e1cc29bb09aee1c548903fa04bdfcd20603c400d948d")[]
blck.kzg_proofs.len == 0
blck.blobs.len == 0
test "KzgCommitment":
let
zeroString =

View File

@ -82,40 +82,28 @@ func getNodePort(basePort: int, rt: RemoteSignerType): int =
of RemoteSignerType.VerifyingWeb3Signer:
basePort + 1
func init(
T: type ForkedBeaconBlock, contents: RestPublishedSignedBlockContents): T =
case contents.kind
of ConsensusFork.Phase0 .. ConsensusFork.Bellatrix:
raiseAssert "Unsupported fork"
of ConsensusFork.Capella:
return ForkedBeaconBlock.init(contents.capellaData.message)
of ConsensusFork.Deneb:
return ForkedBeaconBlock.init(contents.denebData.signed_block.message)
of ConsensusFork.Electra:
return ForkedBeaconBlock.init(contents.electraData.signed_block.message)
proc getBlock(
fork: ConsensusFork,
feeRecipient = SigningExpectedFeeRecipient
): ForkedBeaconBlock {.raises: [ResultError[cstring]].} =
let blckData =
try:
case fork
of ConsensusFork.Phase0 .. ConsensusFork.Bellatrix:
raiseAssert "Unsupported fork"
of ConsensusFork.Capella: CapellaBlock % [feeRecipient, SomeSignature]
of ConsensusFork.Deneb:
DenebBlockContents % [feeRecipient, SomeSignature]
of ConsensusFork.Electra:
debugComment "electra test signing node getblock"
raiseAssert "electra unsupported"
except ValueError:
# https://github.com/nim-lang/Nim/pull/23356
raiseAssert "Arguments match the format string"
try:
ForkedBeaconBlock.init(RestJson.decode(
blckData, RestPublishedSignedBlockContents))
case fork
of ConsensusFork.Phase0 .. ConsensusFork.Bellatrix:
raiseAssert "Unsupported fork"
of ConsensusFork.Capella:
ForkedBeaconBlock.init(RestJson.decode(
CapellaBlock % [feeRecipient, SomeSignature],
capella.SignedBeaconBlock).message)
of ConsensusFork.Deneb:
ForkedBeaconBlock.init(RestJson.decode(
DenebBlockContents % [feeRecipient, SomeSignature],
DenebSignedBlockContents).signed_block.message)
of ConsensusFork.Electra:
debugComment "electra test signing node getblock"
raiseAssert "electra unsupported"
except ValueError:
# https://github.com/nim-lang/Nim/pull/23356
raiseAssert "Arguments match the format string"
except SerializationError:
raiseAssert "malformed block contents"