Files
lodestar/packages/cli/src/cmds/dev/handler.ts
Lion - dapplion de1e1210b4 Separate lodestar-api package (#2568)
* Refactor REST API definitions

* Simplify REST testing

* Bump to fastify 3.x.x

* Return {} in fastify handler to trigger send

* Move REST server to lodestar-api

* Improve REST tests

* Add eventstream test

* Clean up

* Bump versions

* Fix query string format

* Add extra debug routes

* Consume lodestar-api package

* Fix tests

* Revert package.json change

* Add HttpClient test

* Destroy all active requests immediately on close

* Fastify hook handlers must resolve

* Fix fastify hook config args

* Fix parsing of ValidatorId

* Remove e2e script test

* Add docs

* Simplify req declarations

* Review PR

* Update license
2021-05-27 12:17:49 -05:00

123 lines
4.3 KiB
TypeScript

import fs from "fs";
import {promisify} from "util";
import rimraf from "rimraf";
import path from "path";
import {AbortController} from "abort-controller";
import {GENESIS_SLOT} from "@chainsafe/lodestar-params";
import {BeaconNode, BeaconDb, initStateFromAnchorState, createNodeJsLibp2p, nodeUtils} from "@chainsafe/lodestar";
import {SlashingProtection, Validator} from "@chainsafe/lodestar-validator";
import {LevelDbController} from "@chainsafe/lodestar-db";
import {onGracefulShutdown} from "../../util/process";
import {createEnr, createPeerId} from "../../config";
import {IGlobalArgs} from "../../options";
import {IDevArgs} from "./options";
import {initializeOptionsAndConfig} from "../init/handler";
import {mkdir, initBLS, getCliLogger} from "../../util";
import {getBeaconPaths} from "../beacon/paths";
import {getValidatorPaths} from "../validator/paths";
import {interopSecretKey} from "@chainsafe/lodestar-beacon-state-transition";
import {SecretKey} from "@chainsafe/bls";
/**
* Run a beacon node with validator
*/
export async function devHandler(args: IDevArgs & IGlobalArgs): Promise<void> {
await initBLS();
const {beaconNodeOptions, config} = await initializeOptionsAndConfig(args);
// ENR setup
const peerId = await createPeerId();
const enr = createEnr(peerId);
beaconNodeOptions.set({network: {discv5: {enr}}});
// Custom paths different than regular beacon, validator paths
// network="dev" will store all data in separate dir than other networks
args.network = "dev";
const beaconPaths = getBeaconPaths(args);
const validatorPaths = getValidatorPaths(args);
const beaconDbDir = beaconPaths.dbDir;
const validatorsDbDir = validatorPaths.validatorsDbDir;
mkdir(beaconDbDir);
mkdir(validatorsDbDir);
// TODO: Rename db.name to db.path or db.location
beaconNodeOptions.set({db: {name: beaconPaths.dbDir}});
const options = beaconNodeOptions.getWithDefaults();
// BeaconNode setup
const libp2p = await createNodeJsLibp2p(peerId, options.network);
const logger = getCliLogger(args, beaconPaths, config);
const db = new BeaconDb({config, controller: new LevelDbController(options.db, {logger})});
await db.start();
let anchorState;
if (args.genesisValidators) {
anchorState = await nodeUtils.initDevState(config, db, args.genesisValidators);
nodeUtils.storeSSZState(config, anchorState, path.join(args.rootDir, "dev", "genesis.ssz"));
} else if (args.genesisStateFile) {
anchorState = await initStateFromAnchorState(
config,
db,
logger,
config
.getForkTypes(GENESIS_SLOT)
.BeaconState.createTreeBackedFromBytes(
await fs.promises.readFile(path.join(args.rootDir, args.genesisStateFile))
)
);
} else {
throw new Error("Unable to start node: no available genesis state");
}
const validators: Validator[] = [];
const node = await BeaconNode.init({
opts: options,
config,
db,
logger,
libp2p,
anchorState,
});
const onGracefulShutdownCbs: (() => Promise<void>)[] = [];
onGracefulShutdown(async () => {
for (const cb of onGracefulShutdownCbs) await cb();
await Promise.all([Promise.all(validators.map((v) => v.stop())), node.close()]);
if (args.reset) {
logger.info("Cleaning db directories");
await promisify(rimraf)(beaconDbDir);
await promisify(rimraf)(validatorsDbDir);
}
}, logger.info.bind(logger));
if (args.startValidators) {
const secretKeys: SecretKey[] = [];
const [fromIndex, toIndex] = args.startValidators.split(":").map((s) => parseInt(s));
for (let i = fromIndex; i < toIndex; i++) {
secretKeys.push(interopSecretKey(i));
}
const dbPath = path.join(validatorsDbDir, "validators");
fs.mkdirSync(dbPath, {recursive: true});
const api = args.server === "memory" ? node.api : args.server;
const slashingProtection = new SlashingProtection({
config: config,
controller: new LevelDbController({name: dbPath}, {logger}),
});
const controller = new AbortController();
onGracefulShutdownCbs.push(async () => controller.abort());
// Initailize genesis once for all validators
const validator = await Validator.initializeFromBeaconNode({config, slashingProtection, api, logger, secretKeys});
onGracefulShutdownCbs.push(() => validator.stop());
await validator.start();
}
}