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

Add dumpDb debug routes #4418

Merged
merged 2 commits into from
Aug 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 21 additions & 0 deletions packages/api/src/beacon/routes/lodestar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ export type Api = {

/** Dump Discv5 Kad values */
discv5GetKadValues(): Promise<{data: string[]}>;

/**
* Dump level-db entry keys for a given Bucket declared in code, or for all buckets.
* @param bucket must be the string name of a bucket entry: `allForks_blockArchive`
*/
dumpDbBucketKeys(bucket: string): Promise<string[]>;

/** Return all entries in the StateArchive index with bucket index_stateArchiveRootIndex */
dumpDbStateIndex(): Promise<{root: RootHex; slot: Slot}[]>;
};

/**
Expand All @@ -111,6 +120,8 @@ export const routesData: RoutesData<Api> = {
disconnectPeer: {url: "/eth/v1/lodestar/disconnect_peer", method: "POST"},
getPeers: {url: "/eth/v1/lodestar/peers", method: "GET"},
discv5GetKadValues: {url: "/eth/v1/debug/discv5-kad-values", method: "GET"},
dumpDbBucketKeys: {url: "/eth/v1/debug/dump-db-bucket-keys/:bucket", method: "GET"},
dumpDbStateIndex: {url: "/eth/v1/debug/dump-db-state-index", method: "GET"},
};

export type ReqTypes = {
Expand All @@ -129,6 +140,8 @@ export type ReqTypes = {
disconnectPeer: {query: {peerId: string}};
getPeers: {query: {state?: PeerState[]; direction?: PeerDirection[]}};
discv5GetKadValues: ReqEmpty;
dumpDbBucketKeys: {params: {bucket: string}};
dumpDbStateIndex: ReqEmpty;
};

export function getReqSerializers(): ReqSerializers<Api, ReqTypes> {
Expand Down Expand Up @@ -168,6 +181,12 @@ export function getReqSerializers(): ReqSerializers<Api, ReqTypes> {
schema: {query: {state: Schema.StringArray, direction: Schema.StringArray}},
},
discv5GetKadValues: reqEmpty,
dumpDbBucketKeys: {
writeReq: (bucket) => ({params: {bucket}}),
parseReq: ({params}) => [params.bucket],
schema: {params: {bucket: Schema.String}},
},
dumpDbStateIndex: reqEmpty,
};
}

Expand All @@ -185,5 +204,7 @@ export function getReturnTypes(): ReturnTypes<Api> {
getGossipPeerScoreStats: jsonType("snake"),
getPeers: jsonType("snake"),
discv5GetKadValues: jsonType("snake"),
dumpDbBucketKeys: sameType(),
dumpDbStateIndex: sameType(),
};
}
36 changes: 35 additions & 1 deletion packages/beacon-node/src/api/impl/lodestar/index.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
import PeerId from "peer-id";
import {Multiaddr} from "multiaddr";
import {routes} from "@lodestar/api";
import {Bucket, Repository} from "@lodestar/db";
import {toHex} from "@lodestar/utils";
import {getLatestWeakSubjectivityCheckpointEpoch} from "@lodestar/state-transition";
import {toHexString} from "@chainsafe/ssz";
import {IChainForkConfig} from "@lodestar/config";
import {ssz} from "@lodestar/types";
import {BeaconChain} from "../../../chain/index.js";
import {QueuedStateRegenerator, RegenRequest} from "../../../chain/regen/index.js";
import {GossipType} from "../../../network/index.js";
import {IBeaconDb} from "../../../db/interface.js";
import {ApiModules} from "../types.js";
import {formatNodePeer} from "../node/utils.js";

export function getLodestarApi({
chain,
config,
db,
network,
sync,
}: Pick<ApiModules, "chain" | "config" | "network" | "sync">): routes.lodestar.Api {
}: Pick<ApiModules, "chain" | "config" | "db" | "network" | "sync">): routes.lodestar.Api {
let writingHeapdump = false;

return {
Expand Down Expand Up @@ -153,6 +157,23 @@ export function getLodestarApi({
data: network.discv5?.kadValues().map((enr) => enr.encodeTxt()) ?? [],
};
},

async dumpDbBucketKeys(bucketReq) {
for (const repo of Object.values(db) as IBeaconDb[keyof IBeaconDb][]) {
if (repo instanceof Repository) {
const bucket = (repo as RepositoryAny)["bucket"];
if (bucket === bucket || Bucket[bucket] === bucketReq) {
return stringifyKeys(await repo.keys());
}
}
}

throw Error(`Unknown Bucket '${bucketReq}' available: ${Object.keys(Bucket).join(", ")}`);
},

async dumpDbStateIndex() {
return db.stateArchive.dumpRootIndexEntries();
},
};
}

Expand Down Expand Up @@ -181,3 +202,16 @@ function regenRequestToJson(config: IChainForkConfig, regenRequest: RegenRequest
};
}
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type RepositoryAny = Repository<any, any>;

function stringifyKeys(keys: (Uint8Array | number | string)[]): string[] {
return keys.map((key) => {
if (key instanceof Uint8Array) {
return toHex(key);
} else {
return `${key}`;
}
});
}
15 changes: 13 additions & 2 deletions packages/beacon-node/src/db/repositories/stateArchive.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {BeaconStateAllForks} from "@lodestar/state-transition";
import {Epoch, Root, Slot, ssz} from "@lodestar/types";
import {Epoch, Root, RootHex, Slot, ssz} from "@lodestar/types";
import {IChainForkConfig} from "@lodestar/config";
import {bytesToInt} from "@lodestar/utils";
import {bytesToInt, toHex} from "@lodestar/utils";
import {Db, Bucket, Repository} from "@lodestar/db";
import {getStateTypeFromBytes} from "../../util/multifork.js";
import {getRootIndexKey, storeRootIndex} from "./stateArchiveIndex.js";
Expand Down Expand Up @@ -50,6 +50,17 @@ export class StateArchiveRepository extends Repository<Slot, BeaconStateAllForks
return null;
}

async dumpRootIndexEntries(): Promise<{root: RootHex; slot: Slot}[]> {
const entries = await this.db.entries({
lte: getRootIndexKey(Buffer.alloc(32, 0xff)),
gte: getRootIndexKey(Buffer.alloc(32, 0x00)),
});
return entries.map((entry) => ({
root: toHex(entry.key),
slot: bytesToInt(entry.value, "be"),
}));
}

private async getSlotByRoot(root: Root): Promise<Slot | null> {
const value = await this.db.get(getRootIndexKey(root));
return value && bytesToInt(value, "be");
Expand Down