@chainsafe/lodestar 1.35.0-dev.e9dd48f165 → 1.35.0-dev.fcf8d024ea

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 (256) 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/cli.d.ts +3 -3
  6. package/lib/cli.d.ts.map +1 -0
  7. package/lib/cli.js +1 -1
  8. package/lib/cli.js.map +1 -1
  9. package/lib/cmds/beacon/handler.d.ts +1 -1
  10. package/lib/cmds/beacon/handler.d.ts.map +1 -0
  11. package/lib/cmds/beacon/handler.js +1 -1
  12. package/lib/cmds/beacon/handler.js.map +1 -1
  13. package/lib/cmds/beacon/index.d.ts.map +1 -0
  14. package/lib/cmds/beacon/initBeaconState.d.ts.map +1 -0
  15. package/lib/cmds/beacon/initBeaconState.js.map +1 -1
  16. package/lib/cmds/beacon/initPeerIdAndEnr.d.ts +2 -2
  17. package/lib/cmds/beacon/initPeerIdAndEnr.d.ts.map +1 -0
  18. package/lib/cmds/beacon/initPeerIdAndEnr.js +1 -1
  19. package/lib/cmds/beacon/initPeerIdAndEnr.js.map +1 -1
  20. package/lib/cmds/beacon/options.d.ts.map +1 -0
  21. package/lib/cmds/beacon/paths.d.ts.map +1 -0
  22. package/lib/cmds/bootnode/handler.d.ts +13 -8
  23. package/lib/cmds/bootnode/handler.d.ts.map +1 -0
  24. package/lib/cmds/bootnode/handler.js +2 -2
  25. package/lib/cmds/bootnode/handler.js.map +1 -1
  26. package/lib/cmds/bootnode/index.d.ts.map +1 -0
  27. package/lib/cmds/bootnode/options.d.ts.map +1 -0
  28. package/lib/cmds/bootnode/options.js +2 -1
  29. package/lib/cmds/bootnode/options.js.map +1 -1
  30. package/lib/cmds/dev/files.d.ts.map +1 -0
  31. package/lib/cmds/dev/handler.d.ts.map +1 -0
  32. package/lib/cmds/dev/handler.js +1 -1
  33. package/lib/cmds/dev/handler.js.map +1 -1
  34. package/lib/cmds/dev/index.d.ts.map +1 -0
  35. package/lib/cmds/dev/options.d.ts.map +1 -0
  36. package/lib/cmds/index.d.ts.map +1 -0
  37. package/lib/cmds/lightclient/handler.d.ts.map +1 -0
  38. package/lib/cmds/lightclient/index.d.ts.map +1 -0
  39. package/lib/cmds/lightclient/options.d.ts.map +1 -0
  40. package/lib/cmds/validator/blsToExecutionChange.d.ts.map +1 -0
  41. package/lib/cmds/validator/blsToExecutionChange.js.map +1 -1
  42. package/lib/cmds/validator/handler.d.ts.map +1 -0
  43. package/lib/cmds/validator/handler.js +3 -5
  44. package/lib/cmds/validator/handler.js.map +1 -1
  45. package/lib/cmds/validator/import.d.ts.map +1 -0
  46. package/lib/cmds/validator/index.d.ts.map +1 -0
  47. package/lib/cmds/validator/keymanager/decryptKeystoreDefinitions.d.ts.map +1 -0
  48. package/lib/cmds/validator/keymanager/decryptKeystores/index.d.ts.map +1 -0
  49. package/lib/cmds/validator/keymanager/decryptKeystores/poolSize.d.ts.map +1 -0
  50. package/lib/cmds/validator/keymanager/decryptKeystores/threadPool.d.ts.map +1 -0
  51. package/lib/cmds/validator/keymanager/decryptKeystores/threadPool.js +4 -1
  52. package/lib/cmds/validator/keymanager/decryptKeystores/threadPool.js.map +1 -1
  53. package/lib/cmds/validator/keymanager/decryptKeystores/types.d.ts.map +1 -0
  54. package/lib/cmds/validator/keymanager/decryptKeystores/worker.d.ts.map +1 -0
  55. package/lib/cmds/validator/keymanager/impl.d.ts.map +1 -0
  56. package/lib/cmds/validator/keymanager/impl.js +4 -0
  57. package/lib/cmds/validator/keymanager/impl.js.map +1 -1
  58. package/lib/cmds/validator/keymanager/interface.d.ts.map +1 -0
  59. package/lib/cmds/validator/keymanager/keystoreCache.d.ts.map +1 -0
  60. package/lib/cmds/validator/keymanager/persistedKeys.d.ts.map +1 -0
  61. package/lib/cmds/validator/keymanager/persistedKeys.js +1 -0
  62. package/lib/cmds/validator/keymanager/persistedKeys.js.map +1 -1
  63. package/lib/cmds/validator/keymanager/server.d.ts.map +1 -0
  64. package/lib/cmds/validator/keymanager/server.js +2 -0
  65. package/lib/cmds/validator/keymanager/server.js.map +1 -1
  66. package/lib/cmds/validator/list.d.ts.map +1 -0
  67. package/lib/cmds/validator/options.d.ts.map +1 -0
  68. package/lib/cmds/validator/paths.d.ts.map +1 -0
  69. package/lib/cmds/validator/signers/importExternalKeystores.d.ts.map +1 -0
  70. package/lib/cmds/validator/signers/index.d.ts.map +1 -0
  71. package/lib/cmds/validator/signers/logSigners.d.ts.map +1 -0
  72. package/lib/cmds/validator/slashingProtection/export.d.ts.map +1 -0
  73. package/lib/cmds/validator/slashingProtection/import.d.ts.map +1 -0
  74. package/lib/cmds/validator/slashingProtection/index.d.ts.map +1 -0
  75. package/lib/cmds/validator/slashingProtection/options.d.ts.map +1 -0
  76. package/lib/cmds/validator/slashingProtection/utils.d.ts.map +1 -0
  77. package/lib/cmds/validator/slashingProtection/utils.js +1 -1
  78. package/lib/cmds/validator/slashingProtection/utils.js.map +1 -1
  79. package/lib/cmds/validator/voluntaryExit.d.ts.map +1 -0
  80. package/lib/cmds/validator/voluntaryExit.js +1 -1
  81. package/lib/cmds/validator/voluntaryExit.js.map +1 -1
  82. package/lib/config/beaconNodeOptions.d.ts.map +1 -0
  83. package/lib/config/beaconNodeOptions.js +2 -1
  84. package/lib/config/beaconNodeOptions.js.map +1 -1
  85. package/lib/config/beaconParams.d.ts.map +1 -0
  86. package/lib/config/index.d.ts.map +1 -0
  87. package/lib/config/peerId.d.ts.map +1 -0
  88. package/lib/config/types.d.ts.map +1 -0
  89. package/lib/index.d.ts.map +1 -0
  90. package/lib/index.js.map +1 -1
  91. package/lib/migrations/index.d.ts +1 -0
  92. package/lib/migrations/index.d.ts.map +1 -0
  93. package/lib/migrations/index.js +1 -1
  94. package/lib/networks/chiado.d.ts.map +1 -0
  95. package/lib/networks/dev.d.ts.map +1 -0
  96. package/lib/networks/ephemery.d.ts.map +1 -0
  97. package/lib/networks/gnosis.d.ts.map +1 -0
  98. package/lib/networks/holesky.d.ts.map +1 -0
  99. package/lib/networks/hoodi.d.ts.map +1 -0
  100. package/lib/networks/index.d.ts.map +1 -0
  101. package/lib/networks/index.js +1 -2
  102. package/lib/networks/index.js.map +1 -1
  103. package/lib/networks/mainnet.d.ts.map +1 -0
  104. package/lib/networks/sepolia.d.ts.map +1 -0
  105. package/lib/options/beaconNodeOptions/api.d.ts.map +1 -0
  106. package/lib/options/beaconNodeOptions/builder.d.ts.map +1 -0
  107. package/lib/options/beaconNodeOptions/chain.d.ts.map +1 -0
  108. package/lib/options/beaconNodeOptions/eth1.d.ts.map +1 -0
  109. package/lib/options/beaconNodeOptions/execution.d.ts.map +1 -0
  110. package/lib/options/beaconNodeOptions/index.d.ts.map +1 -0
  111. package/lib/options/beaconNodeOptions/metrics.d.ts.map +1 -0
  112. package/lib/options/beaconNodeOptions/monitoring.d.ts.map +1 -0
  113. package/lib/options/beaconNodeOptions/network.d.ts +1 -0
  114. package/lib/options/beaconNodeOptions/network.d.ts.map +1 -0
  115. package/lib/options/beaconNodeOptions/network.js +6 -3
  116. package/lib/options/beaconNodeOptions/network.js.map +1 -1
  117. package/lib/options/beaconNodeOptions/sync.d.ts.map +1 -0
  118. package/lib/options/globalOptions.d.ts.map +1 -0
  119. package/lib/options/index.d.ts.map +1 -0
  120. package/lib/options/logOptions.d.ts.map +1 -0
  121. package/lib/options/paramsOptions.d.ts.map +1 -0
  122. package/lib/paths/global.d.ts.map +1 -0
  123. package/lib/paths/rootDir.d.ts.map +1 -0
  124. package/lib/util/errors.d.ts.map +1 -0
  125. package/lib/util/ethers.d.ts.map +1 -0
  126. package/lib/util/feeRecipient.d.ts.map +1 -0
  127. package/lib/util/file.d.ts.map +1 -0
  128. package/lib/util/file.js +1 -1
  129. package/lib/util/file.js.map +1 -1
  130. package/lib/util/format.d.ts.map +1 -0
  131. package/lib/util/fs.d.ts.map +1 -0
  132. package/lib/util/gitData/gitDataPath.d.ts.map +1 -0
  133. package/lib/util/gitData/index.d.ts.map +1 -0
  134. package/lib/util/gitData/index.js.map +1 -1
  135. package/lib/util/gitData/writeGitData.d.ts.map +1 -0
  136. package/lib/util/index.d.ts +3 -3
  137. package/lib/util/index.d.ts.map +1 -0
  138. package/lib/util/index.js +3 -3
  139. package/lib/util/index.js.map +1 -1
  140. package/lib/util/jwt.d.ts.map +1 -0
  141. package/lib/util/lockfile.d.ts.map +1 -0
  142. package/lib/util/logger.d.ts.map +1 -0
  143. package/lib/util/object.d.ts.map +1 -0
  144. package/lib/util/object.js.map +1 -1
  145. package/lib/util/passphrase.d.ts.map +1 -0
  146. package/lib/util/process.d.ts.map +1 -0
  147. package/lib/util/progress.d.ts.map +1 -0
  148. package/lib/util/proposerConfig.d.ts.map +1 -0
  149. package/lib/util/proposerConfig.js.map +1 -1
  150. package/lib/util/pruneOldFilesInDir.d.ts.map +1 -0
  151. package/lib/util/sleep.d.ts.map +1 -0
  152. package/lib/util/stripOffNewlines.d.ts.map +1 -0
  153. package/lib/util/types.d.ts.map +1 -0
  154. package/lib/util/version.d.ts.map +1 -0
  155. package/package.json +20 -19
  156. package/src/applyPreset.ts +91 -0
  157. package/src/cli.ts +56 -0
  158. package/src/cmds/beacon/handler.ts +267 -0
  159. package/src/cmds/beacon/index.ts +18 -0
  160. package/src/cmds/beacon/initBeaconState.ts +275 -0
  161. package/src/cmds/beacon/initPeerIdAndEnr.ts +199 -0
  162. package/src/cmds/beacon/options.ts +214 -0
  163. package/src/cmds/beacon/paths.ts +62 -0
  164. package/src/cmds/bootnode/handler.ts +203 -0
  165. package/src/cmds/bootnode/index.ts +13 -0
  166. package/src/cmds/bootnode/options.ts +109 -0
  167. package/src/cmds/dev/files.ts +52 -0
  168. package/src/cmds/dev/handler.ts +86 -0
  169. package/src/cmds/dev/index.ts +18 -0
  170. package/src/cmds/dev/options.ts +110 -0
  171. package/src/cmds/index.ts +15 -0
  172. package/src/cmds/lightclient/handler.ts +36 -0
  173. package/src/cmds/lightclient/index.ts +18 -0
  174. package/src/cmds/lightclient/options.ts +21 -0
  175. package/src/cmds/validator/blsToExecutionChange.ts +91 -0
  176. package/src/cmds/validator/handler.ts +300 -0
  177. package/src/cmds/validator/import.ts +111 -0
  178. package/src/cmds/validator/index.ts +28 -0
  179. package/src/cmds/validator/keymanager/decryptKeystoreDefinitions.ts +189 -0
  180. package/src/cmds/validator/keymanager/decryptKeystores/index.ts +1 -0
  181. package/src/cmds/validator/keymanager/decryptKeystores/poolSize.ts +16 -0
  182. package/src/cmds/validator/keymanager/decryptKeystores/threadPool.ts +75 -0
  183. package/src/cmds/validator/keymanager/decryptKeystores/types.ts +12 -0
  184. package/src/cmds/validator/keymanager/decryptKeystores/worker.ts +24 -0
  185. package/src/cmds/validator/keymanager/impl.ts +425 -0
  186. package/src/cmds/validator/keymanager/interface.ts +35 -0
  187. package/src/cmds/validator/keymanager/keystoreCache.ts +91 -0
  188. package/src/cmds/validator/keymanager/persistedKeys.ts +268 -0
  189. package/src/cmds/validator/keymanager/server.ts +86 -0
  190. package/src/cmds/validator/list.ts +35 -0
  191. package/src/cmds/validator/options.ts +461 -0
  192. package/src/cmds/validator/paths.ts +95 -0
  193. package/src/cmds/validator/signers/importExternalKeystores.ts +69 -0
  194. package/src/cmds/validator/signers/index.ts +176 -0
  195. package/src/cmds/validator/signers/logSigners.ts +81 -0
  196. package/src/cmds/validator/slashingProtection/export.ts +110 -0
  197. package/src/cmds/validator/slashingProtection/import.ts +70 -0
  198. package/src/cmds/validator/slashingProtection/index.ts +12 -0
  199. package/src/cmds/validator/slashingProtection/options.ts +15 -0
  200. package/src/cmds/validator/slashingProtection/utils.ts +56 -0
  201. package/src/cmds/validator/voluntaryExit.ts +232 -0
  202. package/src/config/beaconNodeOptions.ts +68 -0
  203. package/src/config/beaconParams.ts +87 -0
  204. package/src/config/index.ts +3 -0
  205. package/src/config/peerId.ts +50 -0
  206. package/src/config/types.ts +3 -0
  207. package/src/index.ts +28 -0
  208. package/src/migrations/index.ts +0 -0
  209. package/src/networks/chiado.ts +20 -0
  210. package/src/networks/dev.ts +27 -0
  211. package/src/networks/ephemery.ts +9 -0
  212. package/src/networks/gnosis.ts +18 -0
  213. package/src/networks/holesky.ts +17 -0
  214. package/src/networks/hoodi.ts +16 -0
  215. package/src/networks/index.ts +236 -0
  216. package/src/networks/mainnet.ts +34 -0
  217. package/src/networks/sepolia.ts +17 -0
  218. package/src/options/beaconNodeOptions/api.ts +110 -0
  219. package/src/options/beaconNodeOptions/builder.ts +63 -0
  220. package/src/options/beaconNodeOptions/chain.ts +326 -0
  221. package/src/options/beaconNodeOptions/eth1.ts +95 -0
  222. package/src/options/beaconNodeOptions/execution.ts +92 -0
  223. package/src/options/beaconNodeOptions/index.ts +50 -0
  224. package/src/options/beaconNodeOptions/metrics.ts +39 -0
  225. package/src/options/beaconNodeOptions/monitoring.ts +61 -0
  226. package/src/options/beaconNodeOptions/network.ts +401 -0
  227. package/src/options/beaconNodeOptions/sync.ts +65 -0
  228. package/src/options/globalOptions.ts +72 -0
  229. package/src/options/index.ts +3 -0
  230. package/src/options/logOptions.ts +70 -0
  231. package/src/options/paramsOptions.ts +72 -0
  232. package/src/paths/global.ts +24 -0
  233. package/src/paths/rootDir.ts +11 -0
  234. package/src/util/errors.ts +20 -0
  235. package/src/util/ethers.ts +44 -0
  236. package/src/util/feeRecipient.ts +6 -0
  237. package/src/util/file.ts +167 -0
  238. package/src/util/format.ts +76 -0
  239. package/src/util/fs.ts +59 -0
  240. package/src/util/gitData/gitDataPath.ts +48 -0
  241. package/src/util/gitData/index.ts +70 -0
  242. package/src/util/gitData/writeGitData.ts +10 -0
  243. package/src/util/index.ts +17 -0
  244. package/src/util/jwt.ts +10 -0
  245. package/src/util/lockfile.ts +45 -0
  246. package/src/util/logger.ts +105 -0
  247. package/src/util/object.ts +15 -0
  248. package/src/util/passphrase.ts +25 -0
  249. package/src/util/process.ts +25 -0
  250. package/src/util/progress.ts +58 -0
  251. package/src/util/proposerConfig.ts +136 -0
  252. package/src/util/pruneOldFilesInDir.ts +27 -0
  253. package/src/util/sleep.ts +3 -0
  254. package/src/util/stripOffNewlines.ts +6 -0
  255. package/src/util/types.ts +8 -0
  256. package/src/util/version.ts +74 -0
