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:
parent
31b5c3e30e
commit
f2d6166099
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
Loading…
Reference in New Issue