@chainsafe/lodestar 1.35.0-dev.e18102ed8c → 1.35.0-dev.f2a741bbe4

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.
Files changed (233) hide show
  1. package/.git-data.json +1 -1
  2. package/bin/lodestar.js +3 -0
  3. package/bin/lodestar.ts +3 -0
  4. package/lib/applyPreset.d.ts.map +1 -0
  5. package/lib/applyPreset.js +1 -1
  6. package/lib/applyPreset.js.map +1 -1
  7. package/lib/cli.d.ts.map +1 -0
  8. package/lib/cmds/beacon/handler.d.ts +1 -1
  9. package/lib/cmds/beacon/handler.d.ts.map +1 -0
  10. package/lib/cmds/beacon/handler.js +1 -1
  11. package/lib/cmds/beacon/handler.js.map +1 -1
  12. package/lib/cmds/beacon/index.d.ts.map +1 -0
  13. package/lib/cmds/beacon/initBeaconState.d.ts.map +1 -0
  14. package/lib/cmds/beacon/initPeerIdAndEnr.d.ts.map +1 -0
  15. package/lib/cmds/beacon/options.d.ts.map +1 -0
  16. package/lib/cmds/beacon/paths.d.ts.map +1 -0
  17. package/lib/cmds/bootnode/handler.d.ts.map +1 -0
  18. package/lib/cmds/bootnode/index.d.ts.map +1 -0
  19. package/lib/cmds/bootnode/options.d.ts.map +1 -0
  20. package/lib/cmds/dev/files.d.ts.map +1 -0
  21. package/lib/cmds/dev/handler.d.ts.map +1 -0
  22. package/lib/cmds/dev/index.d.ts.map +1 -0
  23. package/lib/cmds/dev/options.d.ts.map +1 -0
  24. package/lib/cmds/index.d.ts.map +1 -0
  25. package/lib/cmds/lightclient/handler.d.ts.map +1 -0
  26. package/lib/cmds/lightclient/index.d.ts.map +1 -0
  27. package/lib/cmds/lightclient/options.d.ts.map +1 -0
  28. package/lib/cmds/validator/blsToExecutionChange.d.ts.map +1 -0
  29. package/lib/cmds/validator/handler.d.ts.map +1 -0
  30. package/lib/cmds/validator/handler.js +1 -1
  31. package/lib/cmds/validator/handler.js.map +1 -1
  32. package/lib/cmds/validator/import.d.ts.map +1 -0
  33. package/lib/cmds/validator/index.d.ts.map +1 -0
  34. package/lib/cmds/validator/keymanager/decryptKeystoreDefinitions.d.ts.map +1 -0
  35. package/lib/cmds/validator/keymanager/decryptKeystores/index.d.ts.map +1 -0
  36. package/lib/cmds/validator/keymanager/decryptKeystores/poolSize.d.ts.map +1 -0
  37. package/lib/cmds/validator/keymanager/decryptKeystores/threadPool.d.ts.map +1 -0
  38. package/lib/cmds/validator/keymanager/decryptKeystores/types.d.ts.map +1 -0
  39. package/lib/cmds/validator/keymanager/decryptKeystores/worker.d.ts.map +1 -0
  40. package/lib/cmds/validator/keymanager/impl.d.ts.map +1 -0
  41. package/lib/cmds/validator/keymanager/interface.d.ts.map +1 -0
  42. package/lib/cmds/validator/keymanager/keystoreCache.d.ts.map +1 -0
  43. package/lib/cmds/validator/keymanager/persistedKeys.d.ts.map +1 -0
  44. package/lib/cmds/validator/keymanager/server.d.ts.map +1 -0
  45. package/lib/cmds/validator/list.d.ts.map +1 -0
  46. package/lib/cmds/validator/options.d.ts.map +1 -0
  47. package/lib/cmds/validator/options.js +5 -3
  48. package/lib/cmds/validator/options.js.map +1 -1
  49. package/lib/cmds/validator/paths.d.ts.map +1 -0
  50. package/lib/cmds/validator/signers/importExternalKeystores.d.ts.map +1 -0
  51. package/lib/cmds/validator/signers/index.d.ts.map +1 -0
  52. package/lib/cmds/validator/signers/logSigners.d.ts.map +1 -0
  53. package/lib/cmds/validator/slashingProtection/export.d.ts.map +1 -0
  54. package/lib/cmds/validator/slashingProtection/import.d.ts.map +1 -0
  55. package/lib/cmds/validator/slashingProtection/index.d.ts.map +1 -0
  56. package/lib/cmds/validator/slashingProtection/options.d.ts.map +1 -0
  57. package/lib/cmds/validator/slashingProtection/utils.d.ts.map +1 -0
  58. package/lib/cmds/validator/slashingProtection/utils.js +1 -1
  59. package/lib/cmds/validator/slashingProtection/utils.js.map +1 -1
  60. package/lib/cmds/validator/voluntaryExit.d.ts.map +1 -0
  61. package/lib/config/beaconNodeOptions.d.ts.map +1 -0
  62. package/lib/config/beaconParams.d.ts.map +1 -0
  63. package/lib/config/index.d.ts.map +1 -0
  64. package/lib/config/peerId.d.ts.map +1 -0
  65. package/lib/config/types.d.ts.map +1 -0
  66. package/lib/index.d.ts.map +1 -0
  67. package/lib/migrations/index.d.ts.map +1 -0
  68. package/lib/networks/chiado.d.ts.map +1 -0
  69. package/lib/networks/dev.d.ts.map +1 -0
  70. package/lib/networks/ephemery.d.ts.map +1 -0
  71. package/lib/networks/gnosis.d.ts.map +1 -0
  72. package/lib/networks/holesky.d.ts.map +1 -0
  73. package/lib/networks/hoodi.d.ts.map +1 -0
  74. package/lib/networks/index.d.ts.map +1 -0
  75. package/lib/networks/mainnet.d.ts.map +1 -0
  76. package/lib/networks/sepolia.d.ts.map +1 -0
  77. package/lib/options/beaconNodeOptions/api.d.ts +6 -0
  78. package/lib/options/beaconNodeOptions/api.d.ts.map +1 -0
  79. package/lib/options/beaconNodeOptions/api.js +14 -6
  80. package/lib/options/beaconNodeOptions/api.js.map +1 -1
  81. package/lib/options/beaconNodeOptions/builder.d.ts.map +1 -0
  82. package/lib/options/beaconNodeOptions/chain.d.ts.map +1 -0
  83. package/lib/options/beaconNodeOptions/eth1.d.ts.map +1 -0
  84. package/lib/options/beaconNodeOptions/execution.d.ts.map +1 -0
  85. package/lib/options/beaconNodeOptions/index.d.ts.map +1 -0
  86. package/lib/options/beaconNodeOptions/metrics.d.ts.map +1 -0
  87. package/lib/options/beaconNodeOptions/monitoring.d.ts.map +1 -0
  88. package/lib/options/beaconNodeOptions/network.d.ts.map +1 -0
  89. package/lib/options/beaconNodeOptions/sync.d.ts.map +1 -0
  90. package/lib/options/globalOptions.d.ts.map +1 -0
  91. package/lib/options/index.d.ts.map +1 -0
  92. package/lib/options/logOptions.d.ts.map +1 -0
  93. package/lib/options/paramsOptions.d.ts.map +1 -0
  94. package/lib/paths/global.d.ts.map +1 -0
  95. package/lib/paths/rootDir.d.ts.map +1 -0
  96. package/lib/util/errors.d.ts.map +1 -0
  97. package/lib/util/ethers.d.ts.map +1 -0
  98. package/lib/util/feeRecipient.d.ts.map +1 -0
  99. package/lib/util/file.d.ts.map +1 -0
  100. package/lib/util/file.js.map +1 -1
  101. package/lib/util/format.d.ts.map +1 -0
  102. package/lib/util/fs.d.ts.map +1 -0
  103. package/lib/util/gitData/gitDataPath.d.ts.map +1 -0
  104. package/lib/util/gitData/index.d.ts.map +1 -0
  105. package/lib/util/gitData/writeGitData.d.ts.map +1 -0
  106. package/lib/util/hasher_bun.d.ts +3 -0
  107. package/lib/util/hasher_bun.d.ts.map +1 -0
  108. package/lib/util/hasher_bun.js +118 -0
  109. package/lib/util/hasher_bun.js.map +1 -0
  110. package/lib/util/hasher_nodejs.d.ts +3 -0
  111. package/lib/util/hasher_nodejs.d.ts.map +1 -0
  112. package/lib/util/hasher_nodejs.js +3 -0
  113. package/lib/util/hasher_nodejs.js.map +1 -0
  114. package/lib/util/index.d.ts.map +1 -0
  115. package/lib/util/jwt.d.ts.map +1 -0
  116. package/lib/util/lockfile.d.ts.map +1 -0
  117. package/lib/util/logger.d.ts.map +1 -0
  118. package/lib/util/logger.js +1 -1
  119. package/lib/util/logger.js.map +1 -1
  120. package/lib/util/object.d.ts.map +1 -0
  121. package/lib/util/passphrase.d.ts.map +1 -0
  122. package/lib/util/process.d.ts.map +1 -0
  123. package/lib/util/progress.d.ts.map +1 -0
  124. package/lib/util/proposerConfig.d.ts.map +1 -0
  125. package/lib/util/pruneOldFilesInDir.d.ts.map +1 -0
  126. package/lib/util/sleep.d.ts.map +1 -0
  127. package/lib/util/stripOffNewlines.d.ts.map +1 -0
  128. package/lib/util/types.d.ts.map +1 -0
  129. package/lib/util/version.d.ts.map +1 -0
  130. package/package.json +32 -19
  131. package/src/applyPreset.ts +92 -0
  132. package/src/cli.ts +56 -0
  133. package/src/cmds/beacon/handler.ts +267 -0
  134. package/src/cmds/beacon/index.ts +18 -0
  135. package/src/cmds/beacon/initBeaconState.ts +275 -0
  136. package/src/cmds/beacon/initPeerIdAndEnr.ts +199 -0
  137. package/src/cmds/beacon/options.ts +214 -0
  138. package/src/cmds/beacon/paths.ts +62 -0
  139. package/src/cmds/bootnode/handler.ts +203 -0
  140. package/src/cmds/bootnode/index.ts +13 -0
  141. package/src/cmds/bootnode/options.ts +109 -0
  142. package/src/cmds/dev/files.ts +52 -0
  143. package/src/cmds/dev/handler.ts +86 -0
  144. package/src/cmds/dev/index.ts +18 -0
  145. package/src/cmds/dev/options.ts +110 -0
  146. package/src/cmds/index.ts +15 -0
  147. package/src/cmds/lightclient/handler.ts +36 -0
  148. package/src/cmds/lightclient/index.ts +18 -0
  149. package/src/cmds/lightclient/options.ts +21 -0
  150. package/src/cmds/validator/blsToExecutionChange.ts +91 -0
  151. package/src/cmds/validator/handler.ts +300 -0
  152. package/src/cmds/validator/import.ts +111 -0
  153. package/src/cmds/validator/index.ts +28 -0
  154. package/src/cmds/validator/keymanager/decryptKeystoreDefinitions.ts +189 -0
  155. package/src/cmds/validator/keymanager/decryptKeystores/index.ts +1 -0
  156. package/src/cmds/validator/keymanager/decryptKeystores/poolSize.ts +16 -0
  157. package/src/cmds/validator/keymanager/decryptKeystores/threadPool.ts +75 -0
  158. package/src/cmds/validator/keymanager/decryptKeystores/types.ts +12 -0
  159. package/src/cmds/validator/keymanager/decryptKeystores/worker.ts +24 -0
  160. package/src/cmds/validator/keymanager/impl.ts +425 -0
  161. package/src/cmds/validator/keymanager/interface.ts +35 -0
  162. package/src/cmds/validator/keymanager/keystoreCache.ts +91 -0
  163. package/src/cmds/validator/keymanager/persistedKeys.ts +268 -0
  164. package/src/cmds/validator/keymanager/server.ts +86 -0
  165. package/src/cmds/validator/list.ts +35 -0
  166. package/src/cmds/validator/options.ts +463 -0
  167. package/src/cmds/validator/paths.ts +95 -0
  168. package/src/cmds/validator/signers/importExternalKeystores.ts +69 -0
  169. package/src/cmds/validator/signers/index.ts +176 -0
  170. package/src/cmds/validator/signers/logSigners.ts +81 -0
  171. package/src/cmds/validator/slashingProtection/export.ts +110 -0
  172. package/src/cmds/validator/slashingProtection/import.ts +70 -0
  173. package/src/cmds/validator/slashingProtection/index.ts +12 -0
  174. package/src/cmds/validator/slashingProtection/options.ts +15 -0
  175. package/src/cmds/validator/slashingProtection/utils.ts +56 -0
  176. package/src/cmds/validator/voluntaryExit.ts +232 -0
  177. package/src/config/beaconNodeOptions.ts +68 -0
  178. package/src/config/beaconParams.ts +87 -0
  179. package/src/config/index.ts +3 -0
  180. package/src/config/peerId.ts +50 -0
  181. package/src/config/types.ts +3 -0
  182. package/src/index.ts +28 -0
  183. package/src/migrations/index.ts +0 -0
  184. package/src/networks/chiado.ts +20 -0
  185. package/src/networks/dev.ts +27 -0
  186. package/src/networks/ephemery.ts +9 -0
  187. package/src/networks/gnosis.ts +18 -0
  188. package/src/networks/holesky.ts +17 -0
  189. package/src/networks/hoodi.ts +16 -0
  190. package/src/networks/index.ts +236 -0
  191. package/src/networks/mainnet.ts +34 -0
  192. package/src/networks/sepolia.ts +17 -0
  193. package/src/options/beaconNodeOptions/api.ts +120 -0
  194. package/src/options/beaconNodeOptions/builder.ts +63 -0
  195. package/src/options/beaconNodeOptions/chain.ts +326 -0
  196. package/src/options/beaconNodeOptions/eth1.ts +95 -0
  197. package/src/options/beaconNodeOptions/execution.ts +92 -0
  198. package/src/options/beaconNodeOptions/index.ts +50 -0
  199. package/src/options/beaconNodeOptions/metrics.ts +39 -0
  200. package/src/options/beaconNodeOptions/monitoring.ts +61 -0
  201. package/src/options/beaconNodeOptions/network.ts +401 -0
  202. package/src/options/beaconNodeOptions/sync.ts +65 -0
  203. package/src/options/globalOptions.ts +72 -0
  204. package/src/options/index.ts +3 -0
  205. package/src/options/logOptions.ts +70 -0
  206. package/src/options/paramsOptions.ts +72 -0
  207. package/src/paths/global.ts +24 -0
  208. package/src/paths/rootDir.ts +11 -0
  209. package/src/util/errors.ts +20 -0
  210. package/src/util/ethers.ts +44 -0
  211. package/src/util/feeRecipient.ts +6 -0
  212. package/src/util/file.ts +167 -0
  213. package/src/util/format.ts +76 -0
  214. package/src/util/fs.ts +59 -0
  215. package/src/util/gitData/gitDataPath.ts +48 -0
  216. package/src/util/gitData/index.ts +70 -0
  217. package/src/util/gitData/writeGitData.ts +10 -0
  218. package/src/util/hasher_bun.ts +133 -0
  219. package/src/util/hasher_nodejs.ts +3 -0
  220. package/src/util/index.ts +17 -0
  221. package/src/util/jwt.ts +10 -0
  222. package/src/util/lockfile.ts +45 -0
  223. package/src/util/logger.ts +105 -0
  224. package/src/util/object.ts +15 -0
  225. package/src/util/passphrase.ts +25 -0
  226. package/src/util/process.ts +25 -0
  227. package/src/util/progress.ts +58 -0
  228. package/src/util/proposerConfig.ts +136 -0
  229. package/src/util/pruneOldFilesInDir.ts +27 -0
  230. package/src/util/sleep.ts +3 -0
  231. package/src/util/stripOffNewlines.ts +6 -0
  232. package/src/util/types.ts +8 -0
  233. 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
+ };