@lodestar/validator 1.35.0-dev.8689cc3545 → 1.35.0-dev.8b45b1e978

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 (115) hide show
  1. package/package.json +15 -13
  2. package/lib/buckets.d.ts.map +0 -1
  3. package/lib/defaults.d.ts.map +0 -1
  4. package/lib/genesis.d.ts.map +0 -1
  5. package/lib/index.d.ts.map +0 -1
  6. package/lib/metrics.d.ts.map +0 -1
  7. package/lib/repositories/index.d.ts.map +0 -1
  8. package/lib/repositories/metaDataRepository.d.ts.map +0 -1
  9. package/lib/services/attestation.d.ts.map +0 -1
  10. package/lib/services/attestationDuties.d.ts.map +0 -1
  11. package/lib/services/block.d.ts.map +0 -1
  12. package/lib/services/blockDuties.d.ts.map +0 -1
  13. package/lib/services/chainHeaderTracker.d.ts.map +0 -1
  14. package/lib/services/doppelgangerService.d.ts.map +0 -1
  15. package/lib/services/emitter.d.ts.map +0 -1
  16. package/lib/services/externalSignerSync.d.ts.map +0 -1
  17. package/lib/services/indices.d.ts.map +0 -1
  18. package/lib/services/prepareBeaconProposer.d.ts.map +0 -1
  19. package/lib/services/syncCommittee.d.ts.map +0 -1
  20. package/lib/services/syncCommitteeDuties.d.ts.map +0 -1
  21. package/lib/services/syncingStatusTracker.d.ts.map +0 -1
  22. package/lib/services/utils.d.ts.map +0 -1
  23. package/lib/services/validatorStore.d.ts.map +0 -1
  24. package/lib/slashingProtection/attestation/attestationByTargetRepository.d.ts.map +0 -1
  25. package/lib/slashingProtection/attestation/attestationLowerBoundRepository.d.ts.map +0 -1
  26. package/lib/slashingProtection/attestation/errors.d.ts.map +0 -1
  27. package/lib/slashingProtection/attestation/index.d.ts.map +0 -1
  28. package/lib/slashingProtection/block/blockBySlotRepository.d.ts.map +0 -1
  29. package/lib/slashingProtection/block/errors.d.ts.map +0 -1
  30. package/lib/slashingProtection/block/index.d.ts.map +0 -1
  31. package/lib/slashingProtection/index.d.ts.map +0 -1
  32. package/lib/slashingProtection/interchange/errors.d.ts.map +0 -1
  33. package/lib/slashingProtection/interchange/formats/completeV4.d.ts.map +0 -1
  34. package/lib/slashingProtection/interchange/formats/index.d.ts.map +0 -1
  35. package/lib/slashingProtection/interchange/formats/v5.d.ts.map +0 -1
  36. package/lib/slashingProtection/interchange/index.d.ts.map +0 -1
  37. package/lib/slashingProtection/interchange/parseInterchange.d.ts.map +0 -1
  38. package/lib/slashingProtection/interchange/serializeInterchange.d.ts.map +0 -1
  39. package/lib/slashingProtection/interchange/types.d.ts.map +0 -1
  40. package/lib/slashingProtection/interface.d.ts.map +0 -1
  41. package/lib/slashingProtection/minMaxSurround/distanceStoreRepository.d.ts.map +0 -1
  42. package/lib/slashingProtection/minMaxSurround/errors.d.ts.map +0 -1
  43. package/lib/slashingProtection/minMaxSurround/index.d.ts.map +0 -1
  44. package/lib/slashingProtection/minMaxSurround/interface.d.ts.map +0 -1
  45. package/lib/slashingProtection/minMaxSurround/minMaxSurround.d.ts.map +0 -1
  46. package/lib/slashingProtection/types.d.ts.map +0 -1
  47. package/lib/slashingProtection/utils.d.ts.map +0 -1
  48. package/lib/types.d.ts.map +0 -1
  49. package/lib/util/batch.d.ts.map +0 -1
  50. package/lib/util/clock.d.ts.map +0 -1
  51. package/lib/util/difference.d.ts.map +0 -1
  52. package/lib/util/externalSignerClient.d.ts.map +0 -1
  53. package/lib/util/format.d.ts.map +0 -1
  54. package/lib/util/index.d.ts.map +0 -1
  55. package/lib/util/logger.d.ts.map +0 -1
  56. package/lib/util/params.d.ts.map +0 -1
  57. package/lib/util/url.d.ts.map +0 -1
  58. package/lib/validator.d.ts.map +0 -1
  59. package/src/buckets.ts +0 -30
  60. package/src/defaults.ts +0 -8
  61. package/src/genesis.ts +0 -19
  62. package/src/index.ts +0 -22
  63. package/src/metrics.ts +0 -417
  64. package/src/repositories/index.ts +0 -1
  65. package/src/repositories/metaDataRepository.ts +0 -42
  66. package/src/services/attestation.ts +0 -349
  67. package/src/services/attestationDuties.ts +0 -405
  68. package/src/services/block.ts +0 -261
  69. package/src/services/blockDuties.ts +0 -215
  70. package/src/services/chainHeaderTracker.ts +0 -89
  71. package/src/services/doppelgangerService.ts +0 -286
  72. package/src/services/emitter.ts +0 -43
  73. package/src/services/externalSignerSync.ts +0 -81
  74. package/src/services/indices.ts +0 -165
  75. package/src/services/prepareBeaconProposer.ts +0 -119
  76. package/src/services/syncCommittee.ts +0 -317
  77. package/src/services/syncCommitteeDuties.ts +0 -337
  78. package/src/services/syncingStatusTracker.ts +0 -74
  79. package/src/services/utils.ts +0 -58
  80. package/src/services/validatorStore.ts +0 -830
  81. package/src/slashingProtection/attestation/attestationByTargetRepository.ts +0 -77
  82. package/src/slashingProtection/attestation/attestationLowerBoundRepository.ts +0 -44
  83. package/src/slashingProtection/attestation/errors.ts +0 -66
  84. package/src/slashingProtection/attestation/index.ts +0 -171
  85. package/src/slashingProtection/block/blockBySlotRepository.ts +0 -78
  86. package/src/slashingProtection/block/errors.ts +0 -28
  87. package/src/slashingProtection/block/index.ts +0 -94
  88. package/src/slashingProtection/index.ts +0 -95
  89. package/src/slashingProtection/interchange/errors.ts +0 -15
  90. package/src/slashingProtection/interchange/formats/completeV4.ts +0 -125
  91. package/src/slashingProtection/interchange/formats/index.ts +0 -7
  92. package/src/slashingProtection/interchange/formats/v5.ts +0 -120
  93. package/src/slashingProtection/interchange/index.ts +0 -5
  94. package/src/slashingProtection/interchange/parseInterchange.ts +0 -55
  95. package/src/slashingProtection/interchange/serializeInterchange.ts +0 -35
  96. package/src/slashingProtection/interchange/types.ts +0 -18
  97. package/src/slashingProtection/interface.ts +0 -28
  98. package/src/slashingProtection/minMaxSurround/distanceStoreRepository.ts +0 -57
  99. package/src/slashingProtection/minMaxSurround/errors.ts +0 -27
  100. package/src/slashingProtection/minMaxSurround/index.ts +0 -4
  101. package/src/slashingProtection/minMaxSurround/interface.ts +0 -23
  102. package/src/slashingProtection/minMaxSurround/minMaxSurround.ts +0 -104
  103. package/src/slashingProtection/types.ts +0 -12
  104. package/src/slashingProtection/utils.ts +0 -42
  105. package/src/types.ts +0 -31
  106. package/src/util/batch.ts +0 -15
  107. package/src/util/clock.ts +0 -164
  108. package/src/util/difference.ts +0 -10
  109. package/src/util/externalSignerClient.ts +0 -277
  110. package/src/util/format.ts +0 -3
  111. package/src/util/index.ts +0 -6
  112. package/src/util/logger.ts +0 -51
  113. package/src/util/params.ts +0 -313
  114. package/src/util/url.ts +0 -16
  115. package/src/validator.ts +0 -418
