* Initial commit.

* One more fix.

* Trying to debug the finalization issue.

* Add debug logs to understand signature issue.

* Restore hash_tree_root calculation.

* Remove all the debugging helpers.

* Add `slot` check.

* Address review comment.
This commit is contained in:
Eugene Kabanov 2022-02-13 17:21:55 +02:00 committed by GitHub
parent 15fc7534cf
commit 1a0bcf0b02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 348 additions and 73 deletions

View File

@ -747,45 +747,25 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
# https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlock
router.api(MethodPost, "/eth/v1/beacon/blocks") do (
contentBody: Option[ContentBody]) -> RestApiResponse:
let forked =
let forkedBlock =
block:
if contentBody.isNone():
return RestApiResponse.jsonError(Http400, EmptyRequestBodyError)
let body = contentBody.get()
let altairRes = decodeBody(altair.SignedBeaconBlock, body)
if altairRes.isOk():
var res = altairRes.get()
if res.message.slot.epoch < node.dag.cfg.ALTAIR_FORK_EPOCH:
# This message deserialized successfully as altair but should
# actually be a phase0 block - try again with phase0
let phase0res = decodeBody(phase0.SignedBeaconBlock, body)
if phase0res.isOk():
var res = phase0res.get()
# `SignedBeaconBlock` deserialization do not update `root` field,
# so we need to calculate it.
res.root = hash_tree_root(res.message)
ForkedSignedBeaconBlock.init(res)
else:
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError,
$phase0res.error())
else:
# `SignedBeaconBlock` deserialization do not update `root` field,
# so we need to calculate it.
res.root = hash_tree_root(res.message)
ForkedSignedBeaconBlock.init(res)
else:
let phase0res = decodeBody(phase0.SignedBeaconBlock, body)
if phase0res.isOk():
var res = phase0res.get()
# `SignedBeaconBlock` deserialization do not update `root` field,
# so we need to calculate it.
res.root = hash_tree_root(res.message)
ForkedSignedBeaconBlock.init(res)
else:
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError,
$phase0res.error())
let res = decodeBody(RestPublishedSignedBeaconBlock, body)
if res.isErr():
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError,
$res.error())
var forked = ForkedSignedBeaconBlock(res.get())
if forked.kind != node.dag.cfg.blockForkAtEpoch(
getForkedBlockField(forked, slot).epoch):
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError)
let res = await node.sendBeaconBlock(forked)
withBlck(forked):
blck.root = hash_tree_root(blck.message)
forked
let res = await node.sendBeaconBlock(forkedBlock)
if res.isErr():
return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError)
if not(res.get()):

View File

