Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow trusted node sync based on LC trusted block root #4736

Merged
merged 12 commits into from
Apr 16, 2023
8 changes: 6 additions & 2 deletions beacon_chain/conf.nim
Original file line number Diff line number Diff line change
Expand Up @@ -779,15 +779,19 @@ type

stateId* {.
desc: "State id to sync to - this can be \"finalized\", a slot number or state hash or \"head\""
defaultValue: "finalized",
name: "state-id"
.}: string
.}: Option[string]

blockId* {.
hidden
desc: "Block id to sync to - this can be a block root, slot number, \"finalized\" or \"head\" (deprecated)"
.}: Option[string]

lcTrustedBlockRoot* {.
hidden
desc: "Recent trusted finalized block root to initialize light client from"
tersec marked this conversation as resolved.
Show resolved Hide resolved
name: "trusted-block-root" .}: Option[Eth2Digest]

backfillBlocks* {.
desc: "Backfill blocks directly from REST server instead of fetching via API"
defaultValue: true
Expand Down
19 changes: 18 additions & 1 deletion beacon_chain/nimbus_beacon_node.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1981,6 +1981,23 @@ proc handleStartUpCmd(config: var BeaconNodeConf) {.raises: [Defect, CatchableEr
let
network = loadEth2Network(config)
cfg = network.cfg
syncTarget =
if config.stateId.isSome:
if config.lcTrustedBlockRoot.isSome:
warn "Ignoring `trustedBlockRoot`, `stateId` is set",
stateId = config.stateId,
trustedBlockRoot = config.lcTrustedBlockRoot
TrustedNodeSyncTarget(
kind: TrustedNodeSyncKind.StateId,
stateId: config.stateId.get)
elif config.lcTrustedBlockRoot.isSome:
TrustedNodeSyncTarget(
kind: TrustedNodeSyncKind.TrustedBlockRoot,
trustedBlockRoot: config.lcTrustedBlockRoot.get)
else:
TrustedNodeSyncTarget(
kind: TrustedNodeSyncKind.StateId,
stateId: "finalized")
genesis =
if network.genesisData.len > 0:
newClone(readSszForkedHashedBeaconState(
Expand All @@ -1997,7 +2014,7 @@ proc handleStartUpCmd(config: var BeaconNodeConf) {.raises: [Defect, CatchableEr
config.databaseDir,
config.eraDir,
config.trustedNodeUrl,
config.stateId,
syncTarget,
config.backfillBlocks,
config.reindex,
config.downloadDepositSnapshot,
Expand Down
70 changes: 68 additions & 2 deletions beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ export
from web3/ethtypes import BlockHash
export ethtypes.BlockHash

func decodeMediaType*(
contentType: Opt[ContentTypeData]): Result[MediaType, string] =
if contentType.isNone or isWildCard(contentType.get.mediaType):
return err("Missing or incorrect Content-Type")
ok contentType.get.mediaType

func decodeEthConsensusVersion*(
value: string): Result[ConsensusFork, string] =
let normalizedValue = value.toLowerAscii()
for consensusFork in ConsensusFork:
if normalizedValue == ($consensusFork).toLowerAscii():
return ok consensusFork
err("Unsupported Eth-Consensus-Version: " & value)

Json.createFlavor RestJson

## The RestJson format implements JSON serialization in the way specified
Expand Down Expand Up @@ -136,7 +150,9 @@ type
Web3SignerSignatureResponse |
Web3SignerStatusResponse |
GetStateRootResponse |
GetBlockRootResponse
GetBlockRootResponse |
SomeForkedLightClientObject |
seq[SomeForkedLightClientObject]

RestVersioned*[T] = object
data*: T
Expand Down Expand Up @@ -1852,6 +1868,49 @@ proc writeValue*(writer: var JsonWriter[RestJson], value: ForkedHashedBeaconStat
writer.writeField("data", value.denebData.data)
writer.endRecord()

## SomeForkedLightClientObject
proc readValue*[T: SomeForkedLightClientObject](
reader: var JsonReader[RestJson], value: var T) {.
raises: [IOError, SerializationError, Defect].} =
var
version: Opt[ConsensusFork]
data: Opt[JsonString]

for fieldName in readObjectFields(reader):
case fieldName
of "version":
if version.isSome:
reader.raiseUnexpectedField("Multiple version fields found", T.name)
let consensusFork =
decodeEthConsensusVersion(reader.readValue(string)).valueOr:
reader.raiseUnexpectedValue("Incorrect version field value")
version.ok consensusFork
of "data":
if data.isSome:
reader.raiseUnexpectedField("Multiple data fields found", T.name)
data.ok reader.readValue(JsonString)
else:
unrecognizedFieldWarning()

if version.isNone:
reader.raiseUnexpectedValue("Field version is missing")
if data.isNone:
reader.raiseUnexpectedValue("Field data is missing")

withLcDataFork(lcDataForkAtConsensusFork(version.get)):
when lcDataFork > LightClientDataFork.None:
value = T(kind: lcDataFork)
try:
value.forky(lcDataFork) = RestJson.decode(
string(data.get()),
T.Forky(lcDataFork),
requireAllFields = true,
allowUnknownFields = true)
except SerializationError:
reader.raiseUnexpectedValue("Incorrect format (" & $lcDataFork & ")")
else:
reader.raiseUnexpectedValue("Unsupported fork " & $version.get)

## Web3SignerRequest
proc writeValue*(writer: var JsonWriter[RestJson],
value: Web3SignerRequest) {.
Expand Down Expand Up @@ -2959,7 +3018,14 @@ proc decodeBytes*[T: DecodeTypes](
proc encodeString*(value: string): RestResult[string] =
ok(value)

proc encodeString*(value: Epoch|Slot|CommitteeIndex|SyncSubcommitteeIndex): RestResult[string] =
proc encodeString*(
value:
uint64 |
SyncCommitteePeriod |
Epoch |
Slot |
CommitteeIndex |
SyncSubcommitteeIndex): RestResult[string] =
ok(Base10.toString(uint64(value)))

proc encodeString*(value: ValidatorSig): RestResult[string] =
Expand Down
6 changes: 4 additions & 2 deletions beacon_chain/spec/eth2_apis/rest_beacon_client.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import
chronos, presto/client,
"."/[
rest_beacon_calls, rest_config_calls, rest_debug_calls,
rest_node_calls, rest_validator_calls, rest_keymanager_calls,
rest_keymanager_calls, rest_light_client_calls,
rest_node_calls, rest_validator_calls,
rest_nimbus_calls, rest_common
]

export
chronos, client,
rest_beacon_calls, rest_config_calls, rest_debug_calls,
rest_node_calls, rest_validator_calls, rest_keymanager_calls,
rest_keymanager_calls, rest_light_client_calls,
rest_node_calls, rest_validator_calls,
rest_nimbus_calls, rest_common
23 changes: 23 additions & 0 deletions beacon_chain/spec/eth2_apis/rest_common.nim
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,26 @@ proc raiseUnknownStatusError*(resp: RestPlainResponse) {.
noreturn, raises: [RestError, Defect].} =
let msg = "Unknown response status error (" & $resp.status & ")"
raise newException(RestError, msg)

proc getBodyBytesWithCap*(
response: HttpClientResponseRef,
maxBytes: int): Future[Opt[seq[byte]]] {.async.} =
var reader = response.getBodyReader()
try:
let
data = await reader.read(maxBytes)
isComplete = reader.atEof()
await reader.closeWait()
reader = nil
await response.finish()
if not isComplete:
return err()
return ok data
except CancelledError as exc:
if not(isNil(reader)):
await reader.closeWait()
raise exc
except AsyncStreamError:
if not(isNil(reader)):
await reader.closeWait()
raise newHttpReadError("Could not read response")
Loading