@lodestar/validator 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 (135) 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.map +1 -0
  5. package/lib/metrics.d.ts.map +1 -0
  6. package/lib/metrics.js +14 -14
  7. package/lib/metrics.js.map +1 -1
  8. package/lib/repositories/index.d.ts.map +1 -0
  9. package/lib/repositories/metaDataRepository.d.ts.map +1 -0
  10. package/lib/services/attestation.d.ts.map +1 -0
  11. package/lib/services/attestation.js +35 -27
  12. package/lib/services/attestation.js.map +1 -1
  13. package/lib/services/attestationDuties.d.ts.map +1 -0
  14. package/lib/services/attestationDuties.js +1 -0
  15. package/lib/services/attestationDuties.js.map +1 -1
  16. package/lib/services/block.d.ts.map +1 -0
  17. package/lib/services/blockDuties.d.ts +2 -2
  18. package/lib/services/blockDuties.d.ts.map +1 -0
  19. package/lib/services/blockDuties.js +4 -3
  20. package/lib/services/blockDuties.js.map +1 -1
  21. package/lib/services/chainHeaderTracker.d.ts.map +1 -0
  22. package/lib/services/doppelgangerService.d.ts.map +1 -0
  23. package/lib/services/emitter.d.ts.map +1 -0
  24. package/lib/services/externalSignerSync.d.ts.map +1 -0
  25. package/lib/services/externalSignerSync.js +1 -1
  26. package/lib/services/externalSignerSync.js.map +1 -1
  27. package/lib/services/indices.d.ts.map +1 -0
  28. package/lib/services/prepareBeaconProposer.d.ts.map +1 -0
  29. package/lib/services/syncCommittee.d.ts.map +1 -0
  30. package/lib/services/syncCommittee.js +30 -22
  31. package/lib/services/syncCommittee.js.map +1 -1
  32. package/lib/services/syncCommitteeDuties.d.ts.map +1 -0
  33. package/lib/services/syncingStatusTracker.d.ts.map +1 -0
  34. package/lib/services/utils.d.ts.map +1 -0
  35. package/lib/services/validatorStore.d.ts.map +1 -0
  36. package/lib/slashingProtection/attestation/attestationByTargetRepository.d.ts.map +1 -0
  37. package/lib/slashingProtection/attestation/attestationLowerBoundRepository.d.ts.map +1 -0
  38. package/lib/slashingProtection/attestation/errors.d.ts.map +1 -0
  39. package/lib/slashingProtection/attestation/index.d.ts.map +1 -0
  40. package/lib/slashingProtection/block/blockBySlotRepository.d.ts.map +1 -0
  41. package/lib/slashingProtection/block/errors.d.ts.map +1 -0
  42. package/lib/slashingProtection/block/index.d.ts.map +1 -0
  43. package/lib/slashingProtection/index.d.ts.map +1 -0
  44. package/lib/slashingProtection/interchange/errors.d.ts.map +1 -0
  45. package/lib/slashingProtection/interchange/formats/completeV4.d.ts.map +1 -0
  46. package/lib/slashingProtection/interchange/formats/index.d.ts.map +1 -0
  47. package/lib/slashingProtection/interchange/formats/v5.d.ts.map +1 -0
  48. package/lib/slashingProtection/interchange/index.d.ts.map +1 -0
  49. package/lib/slashingProtection/interchange/parseInterchange.d.ts.map +1 -0
  50. package/lib/slashingProtection/interchange/serializeInterchange.d.ts.map +1 -0
  51. package/lib/slashingProtection/interchange/types.d.ts.map +1 -0
  52. package/lib/slashingProtection/interface.d.ts.map +1 -0
  53. package/lib/slashingProtection/minMaxSurround/distanceStoreRepository.d.ts.map +1 -0
  54. package/lib/slashingProtection/minMaxSurround/errors.d.ts.map +1 -0
  55. package/lib/slashingProtection/minMaxSurround/index.d.ts.map +1 -0
  56. package/lib/slashingProtection/minMaxSurround/interface.d.ts.map +1 -0
  57. package/lib/slashingProtection/minMaxSurround/minMaxSurround.d.ts.map +1 -0
  58. package/lib/slashingProtection/types.d.ts.map +1 -0
  59. package/lib/slashingProtection/utils.d.ts.map +1 -0
  60. package/lib/types.d.ts.map +1 -0
  61. package/lib/util/batch.d.ts.map +1 -0
  62. package/lib/util/clock.d.ts +3 -0
  63. package/lib/util/clock.d.ts.map +1 -0
  64. package/lib/util/clock.js +11 -8
  65. package/lib/util/clock.js.map +1 -1
  66. package/lib/util/difference.d.ts.map +1 -0
  67. package/lib/util/externalSignerClient.d.ts.map +1 -0
  68. package/lib/util/format.d.ts.map +1 -0
  69. package/lib/util/index.d.ts.map +1 -0
  70. package/lib/util/logger.d.ts.map +1 -0
  71. package/lib/util/params.d.ts.map +1 -0
  72. package/lib/util/params.js +8 -1
  73. package/lib/util/params.js.map +1 -1
  74. package/lib/util/url.d.ts.map +1 -0
  75. package/lib/validator.d.ts.map +1 -0
  76. package/lib/validator.js +1 -1
  77. package/lib/validator.js.map +1 -1
  78. package/package.json +19 -16
  79. package/src/buckets.ts +30 -0
  80. package/src/defaults.ts +8 -0
  81. package/src/genesis.ts +19 -0
  82. package/src/index.ts +22 -0
  83. package/src/metrics.ts +417 -0
  84. package/src/repositories/index.ts +1 -0
  85. package/src/repositories/metaDataRepository.ts +42 -0
  86. package/src/services/attestation.ts +362 -0
  87. package/src/services/attestationDuties.ts +406 -0
  88. package/src/services/block.ts +261 -0
  89. package/src/services/blockDuties.ts +217 -0
  90. package/src/services/chainHeaderTracker.ts +89 -0
  91. package/src/services/doppelgangerService.ts +286 -0
  92. package/src/services/emitter.ts +43 -0
  93. package/src/services/externalSignerSync.ts +81 -0
  94. package/src/services/indices.ts +165 -0
  95. package/src/services/prepareBeaconProposer.ts +119 -0
  96. package/src/services/syncCommittee.ts +338 -0
  97. package/src/services/syncCommitteeDuties.ts +337 -0
  98. package/src/services/syncingStatusTracker.ts +74 -0
  99. package/src/services/utils.ts +58 -0
  100. package/src/services/validatorStore.ts +830 -0
  101. package/src/slashingProtection/attestation/attestationByTargetRepository.ts +77 -0
  102. package/src/slashingProtection/attestation/attestationLowerBoundRepository.ts +44 -0
  103. package/src/slashingProtection/attestation/errors.ts +66 -0
  104. package/src/slashingProtection/attestation/index.ts +171 -0
  105. package/src/slashingProtection/block/blockBySlotRepository.ts +78 -0
  106. package/src/slashingProtection/block/errors.ts +28 -0
  107. package/src/slashingProtection/block/index.ts +94 -0
  108. package/src/slashingProtection/index.ts +95 -0
  109. package/src/slashingProtection/interchange/errors.ts +15 -0
  110. package/src/slashingProtection/interchange/formats/completeV4.ts +125 -0
  111. package/src/slashingProtection/interchange/formats/index.ts +7 -0
  112. package/src/slashingProtection/interchange/formats/v5.ts +120 -0
  113. package/src/slashingProtection/interchange/index.ts +5 -0
  114. package/src/slashingProtection/interchange/parseInterchange.ts +55 -0
  115. package/src/slashingProtection/interchange/serializeInterchange.ts +35 -0
  116. package/src/slashingProtection/interchange/types.ts +18 -0
  117. package/src/slashingProtection/interface.ts +28 -0
  118. package/src/slashingProtection/minMaxSurround/distanceStoreRepository.ts +57 -0
  119. package/src/slashingProtection/minMaxSurround/errors.ts +27 -0
  120. package/src/slashingProtection/minMaxSurround/index.ts +4 -0
  121. package/src/slashingProtection/minMaxSurround/interface.ts +23 -0
  122. package/src/slashingProtection/minMaxSurround/minMaxSurround.ts +104 -0
  123. package/src/slashingProtection/types.ts +12 -0
  124. package/src/slashingProtection/utils.ts +42 -0
  125. package/src/types.ts +31 -0
  126. package/src/util/batch.ts +15 -0
  127. package/src/util/clock.ts +169 -0
  128. package/src/util/difference.ts +10 -0
  129. package/src/util/externalSignerClient.ts +277 -0
  130. package/src/util/format.ts +3 -0
  131. package/src/util/index.ts +6 -0
  132. package/src/util/logger.ts +51 -0
  133. package/src/util/params.ts +320 -0
  134. package/src/util/url.ts +16 -0
  135. package/src/validator.ts +418 -0
