@lodestar/validator 1.38.0 → 1.39.0-dev.39dac0f03d
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.
- package/lib/services/attestation.d.ts +0 -11
- package/lib/services/attestation.d.ts.map +1 -1
- package/lib/services/attestation.js +1 -75
- package/lib/services/attestation.js.map +1 -1
- package/lib/services/attestationDuties.d.ts +8 -0
- package/lib/services/attestationDuties.d.ts.map +1 -1
- package/lib/services/attestationDuties.js +48 -2
- package/lib/services/attestationDuties.js.map +1 -1
- package/lib/services/syncCommittee.d.ts +0 -10
- package/lib/services/syncCommittee.d.ts.map +1 -1
- package/lib/services/syncCommittee.js +0 -67
- package/lib/services/syncCommittee.js.map +1 -1
- package/lib/services/syncCommitteeDuties.d.ts +9 -0
- package/lib/services/syncCommitteeDuties.d.ts.map +1 -1
- package/lib/services/syncCommitteeDuties.js +64 -2
- package/lib/services/syncCommitteeDuties.js.map +1 -1
- package/lib/services/validatorStore.d.ts +7 -7
- package/lib/services/validatorStore.d.ts.map +1 -1
- package/lib/services/validatorStore.js.map +1 -1
- package/lib/util/params.js +1 -2
- package/lib/util/params.js.map +1 -1
- package/package.json +10 -10
- package/src/services/attestation.ts +3 -100
- package/src/services/attestationDuties.ts +66 -2
- package/src/services/syncCommittee.ts +2 -93
- package/src/services/syncCommitteeDuties.ts +85 -3
- package/src/services/validatorStore.ts +8 -8
- package/src/util/params.ts +2 -2
|
@@ -85,7 +85,7 @@ export class SyncCommitteeDutiesService {
|
|
|
85
85
|
private readonly config: ChainForkConfig,
|
|
86
86
|
private readonly logger: LoggerVc,
|
|
87
87
|
private readonly api: ApiClient,
|
|
88
|
-
clock: IClock,
|
|
88
|
+
private readonly clock: IClock,
|
|
89
89
|
private readonly validatorStore: ValidatorStore,
|
|
90
90
|
syncingStatusTracker: SyncingStatusTracker,
|
|
91
91
|
metrics: Metrics | null,
|
|
@@ -134,6 +134,18 @@ export class SyncCommitteeDutiesService {
|
|
|
134
134
|
selectionProofs: await this.getSelectionProofs(slot, dutyAtPeriod.duty),
|
|
135
135
|
});
|
|
136
136
|
}
|
|
137
|
+
|
|
138
|
+
if (this.opts?.distributedAggregationSelection) {
|
|
139
|
+
// Validator in distributed cluster only has a key share, not the full private key.
|
|
140
|
+
// The partial selection proofs must be exchanged for combined selection proofs by
|
|
141
|
+
// calling submitSyncCommitteeSelections on the distributed validator middleware client.
|
|
142
|
+
// This will run in parallel to other sync committee tasks but must be finished before starting
|
|
143
|
+
// sync committee contributions as it is required to correctly determine if validator is aggregator
|
|
144
|
+
// and to produce a ContributionAndProof that can be threshold aggregated by the middleware client.
|
|
145
|
+
this.runDistributedAggregationSelectionTasks(duties, slot).catch((e) =>
|
|
146
|
+
this.logger.error("Error on sync committee aggregation selection", {slot}, e)
|
|
147
|
+
);
|
|
148
|
+
}
|
|
137
149
|
}
|
|
138
150
|
|
|
139
151
|
return duties;
|
|
@@ -307,8 +319,8 @@ export class SyncCommitteeDutiesService {
|
|
|
307
319
|
if (this.opts?.distributedAggregationSelection) {
|
|
308
320
|
// Validator in distributed cluster only has a key share, not the full private key.
|
|
309
321
|
// Passing a partial selection proof to `is_sync_committee_aggregator` would produce incorrect result.
|
|
310
|
-
//
|
|
311
|
-
// distributed validator middleware client
|
|
322
|
+
// For all duties in the slot, aggregators are determined by exchanging partial for combined selection
|
|
323
|
+
// proofs retrieved from distributed validator middleware client at beginning of every slot.
|
|
312
324
|
dutiesAndProofs.push({
|
|
313
325
|
selectionProof: null,
|
|
314
326
|
partialSelectionProof: selectionProof,
|
|
@@ -334,4 +346,74 @@ export class SyncCommitteeDutiesService {
|
|
|
334
346
|
}
|
|
335
347
|
}
|
|
336
348
|
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Performs additional sync committee contribution tasks required if validator is part of distributed cluster
|
|
352
|
+
*
|
|
353
|
+
* 1. Exchange partial for combined selection proofs
|
|
354
|
+
* 2. Determine validators that should produce sync committee contribution
|
|
355
|
+
* 3. Mutate duty objects to set selection proofs for aggregators
|
|
356
|
+
*/
|
|
357
|
+
private async runDistributedAggregationSelectionTasks(duties: SyncDutyAndProofs[], slot: number): Promise<void> {
|
|
358
|
+
if (duties.length === 0) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const partialSelections: routes.validator.SyncCommitteeSelection[] = [];
|
|
363
|
+
|
|
364
|
+
for (const {duty, selectionProofs} of duties) {
|
|
365
|
+
const validatorSelections: routes.validator.SyncCommitteeSelection[] = selectionProofs.map(
|
|
366
|
+
({subcommitteeIndex, partialSelectionProof}) => ({
|
|
367
|
+
validatorIndex: duty.validatorIndex,
|
|
368
|
+
slot,
|
|
369
|
+
subcommitteeIndex,
|
|
370
|
+
selectionProof: partialSelectionProof as BLSSignature,
|
|
371
|
+
})
|
|
372
|
+
);
|
|
373
|
+
partialSelections.push(...validatorSelections);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
this.logger.debug("Submitting partial sync committee selection proofs", {slot, count: partialSelections.length});
|
|
377
|
+
|
|
378
|
+
const res = await this.api.validator.submitSyncCommitteeSelections(
|
|
379
|
+
{selections: partialSelections},
|
|
380
|
+
{
|
|
381
|
+
// Exit sync committee contributions flow if there is no response until CONTRIBUTION_DUE_BPS of the slot.
|
|
382
|
+
// Note that the sync committee contributions flow is not explicitly exited but rather will be skipped
|
|
383
|
+
// due to the fact that calculation of `is_sync_committee_aggregator` in SyncCommitteeDutiesService is not done
|
|
384
|
+
// and selectionProof is set to null, meaning no validator will be considered an aggregator.
|
|
385
|
+
timeoutMs: this.config.getSyncContributionDueMs(this.config.getForkName(slot)) - this.clock.msFromSlot(slot),
|
|
386
|
+
}
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
const combinedSelections = res.value();
|
|
390
|
+
this.logger.debug("Received combined sync committee selection proofs", {slot, count: combinedSelections.length});
|
|
391
|
+
|
|
392
|
+
for (const dutyAndProofs of duties) {
|
|
393
|
+
const {validatorIndex, subnets} = dutyAndProofs.duty;
|
|
394
|
+
|
|
395
|
+
for (const subnet of subnets) {
|
|
396
|
+
const logCtxValidator = {slot, index: subnet, validatorIndex};
|
|
397
|
+
|
|
398
|
+
const combinedSelection = combinedSelections.find(
|
|
399
|
+
(s) => s.validatorIndex === validatorIndex && s.slot === slot && s.subcommitteeIndex === subnet
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
if (!combinedSelection) {
|
|
403
|
+
this.logger.warn("Did not receive combined sync committee selection proof", logCtxValidator);
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const isAggregator = isSyncCommitteeAggregator(combinedSelection.selectionProof);
|
|
408
|
+
|
|
409
|
+
if (isAggregator) {
|
|
410
|
+
const selectionProofObject = dutyAndProofs.selectionProofs.find((p) => p.subcommitteeIndex === subnet);
|
|
411
|
+
if (selectionProofObject) {
|
|
412
|
+
// Update selection proof by mutating proof objects in duty object
|
|
413
|
+
selectionProofObject.selectionProof = combinedSelection.selectionProof;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
337
419
|
}
|
|
@@ -53,7 +53,7 @@ import {DoppelgangerService} from "./doppelgangerService.js";
|
|
|
53
53
|
import {IndicesService} from "./indices.js";
|
|
54
54
|
|
|
55
55
|
type BLSPubkeyMaybeHex = BLSPubkey | PubkeyHex;
|
|
56
|
-
type
|
|
56
|
+
type ExecutionAddress = string;
|
|
57
57
|
|
|
58
58
|
export enum SignerType {
|
|
59
59
|
Local,
|
|
@@ -74,7 +74,7 @@ export type SignerRemote = {
|
|
|
74
74
|
type DefaultProposerConfig = {
|
|
75
75
|
graffiti?: string;
|
|
76
76
|
strictFeeRecipientCheck: boolean;
|
|
77
|
-
feeRecipient:
|
|
77
|
+
feeRecipient: ExecutionAddress;
|
|
78
78
|
builder: {
|
|
79
79
|
gasLimit: number;
|
|
80
80
|
selection: routes.validator.BuilderSelection;
|
|
@@ -85,7 +85,7 @@ type DefaultProposerConfig = {
|
|
|
85
85
|
export type ProposerConfig = {
|
|
86
86
|
graffiti?: string;
|
|
87
87
|
strictFeeRecipientCheck?: boolean;
|
|
88
|
-
feeRecipient?:
|
|
88
|
+
feeRecipient?: ExecutionAddress;
|
|
89
89
|
builder?: {
|
|
90
90
|
gasLimit?: number;
|
|
91
91
|
selection?: routes.validator.BuilderSelection;
|
|
@@ -219,7 +219,7 @@ export class ValidatorStore {
|
|
|
219
219
|
: this.indicesService.pollValidatorIndices(Array.from(this.validators.keys()));
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
-
getFeeRecipient(pubkeyHex: PubkeyHex):
|
|
222
|
+
getFeeRecipient(pubkeyHex: PubkeyHex): ExecutionAddress {
|
|
223
223
|
const validatorData = this.validators.get(pubkeyHex);
|
|
224
224
|
if (validatorData === undefined) {
|
|
225
225
|
throw Error(`Validator pubkey ${pubkeyHex} not known`);
|
|
@@ -227,12 +227,12 @@ export class ValidatorStore {
|
|
|
227
227
|
return validatorData.feeRecipient ?? this.defaultProposerConfig.feeRecipient;
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
getFeeRecipientByIndex(index: ValidatorIndex):
|
|
230
|
+
getFeeRecipientByIndex(index: ValidatorIndex): ExecutionAddress {
|
|
231
231
|
const pubkey = this.indicesService.index2pubkey.get(index);
|
|
232
232
|
return pubkey ? this.getFeeRecipient(pubkey) : this.defaultProposerConfig.feeRecipient;
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
-
setFeeRecipient(pubkeyHex: PubkeyHex, feeRecipient:
|
|
235
|
+
setFeeRecipient(pubkeyHex: PubkeyHex, feeRecipient: ExecutionAddress): void {
|
|
236
236
|
const validatorData = this.validators.get(pubkeyHex);
|
|
237
237
|
if (validatorData === undefined) {
|
|
238
238
|
throw Error(`Validator pubkey ${pubkeyHex} not known`);
|
|
@@ -696,7 +696,7 @@ export class ValidatorStore {
|
|
|
696
696
|
|
|
697
697
|
async signValidatorRegistration(
|
|
698
698
|
pubkeyMaybeHex: BLSPubkeyMaybeHex,
|
|
699
|
-
regAttributes: {feeRecipient:
|
|
699
|
+
regAttributes: {feeRecipient: ExecutionAddress; gasLimit: number},
|
|
700
700
|
_slot: Slot
|
|
701
701
|
): Promise<bellatrix.SignedValidatorRegistrationV1> {
|
|
702
702
|
const pubkey = typeof pubkeyMaybeHex === "string" ? fromHex(pubkeyMaybeHex) : pubkeyMaybeHex;
|
|
@@ -727,7 +727,7 @@ export class ValidatorStore {
|
|
|
727
727
|
|
|
728
728
|
async getValidatorRegistration(
|
|
729
729
|
pubkeyMaybeHex: BLSPubkeyMaybeHex,
|
|
730
|
-
regAttributes: {feeRecipient:
|
|
730
|
+
regAttributes: {feeRecipient: ExecutionAddress; gasLimit: number},
|
|
731
731
|
slot: Slot
|
|
732
732
|
): Promise<bellatrix.SignedValidatorRegistrationV1> {
|
|
733
733
|
const pubkeyHex = typeof pubkeyMaybeHex === "string" ? pubkeyMaybeHex : toPubkeyHex(pubkeyMaybeHex);
|
package/src/util/params.ts
CHANGED
|
@@ -110,8 +110,8 @@ function getSpecCriticalParams(localConfig: ChainConfig): Record<keyof ConfigWit
|
|
|
110
110
|
|
|
111
111
|
PRESET_BASE: false, // Not relevant, each preset value is checked below
|
|
112
112
|
CONFIG_NAME: false, // Arbitrary string, not relevant
|
|
113
|
-
|
|
114
|
-
//
|
|
113
|
+
|
|
114
|
+
// Deprecated - All networks have completed the merge transition
|
|
115
115
|
TERMINAL_TOTAL_DIFFICULTY: false,
|
|
116
116
|
TERMINAL_BLOCK_HASH: false,
|
|
117
117
|
TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: false,
|