Skip to content

Commit

Permalink
Typesafe CLI args
Browse files Browse the repository at this point in the history
  • Loading branch information
dapplion committed May 29, 2023
1 parent 1cecf82 commit 8aaf742
Show file tree
Hide file tree
Showing 27 changed files with 148 additions and 144 deletions.
4 changes: 2 additions & 2 deletions packages/beacon-node/src/eth1/eth1DepositsCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ export class Eth1DepositsCache {
db: IBeaconDb;
config: ChainForkConfig;

constructor(opts: {unsafeAllowDepositDataOverwrite: boolean}, config: ChainForkConfig, db: IBeaconDb) {
constructor(opts: {unsafeAllowDepositDataOverwrite?: boolean}, config: ChainForkConfig, db: IBeaconDb) {
this.config = config;
this.db = db;
this.unsafeAllowDepositDataOverwrite = opts.unsafeAllowDepositDataOverwrite;
this.unsafeAllowDepositDataOverwrite = opts.unsafeAllowDepositDataOverwrite ?? false;
}

/**
Expand Down
10 changes: 6 additions & 4 deletions packages/beacon-node/src/eth1/options.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
export type Eth1Options = {
enabled: boolean;
enabled?: boolean;
disableEth1DepositDataTracker?: boolean;
providerUrls: string[];
providerUrls?: string[];
/**
* jwtSecretHex is the jwt secret if the eth1 modules should ping the jwt auth
* protected engine endpoints.
*/
jwtSecretHex?: string;
depositContractDeployBlock?: number;
unsafeAllowDepositDataOverwrite: boolean;
unsafeAllowDepositDataOverwrite?: boolean;
/**
* Vote for a specific eth1_data regardless of validity and existing votes.
* hex encoded ssz serialized Eth1Data type.
*/
forcedEth1DataVote?: string;
};

export const DEFAULT_PROVIDER_URLS = ["http://localhost:8545"];

export const defaultEth1Options: Eth1Options = {
enabled: true,
providerUrls: ["http://localhost:8545"],
providerUrls: DEFAULT_PROVIDER_URLS,
depositContractDeployBlock: 0,
unsafeAllowDepositDataOverwrite: false,
};
4 changes: 2 additions & 2 deletions packages/beacon-node/src/eth1/provider/eth1Provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {fromHex} from "@lodestar/utils";
import {linspace} from "../../util/numpy.js";
import {depositEventTopics, parseDepositLog} from "../utils/depositContract.js";
import {Eth1Block, IEth1Provider} from "../interface.js";
import {Eth1Options} from "../options.js";
import {DEFAULT_PROVIDER_URLS, Eth1Options} from "../options.js";
import {isValidAddress} from "../../util/address.js";
import {EthJsonRpcBlockRaw} from "../interface.js";
import {JsonRpcHttpClient, JsonRpcHttpClientMetrics, ReqOpts} from "./jsonRpcHttpClient.js";
Expand Down Expand Up @@ -55,7 +55,7 @@ export class Eth1Provider implements IEth1Provider {
) {
this.deployBlock = opts.depositContractDeployBlock ?? 0;
this.depositContractAddress = toHexString(config.DEPOSIT_CONTRACT_ADDRESS);
this.rpc = new JsonRpcHttpClient(opts.providerUrls, {
this.rpc = new JsonRpcHttpClient(opts.providerUrls ?? DEFAULT_PROVIDER_URLS, {
signal,
// Don't fallback with is truncated error. Throw early and let the retry on this class handle it
shouldNotFallback: isJsonRpcTruncatedError,
Expand Down
5 changes: 2 additions & 3 deletions packages/beacon-node/src/monitoring/options.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export type MonitoringOptions = {
/** Remote endpoint URL where client stats are sent */
endpoint: string;
endpoint?: string;
/** Interval in milliseconds between sending client stats */
interval?: number;
/** Initial delay in milliseconds before client stats are sent */
Expand All @@ -11,8 +11,7 @@ export type MonitoringOptions = {
collectSystemStats?: boolean;
};

export const defaultMonitoringOptions: Required<MonitoringOptions> = {
endpoint: "",
export const defaultMonitoringOptions: Required<Omit<MonitoringOptions, "endpoint">> = {
interval: 60_000,
initialDelay: 30_000,
requestTimeout: 10_000,
Expand Down
2 changes: 1 addition & 1 deletion packages/beacon-node/src/monitoring/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export class MonitoringService {

constructor(
client: Client,
options: MonitoringOptions,
options: Required<Pick<MonitoringOptions, "endpoint">> & MonitoringOptions,
{register, logger}: {register: RegistryMetricCreator; logger: Logger}
) {
this.options = {...defaultMonitoringOptions, ...options};
Expand Down
6 changes: 1 addition & 5 deletions packages/beacon-node/src/network/options.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {Eth2GossipsubOpts} from "./gossip/gossipsub.js";
import {defaultGossipHandlerOpts} from "./processor/gossipHandlers.js";
import {PeerManagerOpts, PeerRpcScoreOpts} from "./peers/index.js";
import {ReqRespBeaconNodeOpts} from "./reqresp/ReqRespBeaconNode.js";
import {NetworkProcessorOpts} from "./processor/index.js";
Expand All @@ -15,7 +14,7 @@ export interface NetworkOptions
localMultiaddrs: string[];
bootMultiaddrs?: string[];
subscribeAllSubnets?: boolean;
mdns: boolean;
mdns?: boolean;
connectToDiscv5Bootnodes?: boolean;
version?: string;
useWorker?: boolean;
Expand All @@ -24,17 +23,14 @@ export interface NetworkOptions
export const defaultNetworkOptions: NetworkOptions = {
maxPeers: 55, // Allow some room above targetPeers for new inbound peers
targetPeers: 50,
discv5FirstQueryDelayMs: 1000,
localMultiaddrs: ["/ip4/0.0.0.0/tcp/9000"],
bootMultiaddrs: [],
mdns: false,
/** disabled by default */
discv5: null,
rateLimitMultiplier: 1,
// TODO: this value is 12 per spec, however lodestar has performance issue if there are too many mesh peers
// see https://github.com/ChainSafe/lodestar/issues/5420
gossipsubDHigh: 9,
...defaultGossipHandlerOpts,
// TEMP default to try
useWorker: true,
};
6 changes: 4 additions & 2 deletions packages/beacon-node/src/network/peers/peerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ const STATUS_INBOUND_GRACE_PERIOD = 15 * 1000;
const CHECK_PING_STATUS_INTERVAL = 10 * 1000;
/** A peer is considered long connection if it's >= 1 day */
const LONG_PEER_CONNECTION_MS = 24 * 60 * 60 * 1000;
/** Ref https://github.com/ChainSafe/lodestar/issues/3423 */
const DEFAULT_DISCV5_FIRST_QUERY_DELAY_MS = 1000;
/**
* Tag peer when it's relevant and connecting to our node.
* When node has > maxPeer (55), libp2p randomly prune peers if we don't tag peers in use.
Expand Down Expand Up @@ -70,7 +72,7 @@ export type PeerManagerOpts = {
* Delay the 1st query after starting discv5
* See https://github.com/ChainSafe/lodestar/issues/3423
*/
discv5FirstQueryDelayMs: number;
discv5FirstQueryDelayMs?: number;
/**
* If null, Don't run discv5 queries, nor connect to cached peers in the peerStore
*/
Expand Down Expand Up @@ -166,7 +168,7 @@ export class PeerManager {
opts.discv5 &&
new PeerDiscovery(modules, {
maxPeers: opts.maxPeers,
discv5FirstQueryDelayMs: opts.discv5FirstQueryDelayMs,
discv5FirstQueryDelayMs: opts.discv5FirstQueryDelayMs ?? DEFAULT_DISCV5_FIRST_QUERY_DELAY_MS,
discv5: opts.discv5,
connectToDiscv5Bootnodes: opts.connectToDiscv5Bootnodes,
});
Expand Down
11 changes: 2 additions & 9 deletions packages/beacon-node/src/network/processor/gossipHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,8 @@ import {AggregatorTracker} from "./aggregatorTracker.js";
* Gossip handler options as part of network options
*/
export type GossipHandlerOpts = {
dontSendGossipAttestationsToForkchoice: boolean;
};

/**
* By default:
* + pass gossip attestations to forkchoice
*/
export const defaultGossipHandlerOpts = {
dontSendGossipAttestationsToForkchoice: false,
/** By default pass gossip attestations to forkchoice */
dontSendGossipAttestationsToForkchoice?: boolean;
};

export type ValidatorFnsModules = {
Expand Down
9 changes: 5 additions & 4 deletions packages/beacon-node/src/node/nodejs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,11 @@ export class BeaconNode {

let monitoring = null;
if (opts.monitoring.endpoint) {
monitoring = new MonitoringService("beacon", opts.monitoring, {
register: (metrics as Metrics).register,
logger: logger.child({module: LoggerModule.monitoring}),
});
monitoring = new MonitoringService(
"beacon",
{...opts.monitoring, endpoint: opts.monitoring.endpoint},
{register: (metrics as Metrics).register, logger: logger.child({module: LoggerModule.monitoring})}
);
monitoring.start();
}

Expand Down
19 changes: 8 additions & 11 deletions packages/cli/src/cmds/beacon/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type BeaconExtraArgs = {
persistInvalidSszObjectsDir?: string;
peerStoreDir?: string;
persistNetworkIdentity?: boolean;
attachToGlobalThis?: boolean;
};

export const beaconExtraOptions: CliCommandOptions<BeaconExtraArgs> = {
Expand Down Expand Up @@ -97,6 +98,12 @@ export const beaconExtraOptions: CliCommandOptions<BeaconExtraArgs> = {
description: "Whether to reuse the same peer-id across restarts",
type: "boolean",
},

attachToGlobalThis: {
hidden: true,
description: "Attach the beacon node to `globalThis`. Useful to inspect a running beacon node.",
type: "boolean",
},
};

type ENRArgs = {
Expand Down Expand Up @@ -147,22 +154,12 @@ const enrOptions: Record<string, Options> = {
},
};

export type DebugArgs = {attachToGlobalThis: boolean};
export const debugOptions: CliCommandOptions<DebugArgs> = {
attachToGlobalThis: {
hidden: true,
description: "Attach the beacon node to `globalThis`. Useful to inspect a running beacon node.",
type: "boolean",
},
};

export type BeaconArgs = BeaconExtraArgs & LogArgs & BeaconPaths & BeaconNodeArgs & ENRArgs & DebugArgs;
export type BeaconArgs = BeaconExtraArgs & LogArgs & BeaconPaths & BeaconNodeArgs & ENRArgs;

export const beaconOptions: {[k: string]: Options} = {
...beaconExtraOptions,
...logOptions,
...beaconNodeOptions,
...paramsOptions,
...enrOptions,
...debugOptions,
};
5 changes: 5 additions & 0 deletions packages/cli/src/cmds/lightclient/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {getNodeLogger} from "@lodestar/logger/node";
import {getBeaconConfigFromArgs} from "../../config/beaconParams.js";
import {getGlobalPaths} from "../../paths/global.js";
import {parseLoggerArgs} from "../../util/logger.js";
import {YargsError} from "../../util/errors.js";
import {GlobalArgs} from "../../options/index.js";
import {ILightClientArgs} from "./options.js";

Expand All @@ -17,7 +18,11 @@ export async function lightclientHandler(args: ILightClientArgs & GlobalArgs): P
const logger = getNodeLogger(
parseLoggerArgs(args, {defaultLogFilepath: path.join(globalPaths.dataDir, "lightclient.log")}, config)
);

const {beaconApiUrl, checkpointRoot} = args;
if (!beaconApiUrl) throw new YargsError("must provide beaconApiUrl arg");
if (!checkpointRoot) throw new YargsError("must provide checkpointRoot arg");

const api = getClient({baseUrl: beaconApiUrl}, {config});
const res = await api.beacon.getGenesis();
ApiError.assert(res, "Can not fetch genesis data");
Expand Down
6 changes: 2 additions & 4 deletions packages/cli/src/cmds/lightclient/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@ import {LogArgs, logOptions} from "../../options/logOptions.js";
import {CliCommandOptions} from "../../util/index.js";

export type ILightClientArgs = LogArgs & {
beaconApiUrl: string;
checkpointRoot: string;
beaconApiUrl?: string;
checkpointRoot?: string;
};

export const lightclientOptions: CliCommandOptions<ILightClientArgs> = {
...logOptions,
beaconApiUrl: {
description: "Url to a beacon node that support lightclient API",
type: "string",
require: true,
},
checkpointRoot: {
description: "Checkpoint root hex string to sync the lightclient from, start with 0x",
type: "string",
require: true,
},
};
22 changes: 13 additions & 9 deletions packages/cli/src/cmds/validator/blsToExecutionChange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import {fromHexString} from "@chainsafe/ssz";
import bls from "@chainsafe/bls";
import {PointFormat} from "@chainsafe/bls/types";

import {CliCommand} from "../../util/index.js";
import {CliCommand, YargsError} from "../../util/index.js";
import {GlobalArgs} from "../../options/index.js";
import {getBeaconConfigFromArgs} from "../../config/index.js";
import {IValidatorCliArgs} from "./options.js";

/* eslint-disable no-console */

type BlsToExecutionChangeArgs = {
publicKey: string;
fromBlsPrivkey: string;
toExecutionAddress: string;
publicKey?: string;
fromBlsPrivkey?: string;
toExecutionAddress?: string;
};

export const blsToExecutionChange: CliCommand<BlsToExecutionChangeArgs, IValidatorCliArgs & GlobalArgs> = {
Expand Down Expand Up @@ -54,7 +54,11 @@ like to choose for BLS To Execution Change.",
},

handler: async (args) => {
const publicKey = args.publicKey;
const {publicKey, fromBlsPrivkey, toExecutionAddress} = args;
if (!publicKey) throw new YargsError("must provide publicKey arg");
if (!fromBlsPrivkey) throw new YargsError("must provide fromBlsPrivkey arg");
if (!toExecutionAddress) throw new YargsError("must provide toExecutionAddress arg");

// Fetch genesisValidatorsRoot always from beacon node as anyway beacon node is needed for
// submitting the signed message
const {config: chainForkConfig} = getBeaconConfigFromArgs(args);
Expand All @@ -72,21 +76,21 @@ like to choose for BLS To Execution Change.",
throw new Error(`Validator pubkey ${publicKey} not found in state`);
}

const fromBlsPrivkey = bls.SecretKey.fromBytes(fromHexString(args.fromBlsPrivkey));
const fromBlsPubkey = fromBlsPrivkey.toPublicKey().toBytes(PointFormat.compressed);
const blsPrivkey = bls.SecretKey.fromBytes(fromHexString(fromBlsPrivkey));
const fromBlsPubkey = blsPrivkey.toPublicKey().toBytes(PointFormat.compressed);

const blsToExecutionChange: capella.BLSToExecutionChange = {
validatorIndex: stateValidator.index,
fromBlsPubkey,
toExecutionAddress: fromHexString(args.toExecutionAddress),
toExecutionAddress: fromHexString(toExecutionAddress),
};

const signatureFork = ForkName.phase0;
const domain = config.getDomainAtFork(signatureFork, DOMAIN_BLS_TO_EXECUTION_CHANGE);
const signingRoot = computeSigningRoot(ssz.capella.BLSToExecutionChange, blsToExecutionChange, domain);
const signedBLSToExecutionChange = {
message: blsToExecutionChange,
signature: fromBlsPrivkey.sign(signingRoot).toBytes(),
signature: blsPrivkey.sign(signingRoot).toBytes(),
};

ApiError.assert(
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/cmds/validator/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ export type IValidatorCliArgs = AccountValidatorArgs &
LogArgs & {
validatorsDbDir?: string;
beaconNodes: string[];
force: boolean;
graffiti: string;
force?: boolean;
graffiti?: string;
afterBlockDelaySlotFraction?: number;
scAfterBlockDelaySlotFraction?: number;
disableAttestationGrouping?: boolean;
Expand Down
9 changes: 6 additions & 3 deletions packages/cli/src/cmds/validator/slashingProtection/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {getGenesisValidatorsRoot, getSlashingProtection} from "./utils.js";
import {ISlashingProtectionArgs} from "./options.js";

type ExportArgs = {
file: string;
file?: string;
pubkeys?: string[];
};

Expand Down Expand Up @@ -50,6 +50,9 @@ export const exportCmd: CliCommand<ExportArgs, ISlashingProtectionArgs & Account
},

handler: async (args) => {
const {file} = args;
if (!file) throw new YargsError("must provide file arg");

const {config, network} = getBeaconConfigFromArgs(args);
const validatorPaths = getValidatorPaths(args, network);
// slashingProtection commands are fast so do not require logFile feature
Expand Down Expand Up @@ -102,8 +105,8 @@ export const exportCmd: CliCommand<ExportArgs, ISlashingProtectionArgs & Account
logger
);

logger.info("Writing slashing protection data", {file: args.file});
writeFile600Perm(args.file, interchange);
logger.info("Writing slashing protection data", {file});
writeFile600Perm(file, interchange);
logger.info("Export completed successfully");
},
};
Loading

0 comments on commit 8aaf742

Please sign in to comment.