@ -333,7 +333,7 @@ proc fromHex*(T: typedesc[BloomLogs], s: string): T {.raises: [Defect, ValueErro
proc fromHex*(T: typedesc[ExecutionAddress], s: string): T {.raises: [Defect, ValueError].} =
hexToByteArray(s, result.data)
proc writeValue*(w: var JsonWriter, a: ExecutionAddress) {.raises: [Defect, IOError, SerializationError].} =
proc writeValue*(w: var JsonWriter, a: ExecutionAddress) {.raises: [Defect, IOError].} =
w.writeValue $a
proc readValue*(r: var JsonReader, a: var ExecutionAddress) {.raises: [Defect, IOError, SerializationError].} =

View File

@ -53,6 +53,7 @@ type
ProposerSlashing |
phase0.SignedBeaconBlock |
altair.SignedBeaconBlock |
bellatrix.SignedBeaconBlock |
SignedVoluntaryExit |
Web3SignerRequest |
KeystoresAndSlashingProtection |
@ -678,7 +679,7 @@ proc readValue*(reader: var JsonReader[RestJson],
version = some(BeaconBlockFork.Phase0)
of "altair":
version = some(BeaconBlockFork.Altair)
of "merge":
of "bellatrix":
version = some(BeaconBlockFork.Bellatrix)
else:
reader.raiseUnexpectedValue("Incorrect version field value")
@ -724,7 +725,7 @@ proc readValue*(reader: var JsonReader[RestJson],
except SerializationError:
none[bellatrix.BeaconBlock]()
if res.isNone():
reader.raiseUnexpectedValue("Incorrect merge block format")
reader.raiseUnexpectedValue("Incorrect bellatrix block format")
value = ForkedBeaconBlock.init(res.get())
proc writeValue*(writer: var JsonWriter[RestJson], value: ForkedBeaconBlock) {.
@ -738,12 +739,304 @@ proc writeValue*(writer: var JsonWriter[RestJson], value: ForkedBeaconBlock) {.
writer.writeField("version", "altair")
writer.writeField("data", value.altairData)
of BeaconBlockFork.Bellatrix:
writer.writeField("version", "merge")
when false:
# TODO SerializationError
writer.writeField("data", value.bellatrixData)
writer.writeField("version", "bellatrix")
writer.writeField("data", value.bellatrixData)
writer.endRecord()
## RestPublishedBeaconBlockBody
proc readValue*(reader: var JsonReader[RestJson],
value: var RestPublishedBeaconBlockBody) {.
raises: [IOError, SerializationError, Defect].} =
var
randao_reveal: Option[ValidatorSig]
eth1_data: Option[Eth1Data]
graffiti: Option[GraffitiBytes]
proposer_slashings: Option[
List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]]
attester_slashings: Option[
List[AttesterSlashing, Limit MAX_ATTESTER_SLASHINGS]]
attestations: Option[List[Attestation, Limit MAX_ATTESTATIONS]]
deposits: Option[List[Deposit, Limit MAX_DEPOSITS]]
voluntary_exits: Option[
List[SignedVoluntaryExit, Limit MAX_VOLUNTARY_EXITS]]
sync_aggregate: Option[SyncAggregate]
execution_payload: Option[ExecutionPayload]
for fieldName in readObjectFields(reader):
case fieldName
of "randao_reveal":
if randao_reveal.isSome():
reader.raiseUnexpectedField("Multiple `randao_reveal` fields found",
"RestPublishedBeaconBlockBody")
randao_reveal = some(reader.readValue(ValidatorSig))
of "eth1_data":
if eth1_data.isSome():
reader.raiseUnexpectedField("Multiple `eth1_data` fields found",
"RestPublishedBeaconBlockBody")
eth1_data = some(reader.readValue(Eth1Data))
of "graffiti":
if graffiti.isSome():
reader.raiseUnexpectedField("Multiple `graffiti` fields found",
"RestPublishedBeaconBlockBody")
graffiti = some(reader.readValue(GraffitiBytes))
of "proposer_slashings":
if proposer_slashings.isSome():
reader.raiseUnexpectedField(
"Multiple `proposer_slashings` fields found",
"RestPublishedBeaconBlockBody")
proposer_slashings = 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 = some(
reader.readValue(List[AttesterSlashing, Limit MAX_ATTESTER_SLASHINGS]))
of "attestations":
if attestations.isSome():
reader.raiseUnexpectedField("Multiple `attestations` fields found",
"RestPublishedBeaconBlockBody")
attestations = some(
reader.readValue(List[Attestation, Limit MAX_ATTESTATIONS]))
of "deposits":
if deposits.isSome():
reader.raiseUnexpectedField("Multiple `deposits` fields found",
"RestPublishedBeaconBlockBody")
deposits = 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 = 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 = some(reader.readValue(SyncAggregate))
of "execution_payload":
if execution_payload.isSome():
reader.raiseUnexpectedField("Multiple `execution_payload` fields found",
"RestPublishedBeaconBlockBody")
execution_payload = some(reader.readValue(ExecutionPayload))
else:
# Ignore unknown fields
discard
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 sync_aggregate.isSome():
BeaconBlockFork.Bellatrix
elif execution_payload.isNone() and sync_aggregate.isSome():
BeaconBlockFork.Altair
else:
BeaconBlockFork.Phase0
case bodyKind
of BeaconBlockFork.Phase0:
value = RestPublishedBeaconBlockBody(
kind: BeaconBlockFork.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 BeaconBlockFork.Altair:
value = RestPublishedBeaconBlockBody(
kind: BeaconBlockFork.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 BeaconBlockFork.Bellatrix:
value = RestPublishedBeaconBlockBody(
kind: BeaconBlockFork.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(),
execution_payload: execution_payload.get()
)
)
## RestPublishedBeaconBlock
proc readValue*(reader: var JsonReader[RestJson],
value: var RestPublishedBeaconBlock) {.
raises: [IOError, SerializationError, Defect].} =
var
slot: Option[Slot]
proposer_index: Option[uint64]
parent_root: Option[Eth2Digest]
state_root: Option[Eth2Digest]
blockBody: Option[RestPublishedBeaconBlockBody]
for fieldName in readObjectFields(reader):
case fieldName
of "slot":
if slot.isSome():
reader.raiseUnexpectedField("Multiple `slot` fields found",
"RestPublishedBeaconBlock")
slot = some(reader.readValue(Slot))
of "proposer_index":
if proposer_index.isSome():
reader.raiseUnexpectedField("Multiple `proposer_index` fields found",
"RestPublishedBeaconBlock")
proposer_index = some(reader.readValue(uint64))
of "parent_root":
if parentRoot.isSome():
reader.raiseUnexpectedField("Multiple `parent_root` fields found",
"RestPublishedBeaconBlock")
parent_root = some(reader.readValue(Eth2Digest))
of "state_root":
if state_root.isSome():
reader.raiseUnexpectedField("Multiple `state_root` fields found",
"RestPublishedBeaconBlock")
state_root = some(reader.readValue(Eth2Digest))
of "body":
if blockBody.isSome():
reader.raiseUnexpectedField("Multiple `body` fields found",
"RestPublishedBeaconBlock")
blockBody = some(reader.readValue(RestPublishedBeaconBlockBody))
else:
# Ignore unknown fields
discard
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 BeaconBlockFork.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 BeaconBlockFork.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 BeaconBlockFork.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
)
)
)
## RestPublishedSignedBeaconBlock
proc readValue*(reader: var JsonReader[RestJson],
value: var RestPublishedSignedBeaconBlock) {.
raises: [IOError, SerializationError, Defect].} =
var signature: Option[ValidatorSig]
var message: Option[RestPublishedBeaconBlock]
for fieldName in readObjectFields(reader):
case fieldName
of "message":
if message.isSome():
reader.raiseUnexpectedField("Multiple `message` fields found",
"RestPublishedSignedBeaconBlock")
message = some(reader.readValue(RestPublishedBeaconBlock))
of "signature":
if signature.isSome():
reader.raiseUnexpectedField("Multiple `signature` fields found",
"RestPublishedSignedBeaconBlock")
signature = some(reader.readValue(ValidatorSig))
else:
# Ignore unknown fields
discard
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(
case blck.kind
of BeaconBlockFork.Phase0:
ForkedSignedBeaconBlock.init(
phase0.SignedBeaconBlock(
message: blck.phase0Data,
signature: signature.get()
)
)
of BeaconBlockFork.Altair:
ForkedSignedBeaconBlock.init(
altair.SignedBeaconBlock(
message: blck.altairData,
signature: signature.get()
)
)
of BeaconBlockFork.Bellatrix:
ForkedSignedBeaconBlock.init(
bellatrix.SignedBeaconBlock(
message: blck.bellatrixData,
signature: signature.get()
)
)
)
## ForkedSignedBeaconBlock
proc readValue*(reader: var JsonReader[RestJson],
value: var ForkedSignedBeaconBlock) {.
@ -764,7 +1057,7 @@ proc readValue*(reader: var JsonReader[RestJson],
version = some(BeaconBlockFork.Phase0)
of "altair":
version = some(BeaconBlockFork.Altair)
of "merge":
of "bellatrix":
version = some(BeaconBlockFork.Bellatrix)
else:
reader.raiseUnexpectedValue("Incorrect version field value")
@ -810,7 +1103,7 @@ proc readValue*(reader: var JsonReader[RestJson],
except SerializationError:
none[bellatrix.SignedBeaconBlock]()
if res.isNone():
reader.raiseUnexpectedValue("Incorrect merge block format")
reader.raiseUnexpectedValue("Incorrect bellatrix block format")
value = ForkedSignedBeaconBlock.init(res.get())
withBlck(value):
blck.root = hash_tree_root(blck.message)
@ -827,10 +1120,8 @@ proc writeValue*(writer: var JsonWriter[RestJson],
writer.writeField("version", "altair")
writer.writeField("data", value.altairData)
of BeaconBlockFork.Bellatrix:
writer.writeField("version", "merge")
when false:
# TODO SerializationError
writer.writeField("data", value.bellatrixData)
writer.writeField("version", "bellatrix")
writer.writeField("data", value.bellatrixData)
writer.endRecord()
# ForkedHashedBeaconState is used where a `ForkedBeaconState` normally would
@ -852,7 +1143,7 @@ proc readValue*(reader: var JsonReader[RestJson],
version = case vres
of "phase0": some(BeaconStateFork.Phase0)
of "altair": some(BeaconStateFork.Altair)
of "merge": some(BeaconStateFork.Bellatrix)
of "bellatrix": some(BeaconStateFork.Bellatrix)
else: reader.raiseUnexpectedValue("Incorrect version field value")
of "data":
if data.isSome():
@ -915,10 +1206,8 @@ proc writeValue*(writer: var JsonWriter[RestJson], value: ForkedHashedBeaconStat
writer.writeField("version", "altair")
writer.writeField("data", value.altairData.data)
of BeaconStateFork.Bellatrix:
writer.writeField("version", "merge")
when false:
# TODO SerializationError
writer.writeField("data", value.bellatrixData.data)
writer.writeField("version", "bellatrix")
writer.writeField("data", value.bellatrixData.data)
writer.endRecord()
# Web3SignerRequest

View File

@ -9,7 +9,7 @@
import
chronos, presto/client, chronicles,
".."/".."/validators/slashing_protection_common,
".."/datatypes/[phase0, altair],
".."/datatypes/[phase0, altair, bellatrix],
".."/[helpers, forks, keystore, eth2_ssz_serialization],
"."/[rest_types, rest_common, eth2_rest_serialization]
@ -68,7 +68,6 @@ proc getStateValidatorPlain*(state_id: StateIdent,
endpoint: "/eth/v1/beacon/states/{state_id}/validators/{validator_id}",
meth: MethodGet.}
## https://ethereum.github.io/beacon-APIs/#/Beacon/getStateValidator
##
proc getStateValidatorBalances*(state_id: StateIdent
): RestResponse[GetStateValidatorBalancesResponse] {.
@ -109,6 +108,11 @@ proc publishBlock*(body: altair.SignedBeaconBlock): RestPlainResponse {.
meth: MethodPost.}
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlock
proc publishBlock*(body: bellatrix.SignedBeaconBlock): RestPlainResponse {.
rest, endpoint: "/eth/v1/beacon/blocks",
meth: MethodPost.}
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlock
proc getBlockPlain*(block_id: BlockIdent): RestPlainResponse {.
rest, endpoint: "/eth/v1/beacon/blocks/{block_id}",
accept: preferSSZ,

View File

@ -17,9 +17,9 @@ import
std/[json, typetraits],
stew/base10, web3/ethtypes,
".."/forks,
".."/datatypes/[phase0, altair]
".."/datatypes/[phase0, altair, bellatrix]
export forks, phase0, altair
export forks, phase0, altair, bellatrix
const
# https://github.com/ethereum/eth2.0-APIs/blob/master/apis/beacon/states/validator_balances.yaml#L17
@ -226,11 +226,21 @@ type
attnets*: string
RestNetworkIdentity* = object
peer_id*: string
enr*: string
p2p_addresses*: seq[string]
discovery_addresses*: seq[string]
metadata*: RestMetadata
peer_id*: string
enr*: string
p2p_addresses*: seq[string]
discovery_addresses*: seq[string]
metadata*: RestMetadata
RestPublishedSignedBeaconBlock* = distinct ForkedSignedBeaconBlock
RestPublishedBeaconBlock* = distinct ForkedBeaconBlock
RestPublishedBeaconBlockBody* = object
case kind*: BeaconBlockFork
of BeaconBlockFork.Phase0: phase0Body*: phase0.BeaconBlockBody
of BeaconBlockFork.Altair: altairBody*: altair.BeaconBlockBody
of BeaconBlockFork.Bellatrix: bellatrixBody*: bellatrix.BeaconBlockBody
RestSpec* = object
# https://github.com/ethereum/consensus-specs/blob/v1.0.1/configs/mainnet/phase0.yaml

View File

@ -774,9 +774,7 @@ proc publishBlock*(vc: ValidatorClientRef,
of BeaconBlockFork.Altair:
publishBlock(it, data.altairData)
of BeaconBlockFork.Bellatrix:
raiseAssert "trying to publish merge block"
# TODO this doesn't build due to some nim-presto error
# publishBlock(it, data.bellatrixData)
publishBlock(it, data.bellatrixData)
do:
if apiResponse.isErr():
debug "Unable to publish block", endpoint = node,

View File

@ -564,7 +564,7 @@ proc proposeBlock(node: BeaconNode,
bellatrix.SignedBeaconBlock(
message: blck, signature: signature, root: blockRoot)
else:
static: doAssert "Unkown block type"
static: doAssert "Unknown SignedBeaconBlock type"
# We produced the block using a state transition, meaning the block is valid
# enough that it will not be rejected by gossip - it is unlikely but
@ -1254,14 +1254,8 @@ proc sendBeaconBlock*(node: BeaconNode, forked: ForkedSignedBeaconBlock
# Start with a quick gossip validation check such that broadcasting the
# block doesn't get the node into trouble
let res = withBlck(forked):
when blck isnot bellatrix.SignedBeaconBlock:
validateBeaconBlock(
node.dag, node.quarantine, blck, node.beaconClock.now(),
{})
else:
return SendBlockResult.err(
"TODO merge block proposal via REST not implemented")
validateBeaconBlock(node.dag, node.quarantine, blck,
node.beaconClock.now(), {})
if not res.isGoodForSending():
return SendBlockResult.err(res.error()[1])