@@ -0,0 +1,81 @@
1
+ import {PublicKey} from "@chainsafe/blst";
2
+ import {ChainForkConfig} from "@lodestar/config";
3
+ import {SLOTS_PER_EPOCH} from "@lodestar/params";
4
+ import {fromHex, toPrintableUrl} from "@lodestar/utils";
5
+ import {externalSignerGetKeys} from "../util/externalSignerClient.js";
6
+ import {LoggerVc} from "../util/index.js";
7
+ import {SignerType, ValidatorStore} from "./validatorStore.js";
8
+
9
+ export type ExternalSignerOptions = {
10
+ url?: string;
11
+ fetch?: boolean;
12
+ fetchInterval?: number;
13
+ };
14
+
15
+ /**
16
+ * This service is responsible for keeping the keys managed by the connected
17
+ * external signer and the validator client in sync by adding newly discovered keys
18
+ * and removing no longer present keys on external signer from the validator store.
19
+ */
20
+ export function pollExternalSignerPubkeys(
21
+ config: ChainForkConfig,
22
+ logger: LoggerVc,
23
+ signal: AbortSignal,
24
+ validatorStore: ValidatorStore,
25
+ opts?: ExternalSignerOptions
26
+ ): void {
27
+ const externalSigner = opts ?? {};
28
+
29
+ if (!externalSigner.url || !externalSigner.fetch) {
30
+ return; // Disabled
31
+ }
32
+
33
+ async function fetchExternalSignerPubkeys(): Promise<void> {
34
+ // External signer URL is already validated earlier
35
+ const externalSignerUrl = externalSigner.url as string;
36
+ const printableUrl = toPrintableUrl(externalSignerUrl);
37
+
38
+ try {
39
+ logger.debug("Fetching public keys from external signer", {url: printableUrl});
40
+ const externalPubkeys = await externalSignerGetKeys(externalSignerUrl);
41
+ assertValidPubkeysHex(externalPubkeys);
42
+ logger.debug("Received public keys from external signer", {url: printableUrl, count: externalPubkeys.length});
43
+
44
+ const localPubkeys = validatorStore.getRemoteSignerPubkeys(externalSignerUrl);
45
+ logger.debug("Local public keys stored for external signer", {url: printableUrl, count: localPubkeys.length});
46
+
47
+ const localPubkeysSet = new Set(localPubkeys);
48
+ for (const pubkey of externalPubkeys) {
49
+ if (!localPubkeysSet.has(pubkey)) {
50
+ await validatorStore.addSigner({type: SignerType.Remote, pubkey, url: externalSignerUrl});
51
+ logger.info("Added remote signer", {pubkey, url: printableUrl});
52
+ }
53
+ }
54
+
55
+ const externalPubkeysSet = new Set(externalPubkeys);
56
+ for (const pubkey of localPubkeys) {
57
+ if (!externalPubkeysSet.has(pubkey)) {
58
+ validatorStore.removeSigner(pubkey);
59
+ logger.info("Removed remote signer", {pubkey, url: printableUrl});
60
+ }
61
+ }
62
+ } catch (e) {
63
+ logger.error("Failed to fetch public keys from external signer", {url: printableUrl}, e as Error);
64
+ }
65
+ }
66
+
67
+ const interval = setInterval(
68
+ fetchExternalSignerPubkeys,
69
+ externalSigner.fetchInterval ??
70
+ // Once per epoch by default
71
+ SLOTS_PER_EPOCH * config.SLOT_DURATION_MS
72
+ );
73
+ signal.addEventListener("abort", () => clearInterval(interval), {once: true});
74
+ }
75
+
76
+ function assertValidPubkeysHex(pubkeysHex: string[]): void {
77
+ for (const pubkeyHex of pubkeysHex) {
78
+ const pubkeyBytes = fromHex(pubkeyHex);
79
+ PublicKey.fromBytes(pubkeyBytes, true);
80
+ }
81
+ }
@@ -0,0 +1,165 @@
1
+ import {ApiClient, routes} from "@lodestar/api";
2
+ import {ValidatorIndex} from "@lodestar/types";
3
+ import {Logger, MapDef, toPubkeyHex} from "@lodestar/utils";
4
+ import {Metrics} from "../metrics.js";
5
+ import {batchItems} from "../util/index.js";
6
+
7
+ /**
8
+ * This is to prevent the "Request body is too large" issue for http post.
9
+ * Typical servers accept up to 1MB (2 ** 20 bytes) of request body, for example fastify and nginx.
10
+ * A hex encoded public key with "0x"-prefix has a size of 98 bytes + 2 bytes to account for commas
11
+ * and other JSON padding. `Math.floor(2 ** 20 / 100) == 10485`, we can send up to ~10k keys per request.
12
+ */
13
+ const PUBKEYS_PER_REQUEST = 10_000;
14
+
15
+ // To assist with readability
16
+ type PubkeyHex = string;
17
+
18
+ // To assist with logging statuses, we only log the statuses that are not active_exiting or withdrawal_possible
19
+ type SimpleValidatorStatus = "pending" | "active" | "exited" | "withdrawn";
20
+
21
+ const statusToSimpleStatusMapping = (status: routes.beacon.ValidatorStatus): SimpleValidatorStatus => {
22
+ switch (status) {
23
+ case "active_exiting":
24
+ case "active_slashed":
25
+ case "active_ongoing":
26
+ return "active";
27
+
28
+ case "withdrawal_possible":
29
+ case "exited_slashed":
30
+ case "exited_unslashed":
31
+ return "exited";
32
+
33
+ case "pending_initialized":
34
+ case "pending_queued":
35
+ return "pending";
36
+
37
+ case "withdrawal_done":
38
+ return "withdrawn";
39
+ }
40
+ };
41
+
42
+ export class IndicesService {
43
+ readonly index2pubkey = new Map<ValidatorIndex, PubkeyHex>();
44
+ /** Indexed by pubkey in hex 0x prefixed */
45
+ readonly pubkey2index = new Map<PubkeyHex, ValidatorIndex>();
46
+ // Request indices once
47
+ private pollValidatorIndicesPromise: Promise<ValidatorIndex[]> | null = null;
48
+
49
+ constructor(
50
+ private readonly logger: Logger,
51
+ private readonly api: ApiClient,
52
+ private readonly metrics: Metrics | null
53
+ ) {
54
+ if (metrics) {
55
+ metrics.indices.addCollect(() => metrics.indices.set(this.index2pubkey.size));
56
+ }
57
+ }
58
+
59
+ get indexCount(): number {
60
+ return this.index2pubkey.size;
61
+ }
62
+
63
+ /** Returns the validator index for a given validator pubkey */
64
+ getValidatorIndex(pubKey: PubkeyHex): ValidatorIndex | undefined {
65
+ return this.pubkey2index.get(pubKey);
66
+ }
67
+
68
+ /** Return all known indices from the validatorStore pubkeys */
69
+ getAllLocalIndices(): ValidatorIndex[] {
70
+ return Array.from(this.index2pubkey.keys());
71
+ }
72
+
73
+ /** Return true if `index` is active part of this validator client */
74
+ hasValidatorIndex(index: ValidatorIndex): boolean {
75
+ return this.index2pubkey.has(index);
76
+ }
77
+
78
+ async pollValidatorIndices(pubkeysHex: PubkeyHex[]): Promise<ValidatorIndex[]> {
79
+ // Ensures pollValidatorIndicesInternal() is not called more than once at the same time.
80
+ // AttestationDutiesService, SyncCommitteeDutiesService and DoppelgangerService will call this function at the same time, so this will
81
+ // cache the promise and return it to the second caller, preventing calling the API twice for the same data.
82
+ if (this.pollValidatorIndicesPromise) {
83
+ return this.pollValidatorIndicesPromise;
84
+ }
85
+
86
+ this.pollValidatorIndicesPromise = this.pollValidatorIndicesInternal(pubkeysHex).finally(() => {
87
+ // Once the pollValidatorIndicesInternal() resolves or rejects null the cached promise so it can be called again.
88
+ this.pollValidatorIndicesPromise = null;
89
+ });
90
+ return this.pollValidatorIndicesPromise;
91
+ }
92
+
93
+ removeForKey(pubkey: PubkeyHex): boolean {
94
+ for (const [index, value] of this.index2pubkey) {
95
+ if (value === pubkey) {
96
+ this.index2pubkey.delete(index);
97
+ }
98
+ }
99
+ return this.pubkey2index.delete(pubkey);
100
+ }
101
+
102
+ /** Iterate through all the voting pubkeys in the `ValidatorStore` and attempt to learn any unknown
103
+ validator indices. Returns the new discovered indexes */
104
+ private async pollValidatorIndicesInternal(pubkeysHex: PubkeyHex[]): Promise<ValidatorIndex[]> {
105
+ const pubkeysHexToDiscover = pubkeysHex.filter((pubkey) => !this.pubkey2index.has(pubkey));
106
+
107
+ if (pubkeysHexToDiscover.length === 0) {
108
+ return [];
109
+ }
110
+
111
+ // Query the remote BN to resolve a pubkey to a validator index.
112
+ // support up to 10k pubkeys per poll
113
+ const pubkeysHexBatches = batchItems(pubkeysHexToDiscover, {batchSize: PUBKEYS_PER_REQUEST});
114
+
115
+ const newIndices: number[] = [];
116
+ for (const pubkeysHexBatch of pubkeysHexBatches) {
117
+ const validatorIndicesArr = await this.fetchValidatorIndices(pubkeysHexBatch);
118
+ newIndices.push(...validatorIndicesArr);
119
+ }
120
+
121
+ this.metrics?.discoveredIndices.inc(newIndices.length);
122
+
123
+ return newIndices;
124
+ }
125
+
126
+ private async fetchValidatorIndices(pubkeysHex: string[]): Promise<ValidatorIndex[]> {
127
+ const validators = (await this.api.beacon.postStateValidators({stateId: "head", validatorIds: pubkeysHex})).value();
128
+
129
+ const newIndices = [];
130
+
131
+ const allValidatorStatuses = new MapDef<SimpleValidatorStatus, number>(() => 0);
132
+
133
+ for (const validator of validators) {
134
+ // Group all validators by status
135
+ const status = statusToSimpleStatusMapping(validator.status);
136
+ allValidatorStatuses.set(status, allValidatorStatuses.getOrDefault(status) + 1);
137
+
138
+ const pubkeyHex = toPubkeyHex(validator.validator.pubkey);
139
+ if (!this.pubkey2index.has(pubkeyHex)) {
140
+ this.logger.info("Validator seen on beacon chain", {
141
+ validatorIndex: validator.index,
142
+ pubKey: pubkeyHex,
143
+ });
144
+ this.pubkey2index.set(pubkeyHex, validator.index);
145
+ this.index2pubkey.set(validator.index, pubkeyHex);
146
+ newIndices.push(validator.index);
147
+ }
148
+ }
149
+
150
+ // The number of validators that are not in the beacon chain
151
+ const pendingCount = pubkeysHex.length - validators.length;
152
+
153
+ allValidatorStatuses.set("pending", allValidatorStatuses.getOrDefault("pending") + pendingCount);
154
+
155
+ // Retrieve the number of validators for each status
156
+ const statuses = Object.fromEntries(Array.from(allValidatorStatuses.entries()).filter((entry) => entry[1] > 0));
157
+
158
+ // The total number of validators
159
+ const total = pubkeysHex.length;
160
+
161
+ this.logger.info("Validator statuses", {...statuses, total});
162
+
163
+ return newIndices;
164
+ }
165
+ }
@@ -0,0 +1,119 @@
1
+ import {ApiClient, routes} from "@lodestar/api";
2
+ import {BeaconConfig} from "@lodestar/config";
3
+ import {GENESIS_EPOCH, SLOTS_PER_EPOCH} from "@lodestar/params";
4
+ import {Epoch, bellatrix} from "@lodestar/types";
5
+ import {Metrics} from "../metrics.js";
6
+ import {IClock, LoggerVc, batchItems} from "../util/index.js";
7
+ import {ValidatorStore} from "./validatorStore.js";
8
+
9
+ const REGISTRATION_CHUNK_SIZE = 512;
10
+ /**
11
+ * This service is responsible for registering validators to beacon node with the
12
+ * proposer data (currently `feeRecipient`) so that it can issue advance fcUs to
13
+ * the engine for building execution payload with transactions.
14
+ *
15
+ * This needs to be done every epoch because the BN will cache it at most for
16
+ * two epochs.
17
+ */
18
+ export function pollPrepareBeaconProposer(
19
+ config: BeaconConfig,
20
+ logger: LoggerVc,
21
+ api: ApiClient,
22
+ clock: IClock,
23
+ validatorStore: ValidatorStore,
24
+ _metrics: Metrics | null
25
+ ): void {
26
+ async function prepareBeaconProposer(epoch: Epoch): Promise<void> {
27
+ // Before bellatrix we don't need to update this data on bn/builder
28
+ if (epoch < config.BELLATRIX_FORK_EPOCH - 1) return;
29
+
30
+ // prepareBeaconProposer is not as time sensitive as attesting.
31
+ // Poll indices first, then call api.validator.prepareBeaconProposer once
32
+ await validatorStore.pollValidatorIndices().catch((e: Error) => {
33
+ logger.error("Error on pollValidatorIndices for prepareBeaconProposer", {epoch}, e);
34
+ });
35
+
36
+ const indicesChunks = batchItems(validatorStore.getAllLocalIndices(), {batchSize: REGISTRATION_CHUNK_SIZE});
37
+
38
+ for (const indices of indicesChunks) {
39
+ try {
40
+ const proposers = indices.map(
41
+ (index): routes.validator.ProposerPreparationData => ({
42
+ validatorIndex: index,
43
+ feeRecipient: validatorStore.getFeeRecipientByIndex(index),
44
+ })
45
+ );
46
+ (await api.validator.prepareBeaconProposer({proposers})).assertOk();
47
+ logger.debug("Registered proposers with beacon node", {epoch, count: proposers.length});
48
+ } catch (e) {
49
+ logger.error("Failed to register proposers with beacon node", {epoch}, e as Error);
50
+ }
51
+ }
52
+ }
53
+
54
+ clock.runEveryEpoch(prepareBeaconProposer);
55
+ }
56
+
57
+ /**
58
+ * This service is responsible for registering validators with the mev builder as they
59
+ * might prepare and keep ready the execution payloads of just registered validators.
60
+ *
61
+ * This needs to be done every epoch because the builder(s) will cache it at most for
62
+ * two epochs.
63
+ */
64
+ export function pollBuilderValidatorRegistration(
65
+ config: BeaconConfig,
66
+ logger: LoggerVc,
67
+ api: ApiClient,
68
+ clock: IClock,
69
+ validatorStore: ValidatorStore,
70
+ _metrics: Metrics | null
71
+ ): void {
72
+ async function registerValidator(epoch: Epoch): Promise<void> {
73
+ // Don't send validator registrations pre-genesis as mev-boost-relay will reject
74
+ // those registrations anyways if timestamp is before genesis time and we wanna
75
+ // avoid caching and re-sending them in subsequent requests
76
+ if (epoch < GENESIS_EPOCH) return;
77
+
78
+ // Before bellatrix we don't need to update this data on bn/builder
79
+ if (epoch < config.BELLATRIX_FORK_EPOCH - 1) return;
80
+ const slot = epoch * SLOTS_PER_EPOCH;
81
+
82
+ // registerValidator is not as time sensitive as attesting.
83
+ // Poll indices first, then call api.validator.registerValidator once
84
+ await validatorStore.pollValidatorIndices().catch((e: Error) => {
85
+ logger.error("Error on pollValidatorIndices for registerValidator", {epoch}, e);
86
+ });
87
+ const pubkeyHexes = validatorStore
88
+ .getAllLocalIndices()
89
+ .map((index) => validatorStore.getPubkeyOfIndex(index))
90
+ .filter(
91
+ (pubkeyHex): pubkeyHex is string =>
92
+ pubkeyHex !== undefined &&
93
+ validatorStore.getBuilderSelectionParams(pubkeyHex).selection !==
94
+ routes.validator.BuilderSelection.ExecutionOnly
95
+ );
96
+
97
+ if (pubkeyHexes.length > 0) {
98
+ const pubkeyHexesChunks = batchItems(pubkeyHexes, {batchSize: REGISTRATION_CHUNK_SIZE});
99
+
100
+ for (const pubkeyHexes of pubkeyHexesChunks) {
101
+ try {
102
+ const registrations = await Promise.all(
103
+ pubkeyHexes.map((pubkeyHex): Promise<bellatrix.SignedValidatorRegistrationV1> => {
104
+ const feeRecipient = validatorStore.getFeeRecipient(pubkeyHex);
105
+ const gasLimit = validatorStore.getGasLimit(pubkeyHex);
106
+ return validatorStore.getValidatorRegistration(pubkeyHex, {feeRecipient, gasLimit}, slot);
107
+ })
108
+ );
109
+ (await api.validator.registerValidator({registrations})).assertOk();
110
+ logger.info("Published validator registrations to builder", {epoch, count: registrations.length});
111
+ } catch (e) {
112
+ logger.error("Failed to publish validator registrations to builder", {epoch}, e as Error);
113
+ }
114
+ }
115
+ }
116
+ }
117
+
118
+ clock.runEveryEpoch(registerValidator);
119
+ }