@chainsafe/lodestar 1.35.0-dev.e18102ed8c → 1.35.0-dev.f45a2be721
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.git-data.json +1 -1
- package/bin/lodestar.js +3 -0
- package/bin/lodestar.ts +3 -0
- package/lib/applyPreset.d.ts.map +1 -0
- package/lib/applyPreset.js +1 -1
- package/lib/applyPreset.js.map +1 -1
- package/lib/cli.d.ts.map +1 -0
- package/lib/cmds/beacon/handler.d.ts +1 -1
- package/lib/cmds/beacon/handler.d.ts.map +1 -0
- package/lib/cmds/beacon/handler.js +1 -1
- package/lib/cmds/beacon/handler.js.map +1 -1
- package/lib/cmds/beacon/index.d.ts.map +1 -0
- package/lib/cmds/beacon/initBeaconState.d.ts.map +1 -0
- package/lib/cmds/beacon/initPeerIdAndEnr.d.ts.map +1 -0
- package/lib/cmds/beacon/options.d.ts.map +1 -0
- package/lib/cmds/beacon/paths.d.ts.map +1 -0
- package/lib/cmds/bootnode/handler.d.ts.map +1 -0
- package/lib/cmds/bootnode/index.d.ts.map +1 -0
- package/lib/cmds/bootnode/options.d.ts.map +1 -0
- package/lib/cmds/dev/files.d.ts.map +1 -0
- package/lib/cmds/dev/handler.d.ts.map +1 -0
- package/lib/cmds/dev/index.d.ts.map +1 -0
- package/lib/cmds/dev/options.d.ts.map +1 -0
- package/lib/cmds/index.d.ts.map +1 -0
- package/lib/cmds/lightclient/handler.d.ts.map +1 -0
- package/lib/cmds/lightclient/index.d.ts.map +1 -0
- package/lib/cmds/lightclient/options.d.ts.map +1 -0
- package/lib/cmds/validator/blsToExecutionChange.d.ts.map +1 -0
- package/lib/cmds/validator/handler.d.ts.map +1 -0
- package/lib/cmds/validator/handler.js +1 -1
- package/lib/cmds/validator/handler.js.map +1 -1
- package/lib/cmds/validator/import.d.ts.map +1 -0
- package/lib/cmds/validator/index.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/decryptKeystoreDefinitions.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/decryptKeystores/index.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/decryptKeystores/poolSize.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/decryptKeystores/threadPool.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/decryptKeystores/types.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/decryptKeystores/worker.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/impl.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/interface.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/keystoreCache.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/persistedKeys.d.ts.map +1 -0
- package/lib/cmds/validator/keymanager/server.d.ts.map +1 -0
- package/lib/cmds/validator/list.d.ts.map +1 -0
- package/lib/cmds/validator/options.d.ts.map +1 -0
- package/lib/cmds/validator/options.js +5 -3
- package/lib/cmds/validator/options.js.map +1 -1
- package/lib/cmds/validator/paths.d.ts.map +1 -0
- package/lib/cmds/validator/signers/importExternalKeystores.d.ts.map +1 -0
- package/lib/cmds/validator/signers/index.d.ts.map +1 -0
- package/lib/cmds/validator/signers/logSigners.d.ts.map +1 -0
- package/lib/cmds/validator/slashingProtection/export.d.ts.map +1 -0
- package/lib/cmds/validator/slashingProtection/import.d.ts.map +1 -0
- package/lib/cmds/validator/slashingProtection/index.d.ts.map +1 -0
- package/lib/cmds/validator/slashingProtection/options.d.ts.map +1 -0
- package/lib/cmds/validator/slashingProtection/utils.d.ts.map +1 -0
- package/lib/cmds/validator/slashingProtection/utils.js +1 -1
- package/lib/cmds/validator/slashingProtection/utils.js.map +1 -1
- package/lib/cmds/validator/voluntaryExit.d.ts.map +1 -0
- package/lib/config/beaconNodeOptions.d.ts.map +1 -0
- package/lib/config/beaconParams.d.ts.map +1 -0
- package/lib/config/index.d.ts.map +1 -0
- package/lib/config/peerId.d.ts.map +1 -0
- package/lib/config/types.d.ts.map +1 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/migrations/index.d.ts.map +1 -0
- package/lib/networks/chiado.d.ts.map +1 -0
- package/lib/networks/dev.d.ts.map +1 -0
- package/lib/networks/ephemery.d.ts.map +1 -0
- package/lib/networks/gnosis.d.ts.map +1 -0
- package/lib/networks/holesky.d.ts.map +1 -0
- package/lib/networks/hoodi.d.ts.map +1 -0
- package/lib/networks/index.d.ts.map +1 -0
- package/lib/networks/mainnet.d.ts.map +1 -0
- package/lib/networks/sepolia.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/api.d.ts +6 -0
- package/lib/options/beaconNodeOptions/api.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/api.js +14 -6
- package/lib/options/beaconNodeOptions/api.js.map +1 -1
- package/lib/options/beaconNodeOptions/builder.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/chain.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/eth1.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/execution.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/index.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/metrics.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/monitoring.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/network.d.ts.map +1 -0
- package/lib/options/beaconNodeOptions/sync.d.ts.map +1 -0
- package/lib/options/globalOptions.d.ts.map +1 -0
- package/lib/options/index.d.ts.map +1 -0
- package/lib/options/logOptions.d.ts.map +1 -0
- package/lib/options/paramsOptions.d.ts.map +1 -0
- package/lib/paths/global.d.ts.map +1 -0
- package/lib/paths/rootDir.d.ts.map +1 -0
- package/lib/util/errors.d.ts.map +1 -0
- package/lib/util/ethers.d.ts.map +1 -0
- package/lib/util/feeRecipient.d.ts.map +1 -0
- package/lib/util/file.d.ts.map +1 -0
- package/lib/util/file.js.map +1 -1
- package/lib/util/format.d.ts.map +1 -0
- package/lib/util/fs.d.ts.map +1 -0
- package/lib/util/gitData/gitDataPath.d.ts.map +1 -0
- package/lib/util/gitData/index.d.ts.map +1 -0
- package/lib/util/gitData/writeGitData.d.ts.map +1 -0
- package/lib/util/hasher_bun.d.ts +3 -0
- package/lib/util/hasher_bun.d.ts.map +1 -0
- package/lib/util/hasher_bun.js +118 -0
- package/lib/util/hasher_bun.js.map +1 -0
- package/lib/util/hasher_nodejs.d.ts +3 -0
- package/lib/util/hasher_nodejs.d.ts.map +1 -0
- package/lib/util/hasher_nodejs.js +3 -0
- package/lib/util/hasher_nodejs.js.map +1 -0
- package/lib/util/index.d.ts.map +1 -0
- package/lib/util/jwt.d.ts.map +1 -0
- package/lib/util/lockfile.d.ts.map +1 -0
- package/lib/util/logger.d.ts.map +1 -0
- package/lib/util/logger.js +1 -1
- package/lib/util/logger.js.map +1 -1
- package/lib/util/object.d.ts.map +1 -0
- package/lib/util/passphrase.d.ts.map +1 -0
- package/lib/util/process.d.ts.map +1 -0
- package/lib/util/progress.d.ts.map +1 -0
- package/lib/util/proposerConfig.d.ts.map +1 -0
- package/lib/util/pruneOldFilesInDir.d.ts.map +1 -0
- package/lib/util/sleep.d.ts.map +1 -0
- package/lib/util/stripOffNewlines.d.ts.map +1 -0
- package/lib/util/types.d.ts.map +1 -0
- package/lib/util/version.d.ts.map +1 -0
- package/package.json +32 -19
- package/src/applyPreset.ts +92 -0
- package/src/cli.ts +56 -0
- package/src/cmds/beacon/handler.ts +267 -0
- package/src/cmds/beacon/index.ts +18 -0
- package/src/cmds/beacon/initBeaconState.ts +275 -0
- package/src/cmds/beacon/initPeerIdAndEnr.ts +199 -0
- package/src/cmds/beacon/options.ts +214 -0
- package/src/cmds/beacon/paths.ts +62 -0
- package/src/cmds/bootnode/handler.ts +203 -0
- package/src/cmds/bootnode/index.ts +13 -0
- package/src/cmds/bootnode/options.ts +109 -0
- package/src/cmds/dev/files.ts +52 -0
- package/src/cmds/dev/handler.ts +86 -0
- package/src/cmds/dev/index.ts +18 -0
- package/src/cmds/dev/options.ts +110 -0
- package/src/cmds/index.ts +15 -0
- package/src/cmds/lightclient/handler.ts +36 -0
- package/src/cmds/lightclient/index.ts +18 -0
- package/src/cmds/lightclient/options.ts +21 -0
- package/src/cmds/validator/blsToExecutionChange.ts +91 -0
- package/src/cmds/validator/handler.ts +300 -0
- package/src/cmds/validator/import.ts +111 -0
- package/src/cmds/validator/index.ts +28 -0
- package/src/cmds/validator/keymanager/decryptKeystoreDefinitions.ts +189 -0
- package/src/cmds/validator/keymanager/decryptKeystores/index.ts +1 -0
- package/src/cmds/validator/keymanager/decryptKeystores/poolSize.ts +16 -0
- package/src/cmds/validator/keymanager/decryptKeystores/threadPool.ts +75 -0
- package/src/cmds/validator/keymanager/decryptKeystores/types.ts +12 -0
- package/src/cmds/validator/keymanager/decryptKeystores/worker.ts +24 -0
- package/src/cmds/validator/keymanager/impl.ts +425 -0
- package/src/cmds/validator/keymanager/interface.ts +35 -0
- package/src/cmds/validator/keymanager/keystoreCache.ts +91 -0
- package/src/cmds/validator/keymanager/persistedKeys.ts +268 -0
- package/src/cmds/validator/keymanager/server.ts +86 -0
- package/src/cmds/validator/list.ts +35 -0
- package/src/cmds/validator/options.ts +463 -0
- package/src/cmds/validator/paths.ts +95 -0
- package/src/cmds/validator/signers/importExternalKeystores.ts +69 -0
- package/src/cmds/validator/signers/index.ts +176 -0
- package/src/cmds/validator/signers/logSigners.ts +81 -0
- package/src/cmds/validator/slashingProtection/export.ts +110 -0
- package/src/cmds/validator/slashingProtection/import.ts +70 -0
- package/src/cmds/validator/slashingProtection/index.ts +12 -0
- package/src/cmds/validator/slashingProtection/options.ts +15 -0
- package/src/cmds/validator/slashingProtection/utils.ts +56 -0
- package/src/cmds/validator/voluntaryExit.ts +232 -0
- package/src/config/beaconNodeOptions.ts +68 -0
- package/src/config/beaconParams.ts +87 -0
- package/src/config/index.ts +3 -0
- package/src/config/peerId.ts +50 -0
- package/src/config/types.ts +3 -0
- package/src/index.ts +28 -0
- package/src/migrations/index.ts +0 -0
- package/src/networks/chiado.ts +20 -0
- package/src/networks/dev.ts +27 -0
- package/src/networks/ephemery.ts +9 -0
- package/src/networks/gnosis.ts +18 -0
- package/src/networks/holesky.ts +17 -0
- package/src/networks/hoodi.ts +16 -0
- package/src/networks/index.ts +236 -0
- package/src/networks/mainnet.ts +34 -0
- package/src/networks/sepolia.ts +17 -0
- package/src/options/beaconNodeOptions/api.ts +120 -0
- package/src/options/beaconNodeOptions/builder.ts +63 -0
- package/src/options/beaconNodeOptions/chain.ts +326 -0
- package/src/options/beaconNodeOptions/eth1.ts +95 -0
- package/src/options/beaconNodeOptions/execution.ts +92 -0
- package/src/options/beaconNodeOptions/index.ts +50 -0
- package/src/options/beaconNodeOptions/metrics.ts +39 -0
- package/src/options/beaconNodeOptions/monitoring.ts +61 -0
- package/src/options/beaconNodeOptions/network.ts +401 -0
- package/src/options/beaconNodeOptions/sync.ts +65 -0
- package/src/options/globalOptions.ts +72 -0
- package/src/options/index.ts +3 -0
- package/src/options/logOptions.ts +70 -0
- package/src/options/paramsOptions.ts +72 -0
- package/src/paths/global.ts +24 -0
- package/src/paths/rootDir.ts +11 -0
- package/src/util/errors.ts +20 -0
- package/src/util/ethers.ts +44 -0
- package/src/util/feeRecipient.ts +6 -0
- package/src/util/file.ts +167 -0
- package/src/util/format.ts +76 -0
- package/src/util/fs.ts +59 -0
- package/src/util/gitData/gitDataPath.ts +48 -0
- package/src/util/gitData/index.ts +70 -0
- package/src/util/gitData/writeGitData.ts +10 -0
- package/src/util/hasher_bun.ts +133 -0
- package/src/util/hasher_nodejs.ts +3 -0
- package/src/util/index.ts +17 -0
- package/src/util/jwt.ts +10 -0
- package/src/util/lockfile.ts +45 -0
- package/src/util/logger.ts +105 -0
- package/src/util/object.ts +15 -0
- package/src/util/passphrase.ts +25 -0
- package/src/util/process.ts +25 -0
- package/src/util/progress.ts +58 -0
- package/src/util/proposerConfig.ts +136 -0
- package/src/util/pruneOldFilesInDir.ts +27 -0
- package/src/util/sleep.ts +3 -0
- package/src/util/stripOffNewlines.ts +6 -0
- package/src/util/types.ts +8 -0
- package/src/util/version.ts +74 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import {generateKeyPair} from "@libp2p/crypto/keys";
|
|
5
|
+
import type {PrivateKey} from "@libp2p/interface";
|
|
6
|
+
import {peerIdFromPrivateKey} from "@libp2p/peer-id";
|
|
7
|
+
import {Multiaddr} from "@multiformats/multiaddr";
|
|
8
|
+
import {SignableENR} from "@chainsafe/enr";
|
|
9
|
+
import {Logger} from "@lodestar/utils";
|
|
10
|
+
import {exportToJSON, readPrivateKey} from "../../config/index.js";
|
|
11
|
+
import {parseListenArgs} from "../../options/beaconNodeOptions/network.js";
|
|
12
|
+
import {writeFile600Perm} from "../../util/file.js";
|
|
13
|
+
import {BeaconArgs} from "./options.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Check if multiaddr belongs to the local network interfaces.
|
|
17
|
+
*/
|
|
18
|
+
export function isLocalMultiAddr(multiaddr: Multiaddr | undefined): boolean {
|
|
19
|
+
if (!multiaddr) return false;
|
|
20
|
+
|
|
21
|
+
const protoNames = multiaddr.protoNames();
|
|
22
|
+
if (protoNames.length !== 2 && protoNames[1] !== "udp") {
|
|
23
|
+
throw new Error("Invalid udp multiaddr");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const interfaces = os.networkInterfaces();
|
|
27
|
+
const tuples = multiaddr.tuples();
|
|
28
|
+
const family = tuples[0][0];
|
|
29
|
+
const isIPv4: boolean = family === 4;
|
|
30
|
+
const ip = tuples[0][1];
|
|
31
|
+
|
|
32
|
+
if (!ip) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const ipStr = isIPv4
|
|
37
|
+
? Array.from(ip).join(".")
|
|
38
|
+
: Array.from(Uint16Array.from(ip))
|
|
39
|
+
.map((n) => n.toString(16))
|
|
40
|
+
.join(":");
|
|
41
|
+
|
|
42
|
+
for (const networkInterfaces of Object.values(interfaces)) {
|
|
43
|
+
for (const networkInterface of networkInterfaces || []) {
|
|
44
|
+
// since node version 18, the netowrkinterface family returns 4 | 6 instead of ipv4 | ipv6,
|
|
45
|
+
// even though the documentation says otherwise.
|
|
46
|
+
// This might be a bug that would be corrected in future version, in the meantime
|
|
47
|
+
// the check using endsWith ensures things work in node version 18 and earlier
|
|
48
|
+
if (String(networkInterface.family).endsWith(String(family)) && networkInterface.address === ipStr) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Only update the enr if the value has changed
|
|
59
|
+
*/
|
|
60
|
+
function maybeUpdateEnr<T extends "ip" | "tcp" | "udp" | "ip6" | "tcp6" | "udp6">(
|
|
61
|
+
enr: SignableENR,
|
|
62
|
+
key: T,
|
|
63
|
+
value: SignableENR[T] | undefined
|
|
64
|
+
): void {
|
|
65
|
+
if (enr[key] !== value) {
|
|
66
|
+
enr[key] = value;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function overwriteEnrWithCliArgs(
|
|
71
|
+
enr: SignableENR,
|
|
72
|
+
args: BeaconArgs,
|
|
73
|
+
logger: Logger,
|
|
74
|
+
opts?: {newEnr?: boolean; bootnode?: boolean}
|
|
75
|
+
): void {
|
|
76
|
+
const preSeq = enr.seq;
|
|
77
|
+
const {port, discoveryPort, port6, discoveryPort6} = parseListenArgs(args);
|
|
78
|
+
maybeUpdateEnr(enr, "ip", args["enr.ip"] ?? enr.ip);
|
|
79
|
+
maybeUpdateEnr(enr, "ip6", args["enr.ip6"] ?? enr.ip6);
|
|
80
|
+
maybeUpdateEnr(enr, "udp", args["enr.udp"] ?? discoveryPort ?? enr.udp);
|
|
81
|
+
maybeUpdateEnr(enr, "udp6", args["enr.udp6"] ?? discoveryPort6 ?? enr.udp6);
|
|
82
|
+
if (!opts?.bootnode) {
|
|
83
|
+
maybeUpdateEnr(enr, "tcp", args["enr.tcp"] ?? port ?? enr.tcp);
|
|
84
|
+
maybeUpdateEnr(enr, "tcp6", args["enr.tcp6"] ?? port6 ?? enr.tcp6);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function testMultiaddrForLocal(mu: Multiaddr, ip4: boolean): void {
|
|
88
|
+
const isLocal = isLocalMultiAddr(mu);
|
|
89
|
+
if (args.nat) {
|
|
90
|
+
if (isLocal) {
|
|
91
|
+
logger.warn("--nat flag is set with no purpose");
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
if (!isLocal) {
|
|
95
|
+
logger.warn(
|
|
96
|
+
`Configured ENR ${ip4 ? "IPv4" : "IPv6"} address is not local, clearing ENR ${ip4 ? "ip" : "ip6"} and ${
|
|
97
|
+
ip4 ? "udp" : "udp6"
|
|
98
|
+
}. Set the --nat flag to prevent this`
|
|
99
|
+
);
|
|
100
|
+
if (ip4) {
|
|
101
|
+
enr.delete("ip");
|
|
102
|
+
enr.delete("udp");
|
|
103
|
+
} else {
|
|
104
|
+
enr.delete("ip6");
|
|
105
|
+
enr.delete("udp6");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const udpMultiaddr4 = enr.getLocationMultiaddr("udp4");
|
|
111
|
+
if (udpMultiaddr4) {
|
|
112
|
+
testMultiaddrForLocal(udpMultiaddr4, true);
|
|
113
|
+
}
|
|
114
|
+
const udpMultiaddr6 = enr.getLocationMultiaddr("udp6");
|
|
115
|
+
if (udpMultiaddr6) {
|
|
116
|
+
testMultiaddrForLocal(udpMultiaddr6, false);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (enr.seq !== preSeq) {
|
|
120
|
+
// If the enr is newly created, its sequence number can be set to 1
|
|
121
|
+
// It's especially clean for fully configured bootnodes whose enrs never change
|
|
122
|
+
// Otherwise, we can increment the sequence number as little as possible
|
|
123
|
+
if (opts?.newEnr) {
|
|
124
|
+
enr.seq = BigInt(1);
|
|
125
|
+
} else {
|
|
126
|
+
enr.seq = preSeq + BigInt(1);
|
|
127
|
+
}
|
|
128
|
+
// invalidate cached signature
|
|
129
|
+
// biome-ignore lint/complexity/useLiteralKeys: `_signature` is a private attribute
|
|
130
|
+
delete enr["_signature"];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Create new PeerId and ENR by default, unless persistNetworkIdentity is provided
|
|
136
|
+
*/
|
|
137
|
+
export async function initPrivateKeyAndEnr(
|
|
138
|
+
args: BeaconArgs,
|
|
139
|
+
beaconDir: string,
|
|
140
|
+
logger: Logger,
|
|
141
|
+
bootnode?: boolean
|
|
142
|
+
): Promise<{privateKey: PrivateKey; enr: SignableENR}> {
|
|
143
|
+
const {persistNetworkIdentity} = args;
|
|
144
|
+
|
|
145
|
+
const newPrivateKeyAndENR = async (): Promise<{privateKey: PrivateKey; enr: SignableENR}> => {
|
|
146
|
+
const privateKey = await generateKeyPair("secp256k1");
|
|
147
|
+
const enr = SignableENR.createFromPrivateKey(privateKey);
|
|
148
|
+
return {privateKey, enr};
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const readPersistedPrivateKeyAndENR = async (
|
|
152
|
+
peerIdFile: string,
|
|
153
|
+
enrFile: string
|
|
154
|
+
): Promise<{privateKey: PrivateKey; enr: SignableENR; newEnr: boolean}> => {
|
|
155
|
+
let privateKey: PrivateKey;
|
|
156
|
+
let enr: SignableENR;
|
|
157
|
+
|
|
158
|
+
// attempt to read stored private key
|
|
159
|
+
try {
|
|
160
|
+
privateKey = readPrivateKey(peerIdFile);
|
|
161
|
+
} catch (e) {
|
|
162
|
+
if ((e as {code: string}).code === "ENOENT") {
|
|
163
|
+
logger.debug("peerIdFile not found, creating a new peer id", {peerIdFile});
|
|
164
|
+
} else {
|
|
165
|
+
logger.warn("Unable to read peerIdFile, creating a new peer id", {peerIdFile}, e as Error);
|
|
166
|
+
}
|
|
167
|
+
return {...(await newPrivateKeyAndENR()), newEnr: true};
|
|
168
|
+
}
|
|
169
|
+
// attempt to read stored enr
|
|
170
|
+
try {
|
|
171
|
+
enr = SignableENR.decodeTxt(fs.readFileSync(enrFile, "utf-8"), privateKey.raw);
|
|
172
|
+
} catch (_e) {
|
|
173
|
+
logger.warn("Unable to decode stored local ENR, creating a new ENR");
|
|
174
|
+
enr = SignableENR.createFromPrivateKey(privateKey);
|
|
175
|
+
return {privateKey, enr, newEnr: true};
|
|
176
|
+
}
|
|
177
|
+
// check stored peer id against stored enr
|
|
178
|
+
if (!peerIdFromPrivateKey(privateKey).equals(enr.peerId)) {
|
|
179
|
+
logger.warn("Stored local ENR doesn't match peerIdFile, creating a new ENR");
|
|
180
|
+
enr = SignableENR.createFromPrivateKey(privateKey);
|
|
181
|
+
return {privateKey, enr, newEnr: true};
|
|
182
|
+
}
|
|
183
|
+
return {privateKey, enr, newEnr: false};
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
if (persistNetworkIdentity) {
|
|
187
|
+
const enrFile = path.join(beaconDir, "enr");
|
|
188
|
+
const peerIdFile = path.join(beaconDir, "peer-id.json");
|
|
189
|
+
const {privateKey, enr, newEnr} = await readPersistedPrivateKeyAndENR(peerIdFile, enrFile);
|
|
190
|
+
overwriteEnrWithCliArgs(enr, args, logger, {newEnr, bootnode});
|
|
191
|
+
// Re-persist peer-id and enr
|
|
192
|
+
writeFile600Perm(peerIdFile, exportToJSON(privateKey));
|
|
193
|
+
writeFile600Perm(enrFile, enr.encodeTxt());
|
|
194
|
+
return {privateKey, enr};
|
|
195
|
+
}
|
|
196
|
+
const {privateKey, enr} = await newPrivateKeyAndENR();
|
|
197
|
+
overwriteEnrWithCliArgs(enr, args, logger, {newEnr: true, bootnode});
|
|
198
|
+
return {privateKey, enr};
|
|
199
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import {CliCommandOptions, CliOptionDefinition} from "@lodestar/utils";
|
|
2
|
+
import {BeaconNodeArgs, beaconNodeOptions, paramsOptions} from "../../options/index.js";
|
|
3
|
+
import {LogArgs, logOptions} from "../../options/logOptions.js";
|
|
4
|
+
import {BeaconPaths, defaultBeaconPaths} from "./paths.js";
|
|
5
|
+
|
|
6
|
+
type BeaconExtraArgs = {
|
|
7
|
+
forceGenesis?: boolean;
|
|
8
|
+
genesisStateFile?: string;
|
|
9
|
+
configFile?: string;
|
|
10
|
+
bootnodesFile?: string;
|
|
11
|
+
checkpointSyncUrl?: string;
|
|
12
|
+
checkpointState?: string;
|
|
13
|
+
wssCheckpoint?: string;
|
|
14
|
+
forceCheckpointSync?: boolean;
|
|
15
|
+
ignoreWeakSubjectivityCheck?: boolean;
|
|
16
|
+
beaconDir?: string;
|
|
17
|
+
dbDir?: string;
|
|
18
|
+
persistInvalidSszObjectsDir?: string;
|
|
19
|
+
persistInvalidSszObjectsRetentionHours?: number;
|
|
20
|
+
persistOrphanedBlocksDir?: string;
|
|
21
|
+
peerStoreDir?: string;
|
|
22
|
+
persistNetworkIdentity?: boolean;
|
|
23
|
+
private?: boolean;
|
|
24
|
+
validatorMonitorLogs?: boolean;
|
|
25
|
+
attachToGlobalThis?: boolean;
|
|
26
|
+
disableLightClientServer?: boolean;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const beaconExtraOptions: CliCommandOptions<BeaconExtraArgs> = {
|
|
30
|
+
forceGenesis: {
|
|
31
|
+
description: "Force beacon to create genesis without file",
|
|
32
|
+
type: "boolean",
|
|
33
|
+
hidden: true,
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
genesisStateFile: {
|
|
37
|
+
description: "Path or URL to download a genesis state file in ssz-encoded format",
|
|
38
|
+
type: "string",
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
configFile: {
|
|
42
|
+
hidden: true,
|
|
43
|
+
description: "[DEPRECATED] Beacon node configuration file path",
|
|
44
|
+
type: "string",
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
bootnodesFile: {
|
|
48
|
+
hidden: true,
|
|
49
|
+
description: "Bootnodes file path",
|
|
50
|
+
type: "string",
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
checkpointSyncUrl: {
|
|
54
|
+
description:
|
|
55
|
+
"Server url hosting Beacon Node APIs to fetch weak subjectivity state. Fetch latest finalized by default, else set --wssCheckpoint",
|
|
56
|
+
type: "string",
|
|
57
|
+
group: "weak subjectivity",
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
checkpointState: {
|
|
61
|
+
description: "Set a checkpoint state to start syncing from",
|
|
62
|
+
type: "string",
|
|
63
|
+
group: "weak subjectivity",
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
wssCheckpoint: {
|
|
67
|
+
description:
|
|
68
|
+
"Start beacon node off a state at the provided weak subjectivity checkpoint, to be supplied in <blockRoot>:<epoch> format. For example, 0x1234:100 will sync and start off from the weak subjectivity state at checkpoint of epoch 100 with block root 0x1234.",
|
|
69
|
+
type: "string",
|
|
70
|
+
group: "weak subjectivity",
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
forceCheckpointSync: {
|
|
74
|
+
description:
|
|
75
|
+
"Force syncing from checkpoint state even if db state is within weak subjectivity period. This helps to avoid long sync times after node has been offline for a while.",
|
|
76
|
+
type: "boolean",
|
|
77
|
+
group: "weak subjectivity",
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
ignoreWeakSubjectivityCheck: {
|
|
81
|
+
description:
|
|
82
|
+
"Ignore the checkpoint sync state failing the weak subjectivity check. This is relevant in testnets where the weak subjectivity period is too small for even few epochs of non finalization causing last finalized to be out of range. This flag is not recommended for mainnet use.",
|
|
83
|
+
type: "boolean",
|
|
84
|
+
group: "weak subjectivity",
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
beaconDir: {
|
|
88
|
+
description: "Beacon root directory",
|
|
89
|
+
defaultDescription: defaultBeaconPaths.beaconDir,
|
|
90
|
+
hidden: true,
|
|
91
|
+
type: "string",
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
dbDir: {
|
|
95
|
+
description: "Beacon DB directory",
|
|
96
|
+
defaultDescription: defaultBeaconPaths.dbDir,
|
|
97
|
+
hidden: true,
|
|
98
|
+
type: "string",
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
persistInvalidSszObjectsDir: {
|
|
102
|
+
description: "Enable and specify a directory to persist invalid ssz objects",
|
|
103
|
+
defaultDescription: defaultBeaconPaths.persistInvalidSszObjectsDir,
|
|
104
|
+
hidden: true,
|
|
105
|
+
type: "string",
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
persistInvalidSszObjectsRetentionHours: {
|
|
109
|
+
description: "Number of hours to keep invalid SSZ objects on local disk",
|
|
110
|
+
hidden: true,
|
|
111
|
+
type: "number",
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
persistOrphanedBlocksDir: {
|
|
115
|
+
description: "Enable and specify a directory to persist orphaned blocks",
|
|
116
|
+
defaultDescription: defaultBeaconPaths.persistOrphanedBlocksDir,
|
|
117
|
+
hidden: true,
|
|
118
|
+
type: "string",
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
peerStoreDir: {
|
|
122
|
+
hidden: true,
|
|
123
|
+
description: "Peer store directory",
|
|
124
|
+
defaultDescription: defaultBeaconPaths.peerStoreDir,
|
|
125
|
+
type: "string",
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
persistNetworkIdentity: {
|
|
129
|
+
description:
|
|
130
|
+
"Whether to reuse the same peer-id across restarts. Validator custody requires custody group count to persist relative to a given ENR. Setting to false will reset ENR and validator custody requirements on restarts.",
|
|
131
|
+
default: true,
|
|
132
|
+
type: "boolean",
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
private: {
|
|
136
|
+
description:
|
|
137
|
+
"Do not send implementation details over p2p identify protocol and in builder, execution engine and eth1 requests",
|
|
138
|
+
type: "boolean",
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
validatorMonitorLogs: {
|
|
142
|
+
description: "Log validator monitor events as info.",
|
|
143
|
+
type: "boolean",
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
attachToGlobalThis: {
|
|
147
|
+
hidden: true,
|
|
148
|
+
description: "Attach the beacon node to `globalThis`. Useful to inspect a running beacon node.",
|
|
149
|
+
type: "boolean",
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
disableLightClientServer: {
|
|
153
|
+
description: "Disable light client server.",
|
|
154
|
+
type: "boolean",
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
type ENRArgs = {
|
|
159
|
+
"enr.ip"?: string;
|
|
160
|
+
"enr.tcp"?: number;
|
|
161
|
+
"enr.ip6"?: string;
|
|
162
|
+
"enr.udp"?: number;
|
|
163
|
+
"enr.tcp6"?: number;
|
|
164
|
+
"enr.udp6"?: number;
|
|
165
|
+
nat?: boolean;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const enrOptions: CliCommandOptions<ENRArgs> = {
|
|
169
|
+
"enr.ip": {
|
|
170
|
+
description: "Override ENR IP entry",
|
|
171
|
+
type: "string",
|
|
172
|
+
group: "enr",
|
|
173
|
+
},
|
|
174
|
+
"enr.tcp": {
|
|
175
|
+
description: "Override ENR TCP entry",
|
|
176
|
+
type: "number",
|
|
177
|
+
group: "enr",
|
|
178
|
+
},
|
|
179
|
+
"enr.udp": {
|
|
180
|
+
description: "Override ENR UDP entry",
|
|
181
|
+
type: "number",
|
|
182
|
+
group: "enr",
|
|
183
|
+
},
|
|
184
|
+
"enr.ip6": {
|
|
185
|
+
description: "Override ENR IPv6 entry",
|
|
186
|
+
type: "string",
|
|
187
|
+
group: "enr",
|
|
188
|
+
},
|
|
189
|
+
"enr.tcp6": {
|
|
190
|
+
description: "Override ENR (IPv6-specific) TCP entry",
|
|
191
|
+
type: "number",
|
|
192
|
+
group: "enr",
|
|
193
|
+
},
|
|
194
|
+
"enr.udp6": {
|
|
195
|
+
description: "Override ENR (IPv6-specific) UDP entry",
|
|
196
|
+
type: "number",
|
|
197
|
+
group: "enr",
|
|
198
|
+
},
|
|
199
|
+
nat: {
|
|
200
|
+
type: "boolean",
|
|
201
|
+
description: "Allow configuration of non-local addresses",
|
|
202
|
+
group: "enr",
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export type BeaconArgs = BeaconExtraArgs & LogArgs & BeaconPaths & BeaconNodeArgs & ENRArgs;
|
|
207
|
+
|
|
208
|
+
export const beaconOptions: {[k: string]: CliOptionDefinition} = {
|
|
209
|
+
...beaconExtraOptions,
|
|
210
|
+
...logOptions,
|
|
211
|
+
...beaconNodeOptions,
|
|
212
|
+
...paramsOptions,
|
|
213
|
+
...enrOptions,
|
|
214
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import {GlobalArgs} from "../../options/index.js";
|
|
3
|
+
import {GlobalPaths, getGlobalPaths} from "../../paths/global.js";
|
|
4
|
+
|
|
5
|
+
export type BeaconPathsPartial = Partial<{
|
|
6
|
+
beaconDir: string;
|
|
7
|
+
peerStoreDir: string;
|
|
8
|
+
dbDir: string;
|
|
9
|
+
persistInvalidSszObjectsDir: string;
|
|
10
|
+
persistOrphanedBlocksDir?: string;
|
|
11
|
+
}>;
|
|
12
|
+
|
|
13
|
+
export type BeaconPaths = {
|
|
14
|
+
beaconDir: string;
|
|
15
|
+
peerStoreDir: string;
|
|
16
|
+
dbDir: string;
|
|
17
|
+
persistInvalidSszObjectsDir: string;
|
|
18
|
+
persistOrphanedBlocksDir: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Defines the path structure of the files relevant to the beacon node
|
|
23
|
+
*
|
|
24
|
+
* ```bash
|
|
25
|
+
* $dataDir
|
|
26
|
+
* └── $beaconDir
|
|
27
|
+
* ├── beacon.config.json
|
|
28
|
+
* ├── peer-id.json
|
|
29
|
+
* ├── enr
|
|
30
|
+
* ├── chain-db
|
|
31
|
+
* └── beacon.log
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export function getBeaconPaths(
|
|
35
|
+
// Using Pick<GlobalArgs, "dataDir"> make changes in GlobalArgs throw a type error here
|
|
36
|
+
args: BeaconPathsPartial & Pick<GlobalArgs, "dataDir">,
|
|
37
|
+
network: string
|
|
38
|
+
): GlobalPaths & Required<BeaconPathsPartial> {
|
|
39
|
+
// Compute global paths first
|
|
40
|
+
const globalPaths = getGlobalPaths(args, network);
|
|
41
|
+
|
|
42
|
+
const dataDir = globalPaths.dataDir;
|
|
43
|
+
const beaconDir = dataDir;
|
|
44
|
+
const dbDir = args.dbDir ?? path.join(beaconDir, "chain-db");
|
|
45
|
+
const persistInvalidSszObjectsDir = args.persistInvalidSszObjectsDir ?? path.join(beaconDir, "invalidSszObjects");
|
|
46
|
+
const peerStoreDir = args.peerStoreDir ?? path.join(beaconDir, "peerstore");
|
|
47
|
+
const persistOrphanedBlocksDir = args.persistOrphanedBlocksDir ?? path.join(beaconDir, "orphaned_blocks");
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
...globalPaths,
|
|
51
|
+
beaconDir,
|
|
52
|
+
dbDir,
|
|
53
|
+
persistInvalidSszObjectsDir,
|
|
54
|
+
peerStoreDir,
|
|
55
|
+
persistOrphanedBlocksDir,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Constructs representations of the path structure to show in command's description
|
|
61
|
+
*/
|
|
62
|
+
export const defaultBeaconPaths = getBeaconPaths({dataDir: "$dataDir"}, "$network");
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import {PrivateKey} from "@libp2p/interface";
|
|
3
|
+
import {Multiaddr, multiaddr} from "@multiformats/multiaddr";
|
|
4
|
+
import {Discv5, Discv5EventEmitter} from "@chainsafe/discv5";
|
|
5
|
+
import {ENR, ENRData, SignableENR} from "@chainsafe/enr";
|
|
6
|
+
import {
|
|
7
|
+
HttpMetricsServer,
|
|
8
|
+
IBeaconNodeOptions,
|
|
9
|
+
RegistryMetricCreator,
|
|
10
|
+
getHttpMetricsServer,
|
|
11
|
+
} from "@lodestar/beacon-node";
|
|
12
|
+
import {ErrorAborted, Logger} from "@lodestar/utils";
|
|
13
|
+
import {getBeaconConfigFromArgs} from "../../config/index.js";
|
|
14
|
+
import {getNetworkBootnodes, isKnownNetworkName, readBootnodes} from "../../networks/index.js";
|
|
15
|
+
import {parseArgs as parseMetricsArgs} from "../../options/beaconNodeOptions/metrics.js";
|
|
16
|
+
import {parseArgs as parseNetworkArgs} from "../../options/beaconNodeOptions/network.js";
|
|
17
|
+
import {GlobalArgs} from "../../options/index.js";
|
|
18
|
+
import {mkdir, onGracefulShutdown, writeFile600Perm} from "../../util/index.js";
|
|
19
|
+
import {getVersionData} from "../../util/version.js";
|
|
20
|
+
import {initLogger} from "../beacon/handler.js";
|
|
21
|
+
import {initPrivateKeyAndEnr} from "../beacon/initPeerIdAndEnr.js";
|
|
22
|
+
import {BeaconArgs} from "../beacon/options.js";
|
|
23
|
+
import {getBeaconPaths} from "../beacon/paths.js";
|
|
24
|
+
import {BootnodeArgs} from "./options.js";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Runs a bootnode.
|
|
28
|
+
*/
|
|
29
|
+
export async function bootnodeHandler(args: BootnodeArgs & GlobalArgs): Promise<void> {
|
|
30
|
+
const {discv5Args, metricsArgs, bootnodeDir, network, version, commit, privateKey, enr, logger} =
|
|
31
|
+
await bootnodeHandlerInit(args);
|
|
32
|
+
|
|
33
|
+
const abortController = new AbortController();
|
|
34
|
+
const bindAddrs = discv5Args.bindAddrs;
|
|
35
|
+
|
|
36
|
+
logger.info("Lodestar Bootnode", {network, version, commit});
|
|
37
|
+
logger.info("Bind address", bindAddrs);
|
|
38
|
+
logger.info("Advertised address", {
|
|
39
|
+
ip4: enr.getLocationMultiaddr("udp4")?.toString(),
|
|
40
|
+
ip6: enr.getLocationMultiaddr("udp6")?.toString(),
|
|
41
|
+
});
|
|
42
|
+
logger.info("Identity", {peerId: enr.peerId.toString(), nodeId: enr.nodeId});
|
|
43
|
+
logger.info("ENR", {enr: enr.encodeTxt()});
|
|
44
|
+
|
|
45
|
+
// bootnode setup
|
|
46
|
+
try {
|
|
47
|
+
let metricsRegistry: RegistryMetricCreator | undefined;
|
|
48
|
+
let metricsServer: HttpMetricsServer | undefined;
|
|
49
|
+
if (metricsArgs.enabled) {
|
|
50
|
+
metricsRegistry = new RegistryMetricCreator();
|
|
51
|
+
metricsRegistry.static({
|
|
52
|
+
name: "bootnode_version",
|
|
53
|
+
help: "Bootnode version",
|
|
54
|
+
value: {version, commit, network},
|
|
55
|
+
});
|
|
56
|
+
metricsServer = await getHttpMetricsServer(metricsArgs, {register: metricsRegistry, logger});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const discv5 = Discv5.create({
|
|
60
|
+
enr,
|
|
61
|
+
privateKey,
|
|
62
|
+
bindAddrs: {
|
|
63
|
+
ip4: (bindAddrs.ip4 ? multiaddr(bindAddrs.ip4) : undefined) as Multiaddr,
|
|
64
|
+
ip6: bindAddrs.ip6 ? multiaddr(bindAddrs.ip6) : undefined,
|
|
65
|
+
},
|
|
66
|
+
config: {enrUpdate: !enr.ip && !enr.ip6},
|
|
67
|
+
metricsRegistry,
|
|
68
|
+
}) as Discv5 & Discv5EventEmitter;
|
|
69
|
+
|
|
70
|
+
// If there are any bootnodes, add them to the routing table
|
|
71
|
+
for (const bootEnrStr of Array.from(new Set(discv5Args.bootEnrs).values())) {
|
|
72
|
+
const bootEnr = ENR.decodeTxt(bootEnrStr);
|
|
73
|
+
logger.info("Adding bootnode", {
|
|
74
|
+
ip4: bootEnr.getLocationMultiaddr("udp4")?.toString(),
|
|
75
|
+
ip6: bootEnr.getLocationMultiaddr("udp6")?.toString(),
|
|
76
|
+
peerId: bootEnr.peerId.toString(),
|
|
77
|
+
nodeId: enr.nodeId,
|
|
78
|
+
});
|
|
79
|
+
discv5.addEnr(bootEnr);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// start the server
|
|
83
|
+
await discv5.start();
|
|
84
|
+
|
|
85
|
+
// if there are peers in the local routing table, establish a session by running a query
|
|
86
|
+
if (discv5.kadValues().length) {
|
|
87
|
+
void discv5.findRandomNode();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
discv5.on("multiaddrUpdated", (addr: ENRData) => {
|
|
91
|
+
logger.info("Advertised socket address updated", {addr: addr.toString()});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// respond with metrics every 10 seconds
|
|
95
|
+
const printInterval = setInterval(() => {
|
|
96
|
+
let ip4Only = 0;
|
|
97
|
+
let ip6Only = 0;
|
|
98
|
+
let ip4ip6 = 0;
|
|
99
|
+
let unreachable = 0;
|
|
100
|
+
for (const kadEnr of discv5.kadValues()) {
|
|
101
|
+
const hasIp4 = kadEnr.getLocationMultiaddr("udp4");
|
|
102
|
+
const hasIp6 = kadEnr.getLocationMultiaddr("udp6");
|
|
103
|
+
if (hasIp4 && hasIp6) {
|
|
104
|
+
ip4ip6++;
|
|
105
|
+
} else if (hasIp4) {
|
|
106
|
+
ip4Only++;
|
|
107
|
+
} else if (hasIp6) {
|
|
108
|
+
ip6Only++;
|
|
109
|
+
} else {
|
|
110
|
+
unreachable++;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
logger.info("Server metrics", {
|
|
114
|
+
connectedPeers: discv5.connectedPeerCount,
|
|
115
|
+
activeSessions: discv5.sessionService.sessionsSize(),
|
|
116
|
+
ip4Nodes: ip4Only,
|
|
117
|
+
ip6Nodes: ip6Only,
|
|
118
|
+
ip4AndIp6Nodes: ip4ip6,
|
|
119
|
+
unreachableNodes: unreachable,
|
|
120
|
+
});
|
|
121
|
+
}, 10_000);
|
|
122
|
+
|
|
123
|
+
// Intercept SIGINT signal, to perform final ops before exiting
|
|
124
|
+
onGracefulShutdown(async () => {
|
|
125
|
+
if (args.persistNetworkIdentity) {
|
|
126
|
+
try {
|
|
127
|
+
const enrPath = path.join(bootnodeDir, "enr");
|
|
128
|
+
writeFile600Perm(enrPath, enr.encodeTxt());
|
|
129
|
+
} catch (e) {
|
|
130
|
+
logger.warn("Unable to persist enr", {}, e as Error);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
abortController.abort();
|
|
134
|
+
}, logger.info.bind(logger));
|
|
135
|
+
|
|
136
|
+
abortController.signal.addEventListener(
|
|
137
|
+
"abort",
|
|
138
|
+
async () => {
|
|
139
|
+
try {
|
|
140
|
+
discv5.removeAllListeners();
|
|
141
|
+
clearInterval(printInterval);
|
|
142
|
+
|
|
143
|
+
await metricsServer?.close();
|
|
144
|
+
await discv5.stop();
|
|
145
|
+
logger.debug("Bootnode closed");
|
|
146
|
+
} catch (e) {
|
|
147
|
+
logger.error("Error closing bootnode", {}, e as Error);
|
|
148
|
+
// Must explicitly exit process due to potential active handles
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
{once: true}
|
|
153
|
+
);
|
|
154
|
+
} catch (e) {
|
|
155
|
+
if (e instanceof ErrorAborted) {
|
|
156
|
+
logger.info(e.message); // Let the user know the abort was received but don't print as error
|
|
157
|
+
} else {
|
|
158
|
+
throw e;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export type BootNodeInitOptions = {
|
|
164
|
+
discv5Args: Exclude<IBeaconNodeOptions["network"]["discv5"], null>;
|
|
165
|
+
metricsArgs: IBeaconNodeOptions["metrics"];
|
|
166
|
+
bootnodeDir: string;
|
|
167
|
+
network: string;
|
|
168
|
+
version: string;
|
|
169
|
+
commit: string;
|
|
170
|
+
privateKey: PrivateKey;
|
|
171
|
+
enr: SignableENR;
|
|
172
|
+
logger: Logger;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/** Separate function to simplify unit testing of options merging */
|
|
176
|
+
export async function bootnodeHandlerInit(args: BootnodeArgs & GlobalArgs): Promise<BootNodeInitOptions> {
|
|
177
|
+
const {config, network} = getBeaconConfigFromArgs(args);
|
|
178
|
+
const {version, commit} = getVersionData();
|
|
179
|
+
const beaconPaths = getBeaconPaths(args, network);
|
|
180
|
+
// Use a separate directory to store bootnode enr + peer-id
|
|
181
|
+
const bootnodeDir = path.join(beaconPaths.dataDir, "bootnode");
|
|
182
|
+
const {discv5: discv5Args} = parseNetworkArgs(args);
|
|
183
|
+
const metricsArgs = parseMetricsArgs(args);
|
|
184
|
+
if (!discv5Args) {
|
|
185
|
+
// Unreachable because bootnode requires discv5 to be enabled - duh
|
|
186
|
+
throw new Error("unreachable - bootnode requires discv5 to be enabled");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// initialize directories
|
|
190
|
+
mkdir(beaconPaths.dataDir);
|
|
191
|
+
mkdir(bootnodeDir);
|
|
192
|
+
|
|
193
|
+
// Fetch extra bootnodes
|
|
194
|
+
discv5Args.bootEnrs = (discv5Args.bootEnrs ?? []).concat(
|
|
195
|
+
args.bootnodesFile ? readBootnodes(args.bootnodesFile) : [],
|
|
196
|
+
isKnownNetworkName(network) ? await getNetworkBootnodes(network) : []
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const logger = initLogger(args, beaconPaths.dataDir, config, "bootnode.log");
|
|
200
|
+
const {privateKey, enr} = await initPrivateKeyAndEnr(args as unknown as BeaconArgs, bootnodeDir, logger, true);
|
|
201
|
+
|
|
202
|
+
return {discv5Args, metricsArgs, bootnodeDir, network, version, commit, privateKey, enr, logger};
|
|
203
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {CliCommand, CliCommandOptions} from "@lodestar/utils";
|
|
2
|
+
import {GlobalArgs} from "../../options/index.js";
|
|
3
|
+
import {bootnodeHandler} from "./handler.js";
|
|
4
|
+
import {BootnodeArgs, bootnodeOptions} from "./options.js";
|
|
5
|
+
|
|
6
|
+
export const bootnode: CliCommand<BootnodeArgs, GlobalArgs> = {
|
|
7
|
+
command: "bootnode",
|
|
8
|
+
describe:
|
|
9
|
+
"Run a discv5 bootnode. This will NOT perform any beacon node functions, rather, it will run a discv5 service that allows nodes on the network to discover one another.",
|
|
10
|
+
docsFolder: "run/bootnode",
|
|
11
|
+
options: bootnodeOptions as CliCommandOptions<BootnodeArgs>,
|
|
12
|
+
handler: bootnodeHandler,
|
|
13
|
+
};
|