@lodestar/validator 1.35.0-dev.f80d2d52da → 1.35.0-dev.fd1dac853d

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 (174) hide show
  1. package/lib/buckets.d.ts.map +1 -0
  2. package/lib/defaults.d.ts.map +1 -0
  3. package/lib/genesis.d.ts.map +1 -0
  4. package/lib/index.d.ts +7 -7
  5. package/lib/index.d.ts.map +1 -0
  6. package/lib/index.js +5 -5
  7. package/lib/index.js.map +1 -1
  8. package/lib/metrics.d.ts.map +1 -0
  9. package/lib/metrics.js +14 -14
  10. package/lib/metrics.js.map +1 -1
  11. package/lib/repositories/index.d.ts.map +1 -0
  12. package/lib/repositories/metaDataRepository.d.ts.map +1 -0
  13. package/lib/repositories/metaDataRepository.js +4 -3
  14. package/lib/repositories/metaDataRepository.js.map +1 -1
  15. package/lib/services/attestation.d.ts.map +1 -0
  16. package/lib/services/attestation.js +77 -60
  17. package/lib/services/attestation.js.map +1 -1
  18. package/lib/services/attestationDuties.d.ts.map +1 -0
  19. package/lib/services/attestationDuties.js +105 -98
  20. package/lib/services/attestationDuties.js.map +1 -1
  21. package/lib/services/block.d.ts.map +1 -0
  22. package/lib/services/block.js +64 -56
  23. package/lib/services/block.js.map +1 -1
  24. package/lib/services/blockDuties.d.ts +2 -2
  25. package/lib/services/blockDuties.d.ts.map +1 -0
  26. package/lib/services/blockDuties.js +35 -26
  27. package/lib/services/blockDuties.js.map +1 -1
  28. package/lib/services/chainHeaderTracker.d.ts.map +1 -0
  29. package/lib/services/chainHeaderTracker.js +30 -27
  30. package/lib/services/chainHeaderTracker.js.map +1 -1
  31. package/lib/services/doppelgangerService.d.ts.map +1 -0
  32. package/lib/services/doppelgangerService.js +52 -45
  33. package/lib/services/doppelgangerService.js.map +1 -1
  34. package/lib/services/emitter.d.ts +1 -1
  35. package/lib/services/emitter.d.ts.map +1 -0
  36. package/lib/services/externalSignerSync.d.ts.map +1 -0
  37. package/lib/services/externalSignerSync.js +1 -1
  38. package/lib/services/externalSignerSync.js.map +1 -1
  39. package/lib/services/indices.d.ts.map +1 -0
  40. package/lib/services/indices.js +8 -5
  41. package/lib/services/indices.js.map +1 -1
  42. package/lib/services/prepareBeaconProposer.d.ts.map +1 -0
  43. package/lib/services/prepareBeaconProposer.js.map +1 -1
  44. package/lib/services/syncCommittee.d.ts.map +1 -0
  45. package/lib/services/syncCommittee.js +80 -61
  46. package/lib/services/syncCommittee.js.map +1 -1
  47. package/lib/services/syncCommitteeDuties.d.ts.map +1 -0
  48. package/lib/services/syncCommitteeDuties.js +28 -23
  49. package/lib/services/syncCommitteeDuties.js.map +1 -1
  50. package/lib/services/syncingStatusTracker.d.ts.map +1 -0
  51. package/lib/services/syncingStatusTracker.js +32 -27
  52. package/lib/services/syncingStatusTracker.js.map +1 -1
  53. package/lib/services/utils.d.ts.map +1 -0
  54. package/lib/services/validatorStore.d.ts.map +1 -0
  55. package/lib/services/validatorStore.js +9 -3
  56. package/lib/services/validatorStore.js.map +1 -1
  57. package/lib/slashingProtection/attestation/attestationByTargetRepository.d.ts.map +1 -0
  58. package/lib/slashingProtection/attestation/attestationByTargetRepository.js +7 -3
  59. package/lib/slashingProtection/attestation/attestationByTargetRepository.js.map +1 -1
  60. package/lib/slashingProtection/attestation/attestationLowerBoundRepository.d.ts.map +1 -0
  61. package/lib/slashingProtection/attestation/attestationLowerBoundRepository.js +5 -3
  62. package/lib/slashingProtection/attestation/attestationLowerBoundRepository.js.map +1 -1
  63. package/lib/slashingProtection/attestation/errors.d.ts.map +1 -0
  64. package/lib/slashingProtection/attestation/index.d.ts.map +1 -0
  65. package/lib/slashingProtection/attestation/index.js +3 -0
  66. package/lib/slashingProtection/attestation/index.js.map +1 -1
  67. package/lib/slashingProtection/block/blockBySlotRepository.d.ts.map +1 -0
  68. package/lib/slashingProtection/block/blockBySlotRepository.js +7 -3
  69. package/lib/slashingProtection/block/blockBySlotRepository.js.map +1 -1
  70. package/lib/slashingProtection/block/errors.d.ts.map +1 -0
  71. package/lib/slashingProtection/block/index.d.ts.map +1 -0
  72. package/lib/slashingProtection/block/index.js +1 -0
  73. package/lib/slashingProtection/block/index.js.map +1 -1
  74. package/lib/slashingProtection/index.d.ts +1 -1
  75. package/lib/slashingProtection/index.d.ts.map +1 -0
  76. package/lib/slashingProtection/index.js +3 -0
  77. package/lib/slashingProtection/index.js.map +1 -1
  78. package/lib/slashingProtection/interchange/errors.d.ts.map +1 -0
  79. package/lib/slashingProtection/interchange/formats/completeV4.d.ts.map +1 -0
  80. package/lib/slashingProtection/interchange/formats/index.d.ts.map +1 -0
  81. package/lib/slashingProtection/interchange/formats/v5.d.ts.map +1 -0
  82. package/lib/slashingProtection/interchange/index.d.ts.map +1 -0
  83. package/lib/slashingProtection/interchange/parseInterchange.d.ts.map +1 -0
  84. package/lib/slashingProtection/interchange/serializeInterchange.d.ts.map +1 -0
  85. package/lib/slashingProtection/interchange/types.d.ts.map +1 -0
  86. package/lib/slashingProtection/interface.d.ts.map +1 -0
  87. package/lib/slashingProtection/minMaxSurround/distanceStoreRepository.d.ts.map +1 -0
  88. package/lib/slashingProtection/minMaxSurround/distanceStoreRepository.js +8 -0
  89. package/lib/slashingProtection/minMaxSurround/distanceStoreRepository.js.map +1 -1
  90. package/lib/slashingProtection/minMaxSurround/errors.d.ts.map +1 -0
  91. package/lib/slashingProtection/minMaxSurround/index.d.ts.map +1 -0
  92. package/lib/slashingProtection/minMaxSurround/interface.d.ts.map +1 -0
  93. package/lib/slashingProtection/minMaxSurround/minMaxSurround.d.ts.map +1 -0
  94. package/lib/slashingProtection/minMaxSurround/minMaxSurround.js +2 -0
  95. package/lib/slashingProtection/minMaxSurround/minMaxSurround.js.map +1 -1
  96. package/lib/slashingProtection/types.d.ts.map +1 -0
  97. package/lib/slashingProtection/utils.d.ts +1 -1
  98. package/lib/slashingProtection/utils.d.ts.map +1 -0
  99. package/lib/types.d.ts.map +1 -0
  100. package/lib/util/batch.d.ts.map +1 -0
  101. package/lib/util/clock.d.ts +3 -0
  102. package/lib/util/clock.d.ts.map +1 -0
  103. package/lib/util/clock.js +16 -9
  104. package/lib/util/clock.js.map +1 -1
  105. package/lib/util/difference.d.ts.map +1 -0
  106. package/lib/util/externalSignerClient.d.ts.map +1 -0
  107. package/lib/util/format.d.ts.map +1 -0
  108. package/lib/util/index.d.ts.map +1 -0
  109. package/lib/util/logger.d.ts.map +1 -0
  110. package/lib/util/params.d.ts.map +1 -0
  111. package/lib/util/params.js +18 -2
  112. package/lib/util/params.js.map +1 -1
  113. package/lib/util/url.d.ts.map +1 -0
  114. package/lib/validator.d.ts.map +1 -0
  115. package/lib/validator.js +16 -1
  116. package/lib/validator.js.map +1 -1
  117. package/package.json +19 -16
  118. package/src/buckets.ts +30 -0
  119. package/src/defaults.ts +8 -0
  120. package/src/genesis.ts +19 -0
  121. package/src/index.ts +22 -0
  122. package/src/metrics.ts +417 -0
  123. package/src/repositories/index.ts +1 -0
  124. package/src/repositories/metaDataRepository.ts +42 -0
  125. package/src/services/attestation.ts +362 -0
  126. package/src/services/attestationDuties.ts +406 -0
  127. package/src/services/block.ts +261 -0
  128. package/src/services/blockDuties.ts +217 -0
  129. package/src/services/chainHeaderTracker.ts +89 -0
  130. package/src/services/doppelgangerService.ts +286 -0
  131. package/src/services/emitter.ts +43 -0
  132. package/src/services/externalSignerSync.ts +81 -0
  133. package/src/services/indices.ts +165 -0
  134. package/src/services/prepareBeaconProposer.ts +119 -0
  135. package/src/services/syncCommittee.ts +338 -0
  136. package/src/services/syncCommitteeDuties.ts +337 -0
  137. package/src/services/syncingStatusTracker.ts +74 -0
  138. package/src/services/utils.ts +58 -0
  139. package/src/services/validatorStore.ts +830 -0
  140. package/src/slashingProtection/attestation/attestationByTargetRepository.ts +77 -0
  141. package/src/slashingProtection/attestation/attestationLowerBoundRepository.ts +44 -0
  142. package/src/slashingProtection/attestation/errors.ts +66 -0
  143. package/src/slashingProtection/attestation/index.ts +171 -0
  144. package/src/slashingProtection/block/blockBySlotRepository.ts +78 -0
  145. package/src/slashingProtection/block/errors.ts +28 -0
  146. package/src/slashingProtection/block/index.ts +94 -0
  147. package/src/slashingProtection/index.ts +95 -0
  148. package/src/slashingProtection/interchange/errors.ts +15 -0
  149. package/src/slashingProtection/interchange/formats/completeV4.ts +125 -0
  150. package/src/slashingProtection/interchange/formats/index.ts +7 -0
  151. package/src/slashingProtection/interchange/formats/v5.ts +120 -0
  152. package/src/slashingProtection/interchange/index.ts +5 -0
  153. package/src/slashingProtection/interchange/parseInterchange.ts +55 -0
  154. package/src/slashingProtection/interchange/serializeInterchange.ts +35 -0
  155. package/src/slashingProtection/interchange/types.ts +18 -0
  156. package/src/slashingProtection/interface.ts +28 -0
  157. package/src/slashingProtection/minMaxSurround/distanceStoreRepository.ts +57 -0
  158. package/src/slashingProtection/minMaxSurround/errors.ts +27 -0
  159. package/src/slashingProtection/minMaxSurround/index.ts +4 -0
  160. package/src/slashingProtection/minMaxSurround/interface.ts +23 -0
  161. package/src/slashingProtection/minMaxSurround/minMaxSurround.ts +104 -0
  162. package/src/slashingProtection/types.ts +12 -0
  163. package/src/slashingProtection/utils.ts +42 -0
  164. package/src/types.ts +31 -0
  165. package/src/util/batch.ts +15 -0
  166. package/src/util/clock.ts +169 -0
  167. package/src/util/difference.ts +10 -0
  168. package/src/util/externalSignerClient.ts +277 -0
  169. package/src/util/format.ts +3 -0
  170. package/src/util/index.ts +6 -0
  171. package/src/util/logger.ts +51 -0
  172. package/src/util/params.ts +320 -0
  173. package/src/util/url.ts +16 -0
  174. package/src/validator.ts +418 -0