@@ -1,349 +0,0 @@
1
- import {ApiClient, routes} from "@lodestar/api";
2
- import {ChainForkConfig} from "@lodestar/config";
3
- import {ForkSeq} from "@lodestar/params";
4
- import {computeEpochAtSlot, isAggregatorFromCommitteeLength} from "@lodestar/state-transition";
5
- import {BLSSignature, SignedAggregateAndProof, SingleAttestation, Slot, phase0, ssz} from "@lodestar/types";
6
- import {prettyBytes, sleep, toRootHex} from "@lodestar/utils";
7
- import {Metrics} from "../metrics.js";
8
- import {PubkeyHex} from "../types.js";
9
- import {IClock, LoggerVc} from "../util/index.js";
10
- import {AttDutyAndProof, AttestationDutiesService} from "./attestationDuties.js";
11
- import {ChainHeaderTracker} from "./chainHeaderTracker.js";
12
- import {ValidatorEventEmitter} from "./emitter.js";
13
- import {SyncingStatusTracker} from "./syncingStatusTracker.js";
14
- import {groupAttDutiesByCommitteeIndex} from "./utils.js";
15
- import {ValidatorStore} from "./validatorStore.js";
16
-
17
- export type AttestationServiceOpts = {
18
- afterBlockDelaySlotFraction?: number;
19
- distributedAggregationSelection?: boolean;
20
- };
21
-
22
- /**
23
- * Previously, submitting attestations too early may cause some attestations missed (because some clients may not queue attestations, and
24
- * sent peers are few) so it was configured as 1/6. See https://github.com/ChainSafe/lodestar/issues/3943
25
- *
26
- * As of Nov 2022, it's proved that submitting attestations asap is better as it avoids busy time of node at around 1/3 of slot (and could be
27
- * because sent peers are better than before). See https://github.com/ChainSafe/lodestar/issues/4600#issuecomment-1321546586
28
- */
29
- const DEFAULT_AFTER_BLOCK_DELAY_SLOT_FRACTION = 0;
30
-
31
- /**
32
- * Service that sets up and handles validator attester duties.
33
- */
34
- export class AttestationService {
35
- private readonly dutiesService: AttestationDutiesService;
36
-
37
- constructor(
38
- private readonly logger: LoggerVc,
39
- private readonly api: ApiClient,
40
- private readonly clock: IClock,
41
- private readonly validatorStore: ValidatorStore,
42
- private readonly emitter: ValidatorEventEmitter,
43
- chainHeadTracker: ChainHeaderTracker,
44
- syncingStatusTracker: SyncingStatusTracker,
45
- private readonly metrics: Metrics | null,
46
- private readonly config: ChainForkConfig,
47
- private readonly opts?: AttestationServiceOpts
48
- ) {
49
- this.dutiesService = new AttestationDutiesService(
50
- logger,
51
- api,
52
- clock,
53
- validatorStore,
54
- chainHeadTracker,
55
- syncingStatusTracker,
56
- metrics,
57
- {
58
- distributedAggregationSelection: opts?.distributedAggregationSelection,
59
- }
60
- );
61
-
62
- // At most every slot, check existing duties from AttestationDutiesService and run tasks
63
- clock.runEverySlot(this.runAttestationTasks);
64
- }
65
-
66
- removeDutiesForKey(pubkey: PubkeyHex): void {
67
- this.dutiesService.removeDutiesForKey(pubkey);
68
- }
69
-
70
- private runAttestationTasks = async (slot: Slot, signal: AbortSignal): Promise<void> => {
71
- // Fetch info first so a potential delay is absorbed by the sleep() below
72
- const duties = this.dutiesService.getDutiesAtSlot(slot);
73
- if (duties.length === 0) {
74
- return;
75
- }
76
-
77
- if (this.opts?.distributedAggregationSelection) {
78
- // Validator in distributed cluster only has a key share, not the full private key.
79
- // The partial selection proofs must be exchanged for combined selection proofs by
80
- // calling submitBeaconCommitteeSelections on the distributed validator middleware client.
81
- // This will run in parallel to other attestation tasks but must be finished before starting
82
- // attestation aggregation as it is required to correctly determine if validator is aggregator
83
- // and to produce a AggregateAndProof that can be threshold aggregated by the middleware client.
84
- this.runDistributedAggregationSelectionTasks(duties, slot, signal).catch((e) =>
85
- this.logger.error("Error on attestation aggregation selection", {slot}, e)
86
- );
87
- }
88
-
89
- // A validator should create and broadcast the attestation to the associated attestation subnet when either
90
- // (a) the validator has received a valid block from the expected block proposer for the assigned slot or
91
- // (b) one-third of the slot has transpired (SECONDS_PER_SLOT / 3 seconds after the start of slot) -- whichever comes first.
92
- await Promise.race([sleep(this.clock.msToSlot(slot + 1 / 3), signal), this.emitter.waitForBlockSlot(slot)]);
93
- this.metrics?.attesterStepCallProduceAttestation.observe(this.clock.secFromSlot(slot + 1 / 3));
94
-
95
- // Beacon node's endpoint produceAttestationData return data is not dependent on committeeIndex.
96
- // Produce a single attestation for all committees and submit unaggregated attestations in one go.
97
- try {
98
- // Produce a single attestation for all committees, and clone mutate before signing
99
- const attestationNoCommittee = await this.produceAttestation(0, slot);
100
-
101
- // Step 1. Mutate, and sign `Attestation` for each validator. Then publish all `Attestations` in one go
102
- await this.signAndPublishAttestations(slot, attestationNoCommittee, duties);
103
-
104
- // Step 2. after all attestations are submitted, make an aggregate.
105
- // First, wait until the `aggregation_production_instant` (2/3rds of the way through the slot)
106
- await sleep(this.clock.msToSlot(slot + 2 / 3), signal);
107
- this.metrics?.attesterStepCallProduceAggregate.observe(this.clock.secFromSlot(slot + 2 / 3));
108
-
109
- const dutiesByCommitteeIndex = groupAttDutiesByCommitteeIndex(duties);
110
- const isPostElectra = this.config.getForkSeq(slot) >= ForkSeq.electra;
111
-
112
- // Then download, sign and publish a `SignedAggregateAndProof` for each
113
- // validator that is elected to aggregate for this `slot` and `committeeIndex`.
114
- await Promise.all(
115
- Array.from(dutiesByCommitteeIndex.entries()).map(([index, dutiesSameCommittee]) => {
116
- const attestationData: phase0.AttestationData = {...attestationNoCommittee, index: isPostElectra ? 0 : index};
117
- return this.produceAndPublishAggregates(attestationData, index, dutiesSameCommittee);
118
- })
119
- );
120
- } catch (e) {
121
- this.logger.error("Error on attestation routine", {slot}, e as Error);
122
- }
123
- };
124
-
125
- /**
126
- * Performs the first step of the attesting process: downloading one `Attestation` object.
127
- * Beacon node's endpoint produceAttestationData return data is not dependent on committeeIndex.
128
- * For a validator client with many validators this allows to do a single call for all committees
129
- * in a slot, saving resources in both the vc and beacon node
130
- */
131
- private async produceAttestation(committeeIndex: number, slot: Slot): Promise<phase0.AttestationData> {
132
- // Produce one attestation data per slot and committeeIndex
133
- return (await this.api.validator.produceAttestationData({committeeIndex, slot})).value();
134
- }
135
-
136
- /**
137
- * Only one `Attestation` is downloaded from the BN. It is then signed by each
138
- * validator and the list of individually-signed `Attestation` objects is returned to the BN.
139
- */
140
- private async signAndPublishAttestations(
141
- slot: Slot,
142
- attestationNoCommittee: phase0.AttestationData,
143
- duties: AttDutyAndProof[]
144
- ): Promise<void> {
145
- const signedAttestations: SingleAttestation[] = [];
146
- const headRootHex = toRootHex(attestationNoCommittee.beaconBlockRoot);
147
- const currentEpoch = computeEpochAtSlot(slot);
148
- const isPostElectra = this.config.getForkSeq(slot) >= ForkSeq.electra;
149
-
150
- await Promise.all(
151
- duties.map(async ({duty}) => {
152
- const index = isPostElectra ? 0 : duty.committeeIndex;
153
- const attestationData: phase0.AttestationData = {...attestationNoCommittee, index};
154
- const logCtxValidator = {slot, index, head: headRootHex, validatorIndex: duty.validatorIndex};
155
-
156
- try {
157
- signedAttestations.push(await this.validatorStore.signAttestation(duty, attestationData, currentEpoch));
158
- this.logger.debug("Signed attestation", logCtxValidator);
159
- } catch (e) {
160
- this.metrics?.attestaterError.inc({error: "sign"});
161
- this.logger.error("Error signing attestation", logCtxValidator, e as Error);
162
- }
163
- })
164
- );
165
-
166
- // signAndPublishAttestations() may be called before the 1/3 cutoff time if the block was received early.
167
- // If we produced the block or we got the block sooner than our peers, our attestations can be dropped because
168
- // they reach our peers before the block. To prevent that, we wait 2 extra seconds AFTER block arrival, but
169
- // never beyond the 1/3 cutoff time.
170
- // https://github.com/status-im/nimbus-eth2/blob/7b64c1dce4392731a4a59ee3a36caef2e0a8357a/beacon_chain/validators/validator_duties.nim#L1123
171
- const msToOneThirdSlot = this.clock.msToSlot(slot + 1 / 3);
172
- // submitting attestations asap to avoid busy time at around 1/3 of slot
173
- const afterBlockDelayMs =
174
- 1000 *
175
- this.clock.secondsPerSlot *
176
- (this.opts?.afterBlockDelaySlotFraction ?? DEFAULT_AFTER_BLOCK_DELAY_SLOT_FRACTION);
177
- await sleep(Math.min(msToOneThirdSlot, afterBlockDelayMs));
178
-
179
- this.metrics?.attesterStepCallPublishAttestation.observe(this.clock.secFromSlot(slot + 1 / 3));
180
-
181
- // Step 2. Publish all `Attestations` in one go
182
- try {
183
- (await this.api.beacon.submitPoolAttestationsV2({signedAttestations})).assertOk();
184
- this.logger.info("Published attestations", {
185
- slot,
186
- head: prettyBytes(headRootHex),
187
- count: signedAttestations.length,
188
- });
189
- this.metrics?.publishedAttestations.inc(signedAttestations.length);
190
- } catch (e) {
191
- // Note: metric counts only 1 since we don't know how many signedAttestations are invalid
192
- this.metrics?.attestaterError.inc({error: "publish"});
193
- this.logger.error("Error publishing attestations", {slot}, e as Error);
194
- }
195
- }
196
-
197
- /**
198
- * Performs the second step of the attesting process: downloading an aggregated `Attestation`,
199
- * converting it into a `SignedAggregateAndProof` and returning it to the BN.
200
- *
201
- * https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/validator.md#broadcast-aggregate
202
- *
203
- * Only one aggregated `Attestation` is downloaded from the BN. It is then signed
204
- * by each validator and the list of individually-signed `SignedAggregateAndProof` objects is
205
- * returned to the BN.
206
- */
207
- private async produceAndPublishAggregates(
208
- attestation: phase0.AttestationData,
209
- committeeIndex: number,
210
- duties: AttDutyAndProof[]
211
- ): Promise<void> {
212
- const logCtx = {slot: attestation.slot, index: committeeIndex};
213
-
214
- // No validator is aggregator, skip
215
- if (duties.every(({selectionProof}) => selectionProof === null)) {
216
- return;
217
- }
218
-
219
- this.logger.verbose("Aggregating attestations", logCtx);
220
- const aggregate = (
221
- await this.api.validator.getAggregatedAttestationV2({
222
- attestationDataRoot: ssz.phase0.AttestationData.hashTreeRoot(attestation),
223
- slot: attestation.slot,
224
- committeeIndex,
225
- })
226
- ).value();
227
- const participants = aggregate.aggregationBits.getTrueBitIndexes().length;
228
- this.metrics?.numParticipantsInAggregate.observe(participants);
229
-
230
- const signedAggregateAndProofs: SignedAggregateAndProof[] = [];
231
-
232
- await Promise.all(
233
- duties.map(async ({duty, selectionProof}) => {
234
- const logCtxValidator = {...logCtx, validatorIndex: duty.validatorIndex};
235
- try {
236
- // Produce signed aggregates only for validators that are subscribed aggregators.
237
- if (selectionProof !== null) {
238
- signedAggregateAndProofs.push(
239
- await this.validatorStore.signAggregateAndProof(duty, selectionProof, aggregate)
240
- );
241
- this.logger.debug("Signed aggregateAndProofs", logCtxValidator);
242
- }
243
- } catch (e) {
244
- this.logger.error("Error signing aggregateAndProofs", logCtxValidator, e as Error);
245
- }
246
- })
247
- );
248
-
249
- this.metrics?.attesterStepCallPublishAggregate.observe(this.clock.secFromSlot(attestation.slot + 2 / 3));
250
-
251
- if (signedAggregateAndProofs.length > 0) {
252
- try {
253
- (await this.api.validator.publishAggregateAndProofsV2({signedAggregateAndProofs})).assertOk();
254
- this.logger.info("Published aggregateAndProofs", {
255
- ...logCtx,
256
- participants,
257
- count: signedAggregateAndProofs.length,
258
- });
259
- this.metrics?.publishedAggregates.inc(signedAggregateAndProofs.length);
260
- } catch (e) {
261
- this.logger.error("Error publishing aggregateAndProofs", logCtx, e as Error);
262
- }
263
- }
264
- }
265
-
266
- /**
267
- * Performs additional attestation aggregation tasks required if validator is part of distributed cluster
268
- *
269
- * 1. Exchange partial for combined selection proofs
270
- * 2. Determine validators that should aggregate attestations
271
- * 3. Mutate duty objects to set selection proofs for aggregators
272
- * 4. Resubscribe validators as aggregators on beacon committee subnets
273
- *
274
- * See https://docs.google.com/document/d/1q9jOTPcYQa-3L8luRvQJ-M0eegtba4Nmon3dpO79TMk/mobilebasic
275
- */
276
- private async runDistributedAggregationSelectionTasks(
277
- duties: AttDutyAndProof[],
278
- slot: number,
279
- signal: AbortSignal
280
- ): Promise<void> {
281
- const partialSelections: routes.validator.BeaconCommitteeSelection[] = duties.map(
282
- ({duty, partialSelectionProof}) => ({
283
- validatorIndex: duty.validatorIndex,
284
- slot,
285
- selectionProof: partialSelectionProof as BLSSignature,
286
- })
287
- );
288
-
289
- this.logger.debug("Submitting partial beacon committee selection proofs", {slot, count: partialSelections.length});
290
-
291
- const res = await Promise.race([
292
- this.api.validator.submitBeaconCommitteeSelections({selections: partialSelections}),
293
- // Exit attestation aggregation flow if there is no response after 1/3 of slot as
294
- // beacon node would likely not have enough time to prepare an aggregate attestation.
295
- // Note that the aggregations flow is not explicitly exited but rather will be skipped
296
- // due to the fact that calculation of `is_aggregator` in AttestationDutiesService is not done
297
- // and selectionProof is set to null, meaning no validator will be considered an aggregator.
298
- sleep(this.clock.msToSlot(slot + 1 / 3), signal),
299
- ]);
300
-
301
- if (!res) {
302
- throw new Error("Failed to receive combined selection proofs before 1/3 of slot");
303
- }
304
-
305
- const combinedSelections = res.value();
306
- this.logger.debug("Received combined beacon committee selection proofs", {slot, count: combinedSelections.length});
307
-
308
- const beaconCommitteeSubscriptions: routes.validator.BeaconCommitteeSubscription[] = [];
309
-
310
- for (const dutyAndProof of duties) {
311
- const {validatorIndex, committeeIndex, committeeLength, committeesAtSlot} = dutyAndProof.duty;
312
- const logCtxValidator = {slot, index: committeeIndex, validatorIndex};
313
-
314
- const combinedSelection = combinedSelections.find((s) => s.validatorIndex === validatorIndex && s.slot === slot);
315
-
316
- if (!combinedSelection) {
317
- this.logger.warn("Did not receive combined beacon committee selection proof", logCtxValidator);
318
- continue;
319
- }
320
-
321
- const isAggregator = isAggregatorFromCommitteeLength(committeeLength, combinedSelection.selectionProof);
322
-
323
- if (isAggregator) {
324
- // Update selection proof by mutating duty object
325
- dutyAndProof.selectionProof = combinedSelection.selectionProof;
326
-
327
- // Only push subnet subscriptions with `isAggregator=true` as all validators
328
- // with duties for slot are already subscribed to subnets with `isAggregator=false`.
329
- beaconCommitteeSubscriptions.push({
330
- validatorIndex,
331
- committeesAtSlot,
332
- committeeIndex,
333
- slot,
334
- isAggregator,
335
- });
336
- this.logger.debug("Resubscribing validator as aggregator on beacon committee subnet", logCtxValidator);
337
- }
338
- }
339
-
340
- // If there are any subscriptions with aggregators, push them out to the beacon node.
341
- if (beaconCommitteeSubscriptions.length > 0) {
342
- (await this.api.validator.prepareBeaconCommitteeSubnet({subscriptions: beaconCommitteeSubscriptions})).assertOk();
343
- this.logger.debug("Resubscribed validators as aggregators on beacon committee subnets", {
344
- slot,
345
- count: beaconCommitteeSubscriptions.length,
346
- });
347
- }
348
- }
349
- }