@@ -0,0 +1,267 @@
1
+ import path from "node:path";
2
+ import {getHeapStatistics} from "node:v8";
3
+ import {SignableENR} from "@chainsafe/enr";
4
+ import {hasher} from "@chainsafe/persistent-merkle-tree";
5
+ import {BeaconDb, BeaconNode} from "@lodestar/beacon-node";
6
+ import {ChainForkConfig, createBeaconConfig} from "@lodestar/config";
7
+ import {LevelDbController} from "@lodestar/db/controller/level";
8
+ import {LoggerNode, getNodeLogger} from "@lodestar/logger/node";
9
+ import {ACTIVE_PRESET, PresetName} from "@lodestar/params";
10
+ import {ErrorAborted, bytesToInt} from "@lodestar/utils";
11
+ import {ProcessShutdownCallback} from "@lodestar/validator";
12
+ import {BeaconNodeOptions, getBeaconConfigFromArgs} from "../../config/index.js";
13
+ import {getNetworkBootnodes, getNetworkData, isKnownNetworkName, readBootnodes} from "../../networks/index.js";
14
+ import {GlobalArgs, parseBeaconNodeArgs} from "../../options/index.js";
15
+ import {LogArgs} from "../../options/logOptions.js";
16
+ import {
17
+ cleanOldLogFiles,
18
+ mkdir,
19
+ onGracefulShutdown,
20
+ parseLoggerArgs,
21
+ pruneOldFilesInDir,
22
+ writeFile600Perm,
23
+ } from "../../util/index.js";
24
+ import {getVersionData} from "../../util/version.js";
25
+ import {initBeaconState} from "./initBeaconState.js";
26
+ import {initPrivateKeyAndEnr} from "./initPeerIdAndEnr.js";
27
+ import {BeaconArgs} from "./options.js";
28
+ import {getBeaconPaths} from "./paths.js";
29
+
30
+ const DEFAULT_RETENTION_SSZ_OBJECTS_HOURS = 15 * 24;
31
+ const HOURS_TO_MS = 3600 * 1000;
32
+ const EIGHT_GB = 8 * 1024 * 1024 * 1024;
33
+
34
+ /**
35
+ * Runs a beacon node.
36
+ */
37
+ export async function beaconHandler(args: BeaconArgs & GlobalArgs): Promise<void> {
38
+ const {config, options, beaconPaths, network, version, commit, privateKey, logger} = await beaconHandlerInit(args);
39
+
40
+ if (hasher.name !== "hashtree") {
41
+ logger.warn(`hashtree is not supported, using hasher ${hasher.name}`);
42
+ }
43
+
44
+ const heapSizeLimit = getHeapStatistics().heap_size_limit;
45
+ if (heapSizeLimit < EIGHT_GB) {
46
+ logger.warn(
47
+ `Node.js heap size limit is too low, consider increasing it to at least ${EIGHT_GB}. See https://chainsafe.github.io/lodestar/faqs/#running-a-beacon-node for more details.`
48
+ );
49
+ }
50
+
51
+ // initialize directories
52
+ mkdir(beaconPaths.dataDir);
53
+ mkdir(beaconPaths.beaconDir);
54
+ mkdir(beaconPaths.dbDir);
55
+
56
+ const abortController = new AbortController();
57
+
58
+ logger.info("Lodestar", {network, version, commit});
59
+ // Callback for beacon to request forced exit, for e.g. in case of irrecoverable
60
+ // forkchoice errors
61
+ const processShutdownCallback: ProcessShutdownCallback = (err) => {
62
+ logger.error("Process shutdown requested", {}, err);
63
+ process.kill(process.pid, "SIGINT");
64
+ };
65
+
66
+ if (ACTIVE_PRESET === PresetName.minimal) logger.info("ACTIVE_PRESET == minimal preset");
67
+
68
+ const db = new BeaconDb(config, await LevelDbController.create(options.db, {metrics: null, logger}));
69
+ logger.info("Connected to LevelDB database", {path: options.db.name});
70
+
71
+ // BeaconNode setup
72
+ try {
73
+ const {anchorState, wsCheckpoint} = await initBeaconState(
74
+ options,
75
+ args,
76
+ config,
77
+ db,
78
+ logger,
79
+ abortController.signal
80
+ );
81
+ const beaconConfig = createBeaconConfig(config, anchorState.genesisValidatorsRoot);
82
+ const node = await BeaconNode.init({
83
+ opts: options,
84
+ config: beaconConfig,
85
+ db,
86
+ logger,
87
+ processShutdownCallback,
88
+ privateKey,
89
+ dataDir: beaconPaths.dataDir,
90
+ peerStoreDir: beaconPaths.peerStoreDir,
91
+ anchorState,
92
+ wsCheckpoint,
93
+ });
94
+
95
+ // dev debug option to have access to the BN instance
96
+ if (args.attachToGlobalThis) {
97
+ (globalThis as unknown as {bn: BeaconNode}).bn = node;
98
+ }
99
+
100
+ // Prune invalid SSZ objects every interval
101
+ const {persistInvalidSszObjectsDir, persistInvalidSszObjects} = options.chain;
102
+ const pruneInvalidSSZObjectsInterval =
103
+ persistInvalidSszObjectsDir && persistInvalidSszObjects
104
+ ? setInterval(() => {
105
+ try {
106
+ const deletedFileCount = pruneOldFilesInDir(
107
+ persistInvalidSszObjectsDir,
108
+ (args.persistInvalidSszObjectsRetentionHours ?? DEFAULT_RETENTION_SSZ_OBJECTS_HOURS) * HOURS_TO_MS
109
+ );
110
+ logger.info("Pruned invalid SSZ objects", {deletedFileCount});
111
+ } catch (e) {
112
+ logger.warn("Error pruning invalid SSZ objects", {persistInvalidSszObjectsDir}, e as Error);
113
+ }
114
+ // Run every ~1 hour
115
+ }, HOURS_TO_MS)
116
+ : null;
117
+
118
+ // Intercept SIGINT signal, to perform final ops before exiting
119
+ onGracefulShutdown(async () => {
120
+ if (args.persistNetworkIdentity) {
121
+ try {
122
+ const networkIdentity = await node.network.getNetworkIdentity();
123
+ const enrPath = path.join(beaconPaths.beaconDir, "enr");
124
+ writeFile600Perm(enrPath, networkIdentity.enr);
125
+ } catch (e) {
126
+ logger.warn("Unable to persist enr", {}, e as Error);
127
+ }
128
+ }
129
+ abortController.abort();
130
+
131
+ if (pruneInvalidSSZObjectsInterval !== null) {
132
+ clearInterval(pruneInvalidSSZObjectsInterval);
133
+ }
134
+ }, logger.info.bind(logger));
135
+
136
+ abortController.signal.addEventListener(
137
+ "abort",
138
+ async () => {
139
+ try {
140
+ await node.close();
141
+ logger.debug("Beacon node closed");
142
+ // Explicitly exit until active handles issue is resolved
143
+ // See https://github.com/ChainSafe/lodestar/issues/5642
144
+ process.exit(0);
145
+ } catch (e) {
146
+ logger.error("Error closing beacon node", {}, e as Error);
147
+ // Make sure db is always closed gracefully
148
+ await db.close();
149
+ // Must explicitly exit process due to potential active handles
150
+ process.exit(1);
151
+ }
152
+ },
153
+ {once: true}
154
+ );
155
+ } catch (e) {
156
+ await db.close();
157
+
158
+ if (e instanceof ErrorAborted) {
159
+ logger.info(e.message); // Let the user know the abort was received but don't print as error
160
+ } else {
161
+ throw e;
162
+ }
163
+ }
164
+ }
165
+
166
+ /** Separate function to simplify unit testing of options merging */
167
+ export async function beaconHandlerInit(args: BeaconArgs & GlobalArgs) {
168
+ const {config, network} = getBeaconConfigFromArgs(args);
169
+
170
+ const beaconNodeOptions = new BeaconNodeOptions(parseBeaconNodeArgs(args));
171
+
172
+ const {version, commit} = getVersionData();
173
+ const beaconPaths = getBeaconPaths(args, network);
174
+ // TODO: Rename db.name to db.path or db.location
175
+ beaconNodeOptions.set({db: {name: beaconPaths.dbDir}});
176
+ beaconNodeOptions.set({
177
+ chain: {
178
+ validatorMonitorLogs: args.validatorMonitorLogs,
179
+ persistInvalidSszObjectsDir: beaconPaths.persistInvalidSszObjectsDir,
180
+ persistOrphanedBlocksDir: beaconPaths.persistOrphanedBlocksDir,
181
+ },
182
+ });
183
+ // Add metrics metadata to show versioning + network info in Prometheus + Grafana
184
+ beaconNodeOptions.set({metrics: {metadata: {version, commit, network}}});
185
+ // Add detailed version string for API node/version endpoint
186
+ beaconNodeOptions.set({api: {commit, version}});
187
+
188
+ if (args.supernode) {
189
+ beaconNodeOptions.set({chain: {supernode: true}, network: {supernode: true}});
190
+ }
191
+
192
+ // Set known depositContractDeployBlock
193
+ if (isKnownNetworkName(network)) {
194
+ const {depositContractDeployBlock} = getNetworkData(network);
195
+ beaconNodeOptions.set({eth1: {depositContractDeployBlock}});
196
+ }
197
+
198
+ const logger = initLogger(args, beaconPaths.dataDir, config);
199
+ const {privateKey, enr} = await initPrivateKeyAndEnr(args, beaconPaths.beaconDir, logger);
200
+
201
+ if (args.discv5 !== false) {
202
+ // Inject ENR to beacon options
203
+ beaconNodeOptions.set({network: {discv5: {enr: enr.encodeTxt(), config: {enrUpdate: !enr.ip && !enr.ip6}}}});
204
+
205
+ // Combine bootnodes from different sources
206
+ const bootnodes = (beaconNodeOptions.get().network?.discv5?.bootEnrs ?? []).concat(
207
+ args.bootnodesFile ? readBootnodes(args.bootnodesFile) : [],
208
+ isKnownNetworkName(network) ? await getNetworkBootnodes(network) : []
209
+ );
210
+ // Deduplicate and set combined bootnodes
211
+ beaconNodeOptions.set({network: {discv5: {bootEnrs: [...new Set(bootnodes)]}}});
212
+ }
213
+
214
+ beaconNodeOptions.set({chain: {initialCustodyGroupCount: getInitialCustodyGroupCount(args, config, enr)}});
215
+
216
+ if (args.disableLightClientServer) {
217
+ beaconNodeOptions.set({chain: {disableLightClientServer: true}});
218
+ }
219
+
220
+ if (args.private) {
221
+ beaconNodeOptions.set({network: {private: true}, api: {private: true}});
222
+ } else {
223
+ const versionStr = `Lodestar/${version}`;
224
+ const simpleVersionStr = version.split("/")[0];
225
+ // Add simple version string for libp2p agent version
226
+ beaconNodeOptions.set({network: {version: simpleVersionStr}});
227
+ // Add User-Agent header to all builder requests
228
+ beaconNodeOptions.set({executionBuilder: {userAgent: versionStr}});
229
+ // Set jwt version with version string
230
+ beaconNodeOptions.set({executionEngine: {jwtVersion: versionStr}, eth1: {jwtVersion: versionStr}});
231
+ // Set commit and version for ClientVersion
232
+ beaconNodeOptions.set({executionEngine: {commit, version}});
233
+ }
234
+
235
+ // Render final options
236
+ const options = beaconNodeOptions.getWithDefaults();
237
+
238
+ return {config, options, beaconPaths, network, version, commit, privateKey, logger};
239
+ }
240
+
241
+ export function initLogger(
242
+ args: LogArgs & Pick<GlobalArgs, "dataDir">,
243
+ dataDir: string,
244
+ config: ChainForkConfig,
245
+ fileName = "beacon.log"
246
+ ): LoggerNode {
247
+ const defaultLogFilepath = path.join(dataDir, fileName);
248
+ const logger = getNodeLogger(parseLoggerArgs(args, {defaultLogFilepath}, config));
249
+ try {
250
+ cleanOldLogFiles(args, {defaultLogFilepath});
251
+ } catch (e) {
252
+ logger.debug("Not able to delete log files", {}, e as Error);
253
+ }
254
+
255
+ return logger;
256
+ }
257
+
258
+ function getInitialCustodyGroupCount(args: BeaconArgs & GlobalArgs, config: ChainForkConfig, enr: SignableENR): number {
259
+ if (args.supernode) {
260
+ return config.NUMBER_OF_CUSTODY_GROUPS;
261
+ }
262
+
263
+ const enrCgcBytes = enr.kvs.get("cgc");
264
+ const enrCgc = enrCgcBytes != null ? bytesToInt(enrCgcBytes, "be") : 0;
265
+
266
+ return Math.max(enrCgc, config.CUSTODY_REQUIREMENT);
267
+ }
@@ -0,0 +1,18 @@
1
+ import {CliCommand, CliCommandOptions} from "@lodestar/utils";
2
+ import {GlobalArgs} from "../../options/index.js";
3
+ import {beaconHandler} from "./handler.js";
4
+ import {BeaconArgs, beaconOptions} from "./options.js";
5
+
6
+ export const beacon: CliCommand<BeaconArgs, GlobalArgs> = {
7
+ command: "beacon",
8
+ describe: "Run a beacon chain node",
9
+ docsFolder: "run/beacon-management",
10
+ examples: [
11
+ {
12
+ command: "beacon --network hoodi",
13
+ description: "Run a beacon chain node and connect to the hoodi testnet",
14
+ },
15
+ ],
16
+ options: beaconOptions as CliCommandOptions<BeaconArgs>,
17
+ handler: beaconHandler,
18
+ };
@@ -0,0 +1,275 @@
1
+ import {
2
+ IBeaconDb,
3
+ IBeaconNodeOptions,
4
+ checkAndPersistAnchorState,
5
+ getStateTypeFromBytes,
6
+ initStateFromEth1,
7
+ } from "@lodestar/beacon-node";
8
+ import {BeaconConfig, ChainForkConfig, createBeaconConfig} from "@lodestar/config";
9
+ import {
10
+ BeaconStateAllForks,
11
+ ensureWithinWeakSubjectivityPeriod,
12
+ isWithinWeakSubjectivityPeriod,
13
+ loadState,
14
+ loadStateAndValidators,
15
+ } from "@lodestar/state-transition";
16
+ import {ssz} from "@lodestar/types";
17
+ import {Checkpoint} from "@lodestar/types/phase0";
18
+ import {Logger, formatBytes, toRootHex} from "@lodestar/utils";
19
+ import {
20
+ fetchWeakSubjectivityState,
21
+ getCheckpointFromArg,
22
+ getCheckpointFromState,
23
+ getGenesisFileUrl,
24
+ getGenesisStateRoot,
25
+ } from "../../networks/index.js";
26
+ import {GlobalArgs, defaultNetwork} from "../../options/globalOptions.js";
27
+ import {downloadOrLoadFile, wrapFnError} from "../../util/index.js";
28
+ import {BeaconArgs} from "./options.js";
29
+
30
+ type StateWithBytes = {state: BeaconStateAllForks; stateBytes: Uint8Array};
31
+
32
+ async function initAndVerifyWeakSubjectivityState(
33
+ config: BeaconConfig,
34
+ db: IBeaconDb,
35
+ logger: Logger,
36
+ dbStateBytes: StateWithBytes,
37
+ wsStateBytes: StateWithBytes,
38
+ wsCheckpoint: Checkpoint,
39
+ opts: {ignoreWeakSubjectivityCheck?: boolean} = {}
40
+ ): Promise<{anchorState: BeaconStateAllForks; wsCheckpoint: Checkpoint}> {
41
+ const dbState = dbStateBytes.state;
42
+ const wsState = wsStateBytes.state;
43
+ // Check if the store's state and wsState are compatible
44
+ if (
45
+ dbState.genesisTime !== wsState.genesisTime ||
46
+ !ssz.Root.equals(dbState.genesisValidatorsRoot, wsState.genesisValidatorsRoot)
47
+ ) {
48
+ throw new Error(
49
+ "Db state and checkpoint state are not compatible, either clear the db or verify your checkpoint source"
50
+ );
51
+ }
52
+
53
+ // Pick the state which is ahead as an anchor to initialize the beacon chain
54
+ let anchorState = wsStateBytes;
55
+ let anchorCheckpoint = wsCheckpoint;
56
+ let isCheckpointState = true;
57
+ if (dbState.slot > wsState.slot) {
58
+ anchorState = dbStateBytes;
59
+ anchorCheckpoint = getCheckpointFromState(dbState);
60
+ isCheckpointState = false;
61
+ logger.verbose(
62
+ "Db state is ahead of the provided checkpoint state, using the db state to initialize the beacon chain"
63
+ );
64
+ }
65
+
66
+ // Throw error unless user explicitly asked not to, in testnets can happen that wss period is too small
67
+ // that even some epochs of non finalization can cause finalized checkpoint to be out of valid range
68
+ const wssCheck = wrapFnError(() => ensureWithinWeakSubjectivityPeriod(config, anchorState.state, anchorCheckpoint));
69
+ const isWithinWeakSubjectivityPeriod = wssCheck.err === null;
70
+ if (!isWithinWeakSubjectivityPeriod && !opts.ignoreWeakSubjectivityCheck) {
71
+ throw wssCheck.err;
72
+ }
73
+
74
+ await checkAndPersistAnchorState(config, db, logger, anchorState.state, anchorState.stateBytes, {
75
+ isWithinWeakSubjectivityPeriod,
76
+ isCheckpointState,
77
+ });
78
+
79
+ // Return the latest anchorState but still return original wsCheckpoint to validate in backfill
80
+ return {anchorState: anchorState.state, wsCheckpoint};
81
+ }
82
+
83
+ /**
84
+ * Initialize a beacon state, picking the strategy based on the `IBeaconArgs`
85
+ *
86
+ * State is initialized in one of three ways:
87
+ * 1. restore from weak subjectivity state (possibly downloaded from a remote beacon node)
88
+ * 2. restore from db
89
+ * 3. restore from genesis state (possibly downloaded via URL)
90
+ * 4. create genesis state from eth1
91
+ */
92
+ export async function initBeaconState(
93
+ options: IBeaconNodeOptions,
94
+ args: BeaconArgs & GlobalArgs,
95
+ chainForkConfig: ChainForkConfig,
96
+ db: IBeaconDb,
97
+ logger: Logger,
98
+ signal: AbortSignal
99
+ ): Promise<{anchorState: BeaconStateAllForks; wsCheckpoint?: Checkpoint}> {
100
+ if (args.forceCheckpointSync && !(args.checkpointState || args.checkpointSyncUrl)) {
101
+ throw new Error("Forced checkpoint sync without specifying a checkpointState or checkpointSyncUrl");
102
+ }
103
+ // fetch the latest state stored in the db which will be used in all cases, if it exists, either
104
+ // i) used directly as the anchor state
105
+ // ii) used to load and verify a weak subjectivity state,
106
+ const lastDbSlot = await db.stateArchive.lastKey();
107
+ let stateBytes = lastDbSlot !== null ? await db.stateArchive.getBinary(lastDbSlot) : null;
108
+ // Convert to `Uint8Array` to avoid unexpected behavior such as `Buffer.prototype.slice` not copying memory
109
+ stateBytes = stateBytes ? new Uint8Array(stateBytes.buffer, stateBytes.byteOffset, stateBytes.byteLength) : null;
110
+ let lastDbState: BeaconStateAllForks | null = null;
111
+ let lastDbValidatorsBytes: Uint8Array | null = null;
112
+ let lastDbStateWithBytes: StateWithBytes | null = null;
113
+ if (stateBytes) {
114
+ logger.verbose("Found the last archived state", {slot: lastDbSlot, size: formatBytes(stateBytes.length)});
115
+ const {state, validatorsBytes} = loadStateAndValidators(chainForkConfig, stateBytes);
116
+ lastDbState = state;
117
+ lastDbValidatorsBytes = validatorsBytes;
118
+ lastDbStateWithBytes = {state, stateBytes: stateBytes};
119
+ }
120
+
121
+ if (lastDbState) {
122
+ const config = createBeaconConfig(chainForkConfig, lastDbState.genesisValidatorsRoot);
123
+ const wssCheck = isWithinWeakSubjectivityPeriod(config, lastDbState, getCheckpointFromState(lastDbState));
124
+
125
+ // Explicitly force syncing from checkpoint state
126
+ if (args.forceCheckpointSync) {
127
+ // Forcing to sync from checkpoint is only recommended if node is taking too long to sync from last db state.
128
+ // It is important to remind the user to remove this flag again unless it is absolutely necessary.
129
+ if (wssCheck) {
130
+ logger.warn(
131
+ `Forced syncing from checkpoint even though db state at slot ${lastDbState.slot} is within weak subjectivity period`
132
+ );
133
+ logger.warn("Please consider removing --forceCheckpointSync flag unless absolutely necessary");
134
+ }
135
+ } else {
136
+ // All cases when we want to directly use lastDbState as the anchor state:
137
+ // - if no checkpoint sync args provided, or
138
+ // - the lastDbState is within weak subjectivity period:
139
+ if ((!args.checkpointState && !args.checkpointSyncUrl) || wssCheck) {
140
+ if (stateBytes === null) {
141
+ // this never happens
142
+ throw Error(`There is no stateBytes for the lastDbState at slot ${lastDbState.slot}`);
143
+ }
144
+ await checkAndPersistAnchorState(config, db, logger, lastDbState, stateBytes, {
145
+ isWithinWeakSubjectivityPeriod: wssCheck,
146
+ isCheckpointState: false,
147
+ });
148
+ return {anchorState: lastDbState};
149
+ }
150
+ }
151
+ }
152
+
153
+ // See if we can sync state using checkpoint sync args or else start from genesis
154
+ if (args.checkpointState) {
155
+ return readWSState(
156
+ lastDbStateWithBytes,
157
+ lastDbValidatorsBytes,
158
+ {
159
+ checkpointState: args.checkpointState,
160
+ wssCheckpoint: args.wssCheckpoint,
161
+ ignoreWeakSubjectivityCheck: args.ignoreWeakSubjectivityCheck,
162
+ },
163
+ chainForkConfig,
164
+ db,
165
+ logger
166
+ );
167
+ }
168
+
169
+ if (args.checkpointSyncUrl) {
170
+ return fetchWSStateFromBeaconApi(
171
+ lastDbStateWithBytes,
172
+ lastDbValidatorsBytes,
173
+ {
174
+ checkpointSyncUrl: args.checkpointSyncUrl,
175
+ wssCheckpoint: args.wssCheckpoint,
176
+ ignoreWeakSubjectivityCheck: args.ignoreWeakSubjectivityCheck,
177
+ },
178
+ chainForkConfig,
179
+ db,
180
+ logger
181
+ );
182
+ }
183
+
184
+ const genesisStateFile = args.genesisStateFile || getGenesisFileUrl(args.network || defaultNetwork);
185
+ if (genesisStateFile && !args.forceGenesis) {
186
+ let stateBytes = await downloadOrLoadFile(genesisStateFile);
187
+ // Convert to `Uint8Array` to avoid unexpected behavior such as `Buffer.prototype.slice` not copying memory
188
+ stateBytes = new Uint8Array(stateBytes.buffer, stateBytes.byteOffset, stateBytes.byteLength);
189
+ const anchorState = getStateTypeFromBytes(chainForkConfig, stateBytes).deserializeToViewDU(stateBytes);
190
+ // Validate genesis state root
191
+ const stateRoot = toRootHex(anchorState.hashTreeRoot());
192
+ const expectedRoot = getGenesisStateRoot(args.network);
193
+ if (expectedRoot !== null && stateRoot !== expectedRoot) {
194
+ throw Error(`Genesis state root mismatch expected=${expectedRoot} received=${stateRoot}`);
195
+ }
196
+ const config = createBeaconConfig(chainForkConfig, anchorState.genesisValidatorsRoot);
197
+ const wssCheck = isWithinWeakSubjectivityPeriod(config, anchorState, getCheckpointFromState(anchorState));
198
+ await checkAndPersistAnchorState(config, db, logger, anchorState, stateBytes, {
199
+ isWithinWeakSubjectivityPeriod: wssCheck,
200
+ isCheckpointState: true,
201
+ });
202
+ return {anchorState};
203
+ }
204
+
205
+ // Only place we will not bother checking isWithinWeakSubjectivityPeriod as forceGenesis passed by user
206
+ const anchorState = await initStateFromEth1({config: chainForkConfig, db, logger, opts: options.eth1, signal});
207
+ return {anchorState};
208
+ }
209
+
210
+ async function readWSState(
211
+ lastDbStateBytes: StateWithBytes | null,
212
+ lastDbValidatorsBytes: Uint8Array | null,
213
+ wssOpts: {checkpointState: string; wssCheckpoint?: string; ignoreWeakSubjectivityCheck?: boolean},
214
+ chainForkConfig: ChainForkConfig,
215
+ db: IBeaconDb,
216
+ logger: Logger
217
+ ): Promise<{anchorState: BeaconStateAllForks; wsCheckpoint?: Checkpoint}> {
218
+ // weak subjectivity sync from a provided state file:
219
+ // if a weak subjectivity checkpoint has been provided, it is used for additional verification
220
+ // otherwise, the state itself is used for verification (not bad, because the trusted state has been explicitly provided)
221
+ const {checkpointState, wssCheckpoint, ignoreWeakSubjectivityCheck} = wssOpts;
222
+ const lastDbState = lastDbStateBytes?.state ?? null;
223
+
224
+ const stateBytes = await downloadOrLoadFile(checkpointState);
225
+ let wsState: BeaconStateAllForks;
226
+ if (lastDbState && lastDbValidatorsBytes) {
227
+ // use lastDbState to load wsState if possible to share the same state tree
228
+ wsState = loadState(chainForkConfig, lastDbState, stateBytes, lastDbValidatorsBytes).state;
229
+ } else {
230
+ wsState = getStateTypeFromBytes(chainForkConfig, stateBytes).deserializeToViewDU(stateBytes);
231
+ }
232
+ const config = createBeaconConfig(chainForkConfig, wsState.genesisValidatorsRoot);
233
+ const wsStateBytes = {state: wsState, stateBytes};
234
+ const store = lastDbStateBytes ?? wsStateBytes;
235
+ const checkpoint = wssCheckpoint ? getCheckpointFromArg(wssCheckpoint) : getCheckpointFromState(wsState);
236
+ return initAndVerifyWeakSubjectivityState(config, db, logger, store, wsStateBytes, checkpoint, {
237
+ ignoreWeakSubjectivityCheck,
238
+ });
239
+ }
240
+
241
+ async function fetchWSStateFromBeaconApi(
242
+ lastDbStateBytes: StateWithBytes | null,
243
+ lastDbValidatorsBytes: Uint8Array | null,
244
+ wssOpts: {checkpointSyncUrl: string; wssCheckpoint?: string; ignoreWeakSubjectivityCheck?: boolean},
245
+ chainForkConfig: ChainForkConfig,
246
+ db: IBeaconDb,
247
+ logger: Logger
248
+ ): Promise<{anchorState: BeaconStateAllForks; wsCheckpoint?: Checkpoint}> {
249
+ // weak subjectivity sync from a state that needs to be fetched:
250
+ // if a weak subjectivity checkpoint has been provided, it is used to inform which state to download and used for additional verification
251
+ // otherwise, the 'finalized' state is downloaded and the state itself is used for verification (all trust delegated to the remote beacon node)
252
+ try {
253
+ // Validate the weakSubjectivityServerUrl and only log the origin to mask the
254
+ // username password credentials
255
+ const checkpointSyncUrl = new URL(wssOpts.checkpointSyncUrl);
256
+ logger.info("Fetching checkpoint state", {
257
+ checkpointSyncUrl: checkpointSyncUrl.origin,
258
+ });
259
+ } catch (e) {
260
+ logger.error("Invalid", {checkpointSyncUrl: wssOpts.checkpointSyncUrl}, e as Error);
261
+ throw e;
262
+ }
263
+
264
+ const {wsState, wsStateBytes, wsCheckpoint} = await fetchWeakSubjectivityState(chainForkConfig, logger, wssOpts, {
265
+ lastDbState: lastDbStateBytes?.state ?? null,
266
+ lastDbValidatorsBytes,
267
+ });
268
+
269
+ const config = createBeaconConfig(chainForkConfig, wsState.genesisValidatorsRoot);
270
+ const wsStateWithBytes = {state: wsState, stateBytes: wsStateBytes};
271
+ const store = lastDbStateBytes ?? wsStateWithBytes;
272
+ return initAndVerifyWeakSubjectivityState(config, db, logger, store, wsStateWithBytes, wsCheckpoint, {
273
+ ignoreWeakSubjectivityCheck: wssOpts.ignoreWeakSubjectivityCheck,
274
+ });
275
+ }