-
-
Notifications
You must be signed in to change notification settings - Fork 311
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
chore: review lightclient routes #6842
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,19 @@ | ||
/* eslint-disable @typescript-eslint/naming-convention */ | ||
import {ListCompositeType, ValueOf, byteArrayEquals} from "@chainsafe/ssz"; | ||
import {ssz, SyncPeriod, allForks, deneb} from "@lodestar/types"; | ||
import {ListCompositeType, ValueOf} from "@chainsafe/ssz"; | ||
import {ssz, SyncPeriod, allForks} from "@lodestar/types"; | ||
import {ForkName} from "@lodestar/params"; | ||
import {BeaconConfig, ChainForkConfig, createBeaconConfig} from "@lodestar/config"; | ||
import {ChainForkConfig} from "@lodestar/config"; | ||
import {Endpoint, RouteDefinitions, Schema} from "../../utils/index.js"; | ||
import {MetaHeader, VersionCodec, VersionMeta} from "../../utils/metadata.js"; | ||
import {getLightClientForkTypes} from "../../utils/fork.js"; | ||
import {VersionCodec, VersionMeta} from "../../utils/metadata.js"; | ||
import {getLightClientForkTypes, toForkName} from "../../utils/fork.js"; | ||
import { | ||
EmptyArgs, | ||
EmptyRequestCodec, | ||
EmptyMeta, | ||
EmptyMetaCodec, | ||
EmptyRequest, | ||
WithVersion, | ||
JsonOnlyResp, | ||
} from "../../utils/codecs.js"; | ||
|
||
// See /packages/api/src/routes/index.ts for reasoning and instructions to add new routes | ||
|
@@ -30,12 +31,7 @@ export type Endpoints = { | |
*/ | ||
getLightClientUpdatesByRange: Endpoint< | ||
"GET", | ||
{ | ||
startPeriod: SyncPeriod; | ||
count: number; | ||
/** Only required for ssz response wire encoding */ | ||
genesisValidatorsRoot?: Uint8Array; | ||
}, | ||
{startPeriod: SyncPeriod; count: number}, | ||
{query: {start_period: number; count: number}}, | ||
allForks.LightClientUpdate[], | ||
{versions: ForkName[]} | ||
|
@@ -88,116 +84,64 @@ export type Endpoints = { | |
>; | ||
}; | ||
|
||
export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoints> { | ||
// this beacon config will be stored here in this closure so fork digests don't need to be recomputed | ||
let beaconConfig: BeaconConfig | undefined; | ||
|
||
export function getDefinitions(_config: ChainForkConfig): RouteDefinitions<Endpoints> { | ||
return { | ||
getLightClientUpdatesByRange: { | ||
url: "/eth/v1/beacon/light_client/updates", | ||
method: "GET", | ||
req: { | ||
writeReq: ({startPeriod, count, genesisValidatorsRoot}) => { | ||
// store the beacon config if its a new genesisValidatorsRoot | ||
if ( | ||
genesisValidatorsRoot && | ||
(beaconConfig === undefined || !byteArrayEquals(beaconConfig.genesisValidatorsRoot, genesisValidatorsRoot)) | ||
) { | ||
beaconConfig = createBeaconConfig(config, genesisValidatorsRoot); | ||
} | ||
return {query: {start_period: startPeriod, count}}; | ||
}, | ||
writeReq: ({startPeriod, count}) => ({query: {start_period: startPeriod, count}}), | ||
parseReq: ({query}) => ({startPeriod: query.start_period, count: query.count}), | ||
schema: {query: {start_period: Schema.UintRequired, count: Schema.UintRequired}}, | ||
}, | ||
resp: { | ||
resp: JsonOnlyResp({ | ||
data: { | ||
toJson: (data, meta) => { | ||
if (data.length !== meta.versions.length) { | ||
throw new Error("TODO"); // TODO | ||
} | ||
const json: unknown[] = []; | ||
for (const [i, version] of meta.versions.entries()) { | ||
json.push(getLightClientForkTypes(version).LightClientUpdate.toJson(data[i] as deneb.LightClientUpdate)); | ||
for (const [i, update] of data.entries()) { | ||
json.push(getLightClientForkTypes(meta.versions[i]).LightClientUpdate.toJson(update)); | ||
} | ||
return json; | ||
}, | ||
fromJson: (data, meta) => { | ||
const updates = data as unknown[]; | ||
const value: allForks.LightClientUpdate[] = []; | ||
for (let i = 0; i < meta.versions.length; i++) { | ||
for (let i = 0; i < updates.length; i++) { | ||
const version = meta.versions[i]; | ||
value.push(getLightClientForkTypes(version).LightClientUpdate.fromJson((data as unknown[])[i])); | ||
} | ||
return value; | ||
}, | ||
serialize: (data, meta) => { | ||
if (data.length !== meta.versions.length) { | ||
throw new Error("TODO"); // TODO | ||
} | ||
if (!beaconConfig) { | ||
throw new Error("TODO"); // TODO | ||
} | ||
const bufs: Uint8Array[] = []; | ||
for (const [i, version] of meta.versions.entries()) { | ||
const forkDigest = beaconConfig.forkName2ForkDigest(version); | ||
const serialized = getLightClientForkTypes(version).LightClientUpdate.serialize( | ||
data[i] as deneb.LightClientUpdate | ||
); | ||
const length = ssz.UintNum64.serialize(4 + serialized.length); | ||
bufs.push(length, forkDigest, serialized); | ||
} | ||
return Buffer.concat(bufs); | ||
}, | ||
deserialize: (data) => { | ||
if (!beaconConfig) { | ||
throw new Error("TODO"); // TODO | ||
} | ||
let offset = 0; | ||
const value: allForks.LightClientUpdate[] = []; | ||
while (offset < data.length) { | ||
const length = ssz.UintNum64.deserialize(data.subarray(offset, offset + 8)); | ||
const forkDigest = ssz.ForkDigest.deserialize(data.subarray(offset + 8, offset + 12)); | ||
const version = beaconConfig.forkDigest2ForkName(forkDigest); | ||
value.push( | ||
getLightClientForkTypes(version).LightClientUpdate.deserialize( | ||
data.subarray(offset + 12, offset + length) | ||
) | ||
); | ||
offset += length; | ||
value.push(getLightClientForkTypes(version).LightClientUpdate.fromJson(updates[i])); | ||
} | ||
return value; | ||
}, | ||
}, | ||
meta: { | ||
toJson: (meta) => meta, | ||
fromJson: (val) => val as {versions: ForkName[]}, | ||
toHeadersObject: (meta) => ({ | ||
[MetaHeader.Version]: meta.versions.join(","), | ||
}), | ||
fromHeaders: (headers) => ({ | ||
// TODO: this header format is not spec compliant, do we care? | ||
versions: headers.getRequired(MetaHeader.Version).split(",") as ForkName[], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as spec already has a well-defined ssz response, we should make sure to be compliant, also ensure we work with Nimbus |
||
}), | ||
toHeadersObject: () => ({}), | ||
fromHeaders: () => ({versions: []}), | ||
}, | ||
transform: { | ||
toResponse: (data, meta) => { | ||
const r: unknown[] = []; | ||
for (let i = 0; i < (data as unknown[]).length; i++) { | ||
r.push({data: (data as unknown[])[i], version: (meta as {versions: string[]}).versions[i]}); | ||
const updates = data as unknown[]; | ||
const resp: unknown[] = []; | ||
for (let i = 0; i < updates.length; i++) { | ||
resp.push({data: updates[i], version: (meta as {versions: string[]}).versions[i]}); | ||
} | ||
return r; | ||
return resp; | ||
}, | ||
fromResponse: (resp) => { | ||
const d: allForks.LightClientUpdate[] = []; | ||
if (!Array.isArray(resp)) { | ||
throw Error("JSON is not an array"); | ||
} | ||
const updates: allForks.LightClientUpdate[] = []; | ||
const meta: {versions: ForkName[]} = {versions: []}; | ||
for (const {data, version} of resp as {data: allForks.LightClientUpdate; version: ForkName}[]) { | ||
d.push(data); | ||
meta.versions.push(version); | ||
for (const {data, version} of resp as {data: allForks.LightClientUpdate; version: string}[]) { | ||
updates.push(data); | ||
meta.versions.push(toForkName(version)); | ||
} | ||
return {data: d, meta}; | ||
return {data: updates, meta}; | ||
}, | ||
}, | ||
}, | ||
}), | ||
}, | ||
getLightClientOptimisticUpdate: { | ||
url: "/eth/v1/beacon/light_client/optimistic_update", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,6 +59,8 @@ const ignoredOperations = [ | |
"getBlindedBlock", // https://github.com/ChainSafe/lodestar/issues/5699 | ||
"getNextWithdrawals", // https://github.com/ChainSafe/lodestar/issues/5696 | ||
"getDebugForkChoice", // https://github.com/ChainSafe/lodestar/issues/5700 | ||
/* Must support ssz response body */ | ||
"getLightClientUpdatesByRange", // https://github.com/ChainSafe/lodestar/issues/6841 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ran this manually, json spec tests are passing but for now need to exclude the whole route as ssz tests are failing |
||
]; | ||
|
||
const ignoredProperties: Record<string, IgnoredProperty> = { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this check seemed a bit unnecessary this known data we get from server impl
lodestar/packages/beacon-node/src/api/impl/lightclient/index.ts
Line 20 in f777b31
I guess it would improve the error message if there is a bug in the server impl but it would latest fail at line 102, I changed the for-loop to go over data instead of meta which seems the better approach.