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

feat: add debug_getHistoricalSummaries endpoint #7245

Merged
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion packages/api/src/beacon/routes/lodestar.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import {ContainerType, ValueOf} from "@chainsafe/ssz";
import {ChainForkConfig} from "@lodestar/config";
import {Epoch, RootHex, Slot} from "@lodestar/types";
import {Epoch, RootHex, Slot, ssz} from "@lodestar/types";
import {
ArrayOf,
EmptyArgs,
EmptyMeta,
EmptyMetaCodec,
EmptyRequest,
EmptyRequestCodec,
EmptyResponseCodec,
EmptyResponseData,
JsonOnlyResponseCodec,
} from "../../utils/codecs.js";
import {Endpoint, RouteDefinitions, Schema} from "../../utils/index.js";
import {StateArgs} from "./beacon/state.js";
import {FilterGetPeers, NodePeer, PeerDirection, PeerState} from "./node.js";

// See /packages/api/src/routes/index.ts for reasoning and instructions to add new routes
Expand Down Expand Up @@ -75,6 +79,16 @@ export type LodestarNodePeer = NodePeer & {

export type LodestarThreadType = "main" | "network" | "discv5";

const HistoricalSummariesResponseType = new ContainerType(
{
historicalSummaries: ssz.capella.HistoricalSummaries,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@acolytec3 I converted this to lower case to align it with existing json responses

proof: ArrayOf(ssz.Bytes8),
},
{jsonCase: "eth2"}
);

export type HistoricalSummariesResponse = ValueOf<typeof HistoricalSummariesResponseType>;

export type Endpoints = {
/** Trigger to write a heapdump to disk at `dirpath`. May take > 1min */
writeHeapdump: Endpoint<
Expand Down Expand Up @@ -214,6 +228,16 @@ export type Endpoints = {
{count: number}
>;

/** Returns historical summaries and proof for a given state ID */
getHistoricalSummaries: Endpoint<
// ⏎
"GET",
StateArgs,
{params: {state_id: string}},
HistoricalSummariesResponse,
EmptyMeta
>;

/** Dump Discv5 Kad values */
discv5GetKadValues: Endpoint<
// ⏎
Expand Down Expand Up @@ -365,6 +389,21 @@ export function getDefinitions(_config: ChainForkConfig): RouteDefinitions<Endpo
},
resp: JsonOnlyResponseCodec,
},
getHistoricalSummaries: {
url: "/eth/v1/lodestar/historical_summaries/{state_id}",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to use v1 since it's now part of lodestar namespace

method: "GET",
req: {
writeReq: ({stateId}) => ({params: {state_id: stateId.toString()}}),
parseReq: ({params}) => ({stateId: params.state_id}),
schema: {
params: {state_id: Schema.StringRequired},
},
},
resp: {
data: HistoricalSummariesResponseType,
meta: EmptyMetaCodec,
},
},
discv5GetKadValues: {
url: "/eth/v1/debug/discv5_kad_values",
method: "GET",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {config} from "@lodestar/config/default";
import {FastifyInstance} from "fastify";
import {afterAll, beforeAll, describe, expect, it, vi} from "vitest";
import {getClient} from "../../../../src/beacon/client/lodestar.js";
import {Endpoints, getDefinitions} from "../../../../src/beacon/routes/lodestar.js";
import {getRoutes} from "../../../../src/beacon/server/lodestar.js";
import {HttpClient} from "../../../../src/utils/client/httpClient.js";
import {AnyEndpoint} from "../../../../src/utils/codecs.js";
import {FastifyRoute} from "../../../../src/utils/server/index.js";
import {WireFormat} from "../../../../src/utils/wireFormat.js";
import {getMockApi, getTestServer} from "../../../utils/utils.js";

describe("beacon / lodestar", () => {
describe("get HistoricalSummaries as json", () => {
const mockApi = getMockApi<Endpoints>(getDefinitions(config));
let baseUrl: string;
let server: FastifyInstance;

beforeAll(async () => {
const res = getTestServer();
server = res.server;
for (const route of Object.values(getRoutes(config, mockApi))) {
server.route(route as FastifyRoute<AnyEndpoint>);
}
baseUrl = await res.start();
});

afterAll(async () => {
if (server !== undefined) await server.close();
});

it("getHistoricalSummaries", async () => {
mockApi.getHistoricalSummaries.mockResolvedValue({
data: {
historicalSummaries: [],
proof: [],
},
});

const httpClient = new HttpClient({baseUrl});
const client = getClient(config, httpClient);

const res = await client.getHistoricalSummaries({stateId: "head"}, {responseWireFormat: WireFormat.json});

expect(res.ok).toBe(true);
expect(res.wireFormat()).toBe(WireFormat.json);
expect(res.json().data).toStrictEqual({
historical_summaries: [],
proof: [],
});
});
});
});
2 changes: 1 addition & 1 deletion packages/beacon-node/src/api/impl/debug/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {routes} from "@lodestar/api";
import {ApplicationMethods} from "@lodestar/api/server";
import {ExecutionStatus} from "@lodestar/fork-choice";
import {ZERO_HASH_HEX} from "@lodestar/params";
import {BeaconState} from "@lodestar/types";
import {BeaconState, ssz} from "@lodestar/types";
import {isOptimisticBlock} from "../../../util/forkChoice.js";
import {getStateSlotFromBytes} from "../../../util/multifork.js";
import {getStateResponseWithRegen} from "../beacon/state/utils.js";
Expand Down
29 changes: 27 additions & 2 deletions packages/beacon-node/src/api/impl/lodestar/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import fs from "node:fs";
import path from "node:path";
import {Tree} from "@chainsafe/persistent-merkle-tree";
import {routes} from "@lodestar/api";
import {ApplicationMethods} from "@lodestar/api/server";
import {ChainForkConfig} from "@lodestar/config";
import {Repository} from "@lodestar/db";
import {SLOTS_PER_EPOCH} from "@lodestar/params";
import {getLatestWeakSubjectivityCheckpointEpoch} from "@lodestar/state-transition";
import {ForkSeq, SLOTS_PER_EPOCH} from "@lodestar/params";
import {BeaconStateCapella, getLatestWeakSubjectivityCheckpointEpoch, loadState} from "@lodestar/state-transition";
import {ssz} from "@lodestar/types";
import {toHex, toRootHex} from "@lodestar/utils";
import {BeaconChain} from "../../../chain/index.js";
import {QueuedStateRegenerator, RegenRequest} from "../../../chain/regen/index.js";
import {IBeaconDb} from "../../../db/interface.js";
import {GossipType} from "../../../network/index.js";
import {profileNodeJS, writeHeapSnapshot} from "../../../util/profile.js";
import {getStateResponseWithRegen} from "../beacon/state/utils.js";
import {ApiModules} from "../types.js";

export function getLodestarApi({
Expand Down Expand Up @@ -187,6 +189,29 @@ export function getLodestarApi({
async dumpDbStateIndex() {
return {data: await db.stateArchive.dumpRootIndexEntries()};
},

async getHistoricalSummaries({stateId}) {
const {state} = await getStateResponseWithRegen(chain, stateId);

const stateView = (
state instanceof Uint8Array ? loadState(config, chain.getHeadState(), state).state : state.clone()
) as BeaconStateCapella;

const fork = config.getForkName(stateView.slot);
if (ForkSeq[fork] < ForkSeq.capella) {
throw new Error("Historical summaries are not supported before Capella");
}

const {gindex} = ssz[fork].BeaconState.getPathInfo(["historicalSummaries"]);
const proof = new Tree(stateView.node).getSingleProof(gindex);

return {
data: {
historicalSummaries: stateView.historicalSummaries.toValue(),
proof: proof,
},
};
},
};
}

Expand Down
6 changes: 5 additions & 1 deletion packages/types/src/capella/sszTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ export const HistoricalSummary = new ContainerType(
{typeName: "HistoricalSummary", jsonCase: "eth2"}
);

export const HistoricalSummaries = new ListCompositeType(HistoricalSummary, HISTORICAL_ROOTS_LIMIT, {
typeName: "HistoricalSummaries",
});

// we don't reuse bellatrix.BeaconState fields since we need to replace some keys
// and we cannot keep order doing that
export const BeaconState = new ContainerType(
Expand Down Expand Up @@ -168,7 +172,7 @@ export const BeaconState = new ContainerType(
nextWithdrawalIndex: WithdrawalIndex, // [New in Capella]
nextWithdrawalValidatorIndex: ValidatorIndex, // [New in Capella]
// Deep history valid from Capella onwards
historicalSummaries: new ListCompositeType(HistoricalSummary, HISTORICAL_ROOTS_LIMIT), // [New in Capella]
historicalSummaries: HistoricalSummaries, // [New in Capella]
},
{typeName: "BeaconState", jsonCase: "eth2"}
);
Expand Down
Loading