@@ -0,0 +1,418 @@
1
+ import {ApiClient, ApiRequestInit, defaultInit, getClient, routes} from "@lodestar/api";
2
+ import {BeaconConfig, ChainForkConfig, createBeaconConfig} from "@lodestar/config";
3
+ import {computeEpochAtSlot, getCurrentSlot} from "@lodestar/state-transition";
4
+ import {BLSPubkey, phase0, ssz} from "@lodestar/types";
5
+ import {Genesis} from "@lodestar/types/phase0";
6
+ import {Logger, toPrintableUrl, toRootHex} from "@lodestar/utils";
7
+ import {waitForGenesis} from "./genesis.js";
8
+ import {Metrics} from "./metrics.js";
9
+ import {MetaDataRepository} from "./repositories/metaDataRepository.js";
10
+ import {AttestationService} from "./services/attestation.js";
11
+ import {BlockProposingService} from "./services/block.js";
12
+ import {ChainHeaderTracker} from "./services/chainHeaderTracker.js";
13
+ import {DoppelgangerService} from "./services/doppelgangerService.js";
14
+ import {ValidatorEventEmitter} from "./services/emitter.js";
15
+ import {ExternalSignerOptions, pollExternalSignerPubkeys} from "./services/externalSignerSync.js";
16
+ import {IndicesService} from "./services/indices.js";
17
+ import {pollBuilderValidatorRegistration, pollPrepareBeaconProposer} from "./services/prepareBeaconProposer.js";
18
+ import {SyncCommitteeService} from "./services/syncCommittee.js";
19
+ import {SyncingStatusTracker} from "./services/syncingStatusTracker.js";
20
+ import {Signer, ValidatorProposerConfig, ValidatorStore, defaultOptions} from "./services/validatorStore.js";
21
+ import {ISlashingProtection, Interchange, InterchangeFormatVersion} from "./slashingProtection/index.js";
22
+ import {LodestarValidatorDatabaseController, ProcessShutdownCallback, PubkeyHex} from "./types.js";
23
+ import {Clock, IClock} from "./util/clock.js";
24
+ import {NotEqualParamsError, assertEqualParams, getLoggerVc} from "./util/index.js";
25
+
26
+ export type ValidatorModules = {
27
+ opts: ValidatorOptions;
28
+ genesis: Genesis;
29
+ validatorStore: ValidatorStore;
30
+ slashingProtection: ISlashingProtection;
31
+ blockProposingService: BlockProposingService;
32
+ attestationService: AttestationService;
33
+ syncCommitteeService: SyncCommitteeService;
34
+ config: BeaconConfig;
35
+ api: ApiClient;
36
+ clock: IClock;
37
+ chainHeaderTracker: ChainHeaderTracker;
38
+ syncingStatusTracker: SyncingStatusTracker;
39
+ logger: Logger;
40
+ db: LodestarValidatorDatabaseController;
41
+ metrics: Metrics | null;
42
+ controller: AbortController;
43
+ };
44
+
45
+ export type ValidatorOptions = {
46
+ slashingProtection: ISlashingProtection;
47
+ db: LodestarValidatorDatabaseController;
48
+ config: ChainForkConfig;
49
+ api: {
50
+ clientOrUrls: ApiClient | string | string[];
51
+ globalInit?: ApiRequestInit;
52
+ };
53
+ signers: Signer[];
54
+ logger: Logger;
55
+ processShutdownCallback: ProcessShutdownCallback;
56
+ abortController: AbortController;
57
+ afterBlockDelaySlotFraction?: number;
58
+ scAfterBlockDelaySlotFraction?: number;
59
+ doppelgangerProtection?: boolean;
60
+ closed?: boolean;
61
+ valProposerConfig?: ValidatorProposerConfig;
62
+ distributed?: boolean;
63
+ broadcastValidation?: routes.beacon.BroadcastValidation;
64
+ blindedLocal?: boolean;
65
+ externalSigner?: ExternalSignerOptions;
66
+ };
67
+
68
+ // TODO: Extend the timeout, and let it be customizable
69
+ /// The global timeout for HTTP requests to the beacon node.
70
+ // const HTTP_TIMEOUT_MS = 12 * 1000;
71
+
72
+ enum Status {
73
+ running,
74
+ closed,
75
+ }
76
+
77
+ /**
78
+ * Main class for the Validator client.
79
+ */
80
+ export class Validator {
81
+ private readonly genesis: Genesis;
82
+ readonly validatorStore: ValidatorStore;
83
+ private readonly slashingProtection: ISlashingProtection;
84
+ private readonly blockProposingService: BlockProposingService;
85
+ private readonly attestationService: AttestationService;
86
+ private readonly syncCommitteeService: SyncCommitteeService;
87
+ private readonly config: BeaconConfig;
88
+ private readonly api: ApiClient;
89
+ private readonly clock: IClock;
90
+ private readonly chainHeaderTracker: ChainHeaderTracker;
91
+ readonly syncingStatusTracker: SyncingStatusTracker;
92
+ private readonly logger: Logger;
93
+ private readonly db: LodestarValidatorDatabaseController;
94
+ private state: Status;
95
+ private readonly controller: AbortController;
96
+
97
+ constructor({
98
+ opts,
99
+ genesis,
100
+ validatorStore,
101
+ slashingProtection,
102
+ blockProposingService,
103
+ attestationService,
104
+ syncCommitteeService,
105
+ config,
106
+ api,
107
+ clock,
108
+ chainHeaderTracker,
109
+ syncingStatusTracker,
110
+ logger,
111
+ db,
112
+ metrics,
113
+ controller,
114
+ }: ValidatorModules) {
115
+ this.genesis = genesis;
116
+ this.validatorStore = validatorStore;
117
+ this.slashingProtection = slashingProtection;
118
+ this.blockProposingService = blockProposingService;
119
+ this.attestationService = attestationService;
120
+ this.syncCommitteeService = syncCommitteeService;
121
+ this.config = config;
122
+ this.api = api;
123
+ this.clock = clock;
124
+ this.chainHeaderTracker = chainHeaderTracker;
125
+ this.syncingStatusTracker = syncingStatusTracker;
126
+ this.logger = logger;
127
+ this.controller = controller;
128
+ this.db = db;
129
+
130
+ if (opts.closed) {
131
+ this.state = Status.closed;
132
+ } else {
133
+ // Add notifier to warn user if primary node is unhealthy as there might
134
+ // not be any errors in the logs due to fallback nodes handling the requests
135
+ const {httpClient} = this.api;
136
+ if (httpClient.urlsInits.length > 1) {
137
+ const primaryNodeUrl = toPrintableUrl(httpClient.urlsInits[0].baseUrl);
138
+
139
+ this.clock.runEveryEpoch(async () => {
140
+ // Only emit warning if URL score is 0 to prevent false positives
141
+ // if just a single request fails which might happen due to other reasons
142
+ if (httpClient.urlsScore[0] === 0) {
143
+ this.logger.warn("Primary beacon node is unhealthy", {url: primaryNodeUrl});
144
+ }
145
+ });
146
+ }
147
+
148
+ if (metrics) {
149
+ this.db.setMetrics(metrics.db);
150
+ }
151
+
152
+ // "start" the validator
153
+ this.state = Status.running;
154
+ this.clock.start(this.controller.signal);
155
+ this.chainHeaderTracker.start(this.controller.signal);
156
+ }
157
+ }
158
+
159
+ get isRunning(): boolean {
160
+ return this.state === Status.running;
161
+ }
162
+
163
+ /**
164
+ * Initialize and start a validator client
165
+ */
166
+ static async init(opts: ValidatorOptions, genesis: Genesis, metrics: Metrics | null = null): Promise<Validator> {
167
+ const {db, config: chainConfig, logger, slashingProtection, signers, valProposerConfig} = opts;
168
+ const config = createBeaconConfig(chainConfig, genesis.genesisValidatorsRoot);
169
+ const controller = opts.abortController;
170
+ const clock = new Clock(config, logger, {genesisTime: Number(genesis.genesisTime)});
171
+ const loggerVc = getLoggerVc(logger, clock);
172
+
173
+ let api: ApiClient;
174
+ const {clientOrUrls, globalInit} = opts.api;
175
+ if (typeof clientOrUrls === "string" || Array.isArray(clientOrUrls)) {
176
+ api = getClient(
177
+ {
178
+ urls: typeof clientOrUrls === "string" ? [clientOrUrls] : clientOrUrls,
179
+ // Validator would need the beacon to respond within the slot
180
+ // See https://github.com/ChainSafe/lodestar/issues/5315 for rationale
181
+ globalInit: {timeoutMs: config.SLOT_DURATION_MS, signal: controller.signal, ...globalInit},
182
+ },
183
+ {config, logger, metrics: metrics?.restApiClient}
184
+ );
185
+ } else {
186
+ api = clientOrUrls;
187
+ }
188
+
189
+ const indicesService = new IndicesService(logger, api, metrics);
190
+
191
+ const doppelgangerService = opts.doppelgangerProtection
192
+ ? new DoppelgangerService(
193
+ logger,
194
+ clock,
195
+ api,
196
+ indicesService,
197
+ slashingProtection,
198
+ opts.processShutdownCallback,
199
+ metrics
200
+ )
201
+ : null;
202
+
203
+ const validatorStore = await ValidatorStore.init(
204
+ {
205
+ config,
206
+ slashingProtection,
207
+ indicesService,
208
+ doppelgangerService,
209
+ metrics,
210
+ },
211
+ signers,
212
+ valProposerConfig
213
+ );
214
+ pollPrepareBeaconProposer(config, loggerVc, api, clock, validatorStore, metrics);
215
+ pollBuilderValidatorRegistration(config, loggerVc, api, clock, validatorStore, metrics);
216
+ pollExternalSignerPubkeys(config, loggerVc, controller.signal, validatorStore, opts.externalSigner);
217
+
218
+ const emitter = new ValidatorEventEmitter();
219
+ // Validator event emitter can have more than 10 listeners in a normal course of operation
220
+ // We set infinity to prevent MaxListenersExceededWarning which get logged when listeners > 10
221
+ emitter.setMaxListeners(Infinity);
222
+
223
+ const chainHeaderTracker = new ChainHeaderTracker(logger, api, emitter);
224
+ const syncingStatusTracker = new SyncingStatusTracker(logger, api, clock, metrics);
225
+
226
+ const blockProposingService = new BlockProposingService(config, loggerVc, api, clock, validatorStore, metrics, {
227
+ broadcastValidation: opts.broadcastValidation ?? defaultOptions.broadcastValidation,
228
+ blindedLocal: opts.blindedLocal ?? defaultOptions.blindedLocal,
229
+ });
230
+
231
+ const attestationService = new AttestationService(
232
+ loggerVc,
233
+ api,
234
+ clock,
235
+ validatorStore,
236
+ emitter,
237
+ chainHeaderTracker,
238
+ syncingStatusTracker,
239
+ metrics,
240
+ config,
241
+ {
242
+ afterBlockDelaySlotFraction: opts.afterBlockDelaySlotFraction,
243
+ distributedAggregationSelection: opts.distributed,
244
+ }
245
+ );
246
+
247
+ const syncCommitteeService = new SyncCommitteeService(
248
+ config,
249
+ loggerVc,
250
+ api,
251
+ clock,
252
+ validatorStore,
253
+ emitter,
254
+ chainHeaderTracker,
255
+ syncingStatusTracker,
256
+ metrics,
257
+ {
258
+ scAfterBlockDelaySlotFraction: opts.scAfterBlockDelaySlotFraction,
259
+ distributedAggregationSelection: opts.distributed,
260
+ }
261
+ );
262
+
263
+ return new Validator({
264
+ opts,
265
+ genesis,
266
+ validatorStore,
267
+ slashingProtection,
268
+ blockProposingService,
269
+ attestationService,
270
+ syncCommitteeService,
271
+ config,
272
+ api,
273
+ clock,
274
+ chainHeaderTracker,
275
+ syncingStatusTracker,
276
+ logger,
277
+ db,
278
+ metrics,
279
+ controller,
280
+ });
281
+ }
282
+
283
+ /** Waits for genesis and genesis time */
284
+ static async initializeFromBeaconNode(opts: ValidatorOptions, metrics?: Metrics | null): Promise<Validator> {
285
+ const {logger, config} = opts;
286
+
287
+ let api: ApiClient;
288
+ const {clientOrUrls, globalInit} = opts.api;
289
+ if (typeof clientOrUrls === "string" || Array.isArray(clientOrUrls)) {
290
+ const urls = typeof clientOrUrls === "string" ? [clientOrUrls] : clientOrUrls;
291
+ // This new api instance can make do with default timeout as a faster timeout is
292
+ // not necessary since this instance won't be used for validator duties
293
+ api = getClient({urls, globalInit: {signal: opts.abortController.signal, ...globalInit}}, {config, logger});
294
+ logger.info("Beacon node", {
295
+ urls: urls.map(toPrintableUrl).toString(),
296
+ requestWireFormat: globalInit?.requestWireFormat ?? defaultInit.requestWireFormat,
297
+ responseWireFormat: globalInit?.responseWireFormat ?? defaultInit.responseWireFormat,
298
+ });
299
+ } else {
300
+ api = clientOrUrls;
301
+ }
302
+
303
+ const genesis = await waitForGenesis(api, opts.logger, opts.abortController.signal);
304
+ logger.info("Genesis fetched from the beacon node");
305
+
306
+ const res = await api.config.getSpec();
307
+ assertEqualParams(config, res.value());
308
+ logger.info("Verified connected beacon node and validator have same the config");
309
+
310
+ await assertEqualGenesis(opts, genesis);
311
+ logger.info("Verified connected beacon node and validator have the same genesisValidatorRoot");
312
+
313
+ const {broadcastValidation = defaultOptions.broadcastValidation, valProposerConfig} = opts;
314
+ const defaultBuilderSelection =
315
+ valProposerConfig?.defaultConfig.builder?.selection ?? defaultOptions.builderSelection;
316
+ const strictFeeRecipientCheck = valProposerConfig?.defaultConfig.strictFeeRecipientCheck ?? false;
317
+ const suggestedFeeRecipient = valProposerConfig?.defaultConfig.feeRecipient ?? defaultOptions.suggestedFeeRecipient;
318
+
319
+ logger.info("Initializing validator", {
320
+ broadcastValidation,
321
+ defaultBuilderSelection,
322
+ suggestedFeeRecipient,
323
+ strictFeeRecipientCheck,
324
+ });
325
+
326
+ metrics?.defaultConfiguration.set({builderSelection: defaultBuilderSelection, broadcastValidation}, 1);
327
+
328
+ // Instantiates block and attestation services and runs them once the chain has been started.
329
+ return Validator.init(opts, genesis, metrics);
330
+ }
331
+
332
+ removeDutiesForKey(pubkey: PubkeyHex): void {
333
+ this.blockProposingService.removeDutiesForKey(pubkey);
334
+ this.attestationService.removeDutiesForKey(pubkey);
335
+ this.syncCommitteeService.removeDutiesForKey(pubkey);
336
+ }
337
+
338
+ /**
339
+ * Stops all validator functions.
340
+ */
341
+ async close(): Promise<void> {
342
+ if (this.state === Status.closed) return;
343
+ this.controller.abort();
344
+ await this.db.close();
345
+ this.state = Status.closed;
346
+ }
347
+
348
+ async importInterchange(interchange: Interchange): Promise<void> {
349
+ return this.slashingProtection.importInterchange(interchange, this.genesis.genesisValidatorsRoot);
350
+ }
351
+
352
+ async exportInterchange(pubkeys: BLSPubkey[], formatVersion: InterchangeFormatVersion): Promise<Interchange> {
353
+ return this.slashingProtection.exportInterchange(this.genesis.genesisValidatorsRoot, pubkeys, formatVersion);
354
+ }
355
+
356
+ /**
357
+ * Perform a voluntary exit for the given validator by its key.
358
+ */
359
+ async voluntaryExit(publicKey: string, exitEpoch?: number): Promise<void> {
360
+ const signedVoluntaryExit = await this.signVoluntaryExit(publicKey, exitEpoch);
361
+
362
+ (await this.api.beacon.submitPoolVoluntaryExit({signedVoluntaryExit})).assertOk();
363
+
364
+ this.logger.info(`Submitted voluntary exit for ${publicKey} to the network`);
365
+ }
366
+
367
+ /**
368
+ * Create a signed voluntary exit message for the given validator by its key.
369
+ */
370
+ async signVoluntaryExit(publicKey: string, exitEpoch?: number): Promise<phase0.SignedVoluntaryExit> {
371
+ const validators = (
372
+ await this.api.beacon.postStateValidators({stateId: "head", validatorIds: [publicKey]})
373
+ ).value();
374
+
375
+ const validator = validators[0];
376
+ if (validator === undefined) {
377
+ throw new Error(`Validator pubkey ${publicKey} not found in state`);
378
+ }
379
+
380
+ if (exitEpoch === undefined) {
381
+ exitEpoch = computeEpochAtSlot(getCurrentSlot(this.config, this.clock.genesisTime));
382
+ }
383
+
384
+ return this.validatorStore.signVoluntaryExit(publicKey, validator.index, exitEpoch);
385
+ }
386
+ }
387
+
388
+ /** Assert the same genesisValidatorRoot and genesisTime */
389
+ async function assertEqualGenesis(opts: ValidatorOptions, genesis: Genesis): Promise<void> {
390
+ const nodeGenesisValidatorRoot = genesis.genesisValidatorsRoot;
391
+ const metaDataRepository = new MetaDataRepository(opts.db);
392
+ const genesisValidatorsRoot = await metaDataRepository.getGenesisValidatorsRoot();
393
+ if (genesisValidatorsRoot) {
394
+ if (!ssz.Root.equals(genesisValidatorsRoot, nodeGenesisValidatorRoot)) {
395
+ // this happens when the existing validator db served another network before
396
+ opts.logger.error("Not the same genesisValidatorRoot", {
397
+ expected: toRootHex(nodeGenesisValidatorRoot),
398
+ actual: toRootHex(genesisValidatorsRoot),
399
+ });
400
+ throw new NotEqualParamsError("Not the same genesisValidatorRoot");
401
+ }
402
+ } else {
403
+ await metaDataRepository.setGenesisValidatorsRoot(nodeGenesisValidatorRoot);
404
+ opts.logger.info("Persisted genesisValidatorRoot", toRootHex(nodeGenesisValidatorRoot));
405
+ }
406
+
407
+ const nodeGenesisTime = genesis.genesisTime;
408
+ const genesisTime = await metaDataRepository.getGenesisTime();
409
+ if (genesisTime !== null) {
410
+ if (genesisTime !== nodeGenesisTime) {
411
+ opts.logger.error("Not the same genesisTime", {expected: nodeGenesisTime, actual: genesisTime});
412
+ throw new NotEqualParamsError("Not the same genesisTime");
413
+ }
414
+ } else {
415
+ await metaDataRepository.setGenesisTime(nodeGenesisTime);
416
+ opts.logger.info("Persisted genesisTime", nodeGenesisTime);
417
+ }
418
+ }