@lodestar/validator 1.35.0-dev.fcf8d024ea → 1.35.0-rc.0
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/metrics.js +14 -14
- package/lib/metrics.js.map +1 -1
- package/lib/services/attestation.d.ts.map +1 -1
- package/lib/services/attestation.js +27 -35
- package/lib/services/attestation.js.map +1 -1
- package/lib/services/attestationDuties.d.ts.map +1 -1
- package/lib/services/attestationDuties.js +0 -1
- package/lib/services/attestationDuties.js.map +1 -1
- package/lib/services/blockDuties.d.ts +2 -2
- package/lib/services/blockDuties.d.ts.map +1 -1
- package/lib/services/blockDuties.js +3 -4
- package/lib/services/blockDuties.js.map +1 -1
- package/lib/services/syncCommittee.d.ts.map +1 -1
- package/lib/services/syncCommittee.js +22 -30
- package/lib/services/syncCommittee.js.map +1 -1
- package/lib/util/clock.d.ts +0 -3
- package/lib/util/clock.d.ts.map +1 -1
- package/lib/util/clock.js +0 -4
- package/lib/util/clock.js.map +1 -1
- package/lib/util/params.js +1 -8
- package/lib/util/params.js.map +1 -1
- package/package.json +10 -10
- package/src/metrics.ts +14 -14
- package/src/services/attestation.ts +24 -37
- package/src/services/attestationDuties.ts +0 -1
- package/src/services/blockDuties.ts +5 -7
- package/src/services/syncCommittee.ts +22 -43
- package/src/util/clock.ts +0 -6
- package/src/util/params.ts +1 -8
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {ApiClient, routes} from "@lodestar/api";
|
|
2
|
-
import {
|
|
2
|
+
import {ChainConfig} from "@lodestar/config";
|
|
3
3
|
import {computeEpochAtSlot, computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
4
4
|
import {BLSPubkey, Epoch, RootHex, Slot} from "@lodestar/types";
|
|
5
5
|
import {sleep, toPubkeyHex} from "@lodestar/utils";
|
|
@@ -9,11 +9,10 @@ import {IClock, LoggerVc, differenceHex} from "../util/index.js";
|
|
|
9
9
|
import {ValidatorStore} from "./validatorStore.js";
|
|
10
10
|
|
|
11
11
|
/** This polls block duties 1s before the next epoch */
|
|
12
|
-
// TODO: change to
|
|
12
|
+
// TODO: change to 6 to do it 2s before the next epoch
|
|
13
13
|
// once we have some improvement on epoch transition time
|
|
14
14
|
// see https://github.com/ChainSafe/lodestar/issues/5792#issuecomment-1647457442
|
|
15
|
-
|
|
16
|
-
const BLOCK_DUTIES_LOOKAHEAD_BPS = 9167;
|
|
15
|
+
const BLOCK_DUTIES_LOOKAHEAD_FACTOR = 12;
|
|
17
16
|
/** Only retain `HISTORICAL_DUTIES_EPOCHS` duties prior to the current epoch */
|
|
18
17
|
const HISTORICAL_DUTIES_EPOCHS = 2;
|
|
19
18
|
// Re-declaring to not have to depend on `lodestar-params` just for this 0
|
|
@@ -31,7 +30,7 @@ export class BlockDutiesService {
|
|
|
31
30
|
private readonly proposers = new Map<Epoch, BlockDutyAtEpoch>();
|
|
32
31
|
|
|
33
32
|
constructor(
|
|
34
|
-
private readonly config:
|
|
33
|
+
private readonly config: ChainConfig,
|
|
35
34
|
private readonly logger: LoggerVc,
|
|
36
35
|
private readonly api: ApiClient,
|
|
37
36
|
private readonly clock: IClock,
|
|
@@ -170,8 +169,7 @@ export class BlockDutiesService {
|
|
|
170
169
|
*/
|
|
171
170
|
private async pollBeaconProposersNextEpoch(currentSlot: Slot, nextEpoch: Epoch, signal: AbortSignal): Promise<void> {
|
|
172
171
|
const nextSlot = currentSlot + 1;
|
|
173
|
-
const lookAheadMs =
|
|
174
|
-
this.config.SLOT_DURATION_MS - this.config.getSlotComponentDurationMs(BLOCK_DUTIES_LOOKAHEAD_BPS);
|
|
172
|
+
const lookAheadMs = (this.config.SECONDS_PER_SLOT * 1000) / BLOCK_DUTIES_LOOKAHEAD_FACTOR;
|
|
175
173
|
await sleep(this.clock.msToSlot(nextSlot) - lookAheadMs, signal);
|
|
176
174
|
this.logger.debug("Polling proposers for next epoch", {nextEpoch, nextSlot});
|
|
177
175
|
// Poll proposers for the next epoch
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {ApiClient, routes} from "@lodestar/api";
|
|
2
2
|
import {ChainForkConfig} from "@lodestar/config";
|
|
3
|
-
import {
|
|
4
|
-
import {isSyncCommitteeAggregator} from "@lodestar/state-transition";
|
|
3
|
+
import {computeEpochAtSlot, isSyncCommitteeAggregator} from "@lodestar/state-transition";
|
|
5
4
|
import {BLSSignature, CommitteeIndex, Root, Slot, altair} from "@lodestar/types";
|
|
6
5
|
import {sleep} from "@lodestar/utils";
|
|
7
6
|
import {Metrics} from "../metrics.js";
|
|
@@ -59,11 +58,9 @@ export class SyncCommitteeService {
|
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
private runSyncCommitteeTasks = async (slot: Slot, signal: AbortSignal): Promise<void> => {
|
|
62
|
-
const fork = this.config.getForkName(slot);
|
|
63
|
-
|
|
64
61
|
try {
|
|
65
62
|
// Before altair fork no need to check duties
|
|
66
|
-
if (
|
|
63
|
+
if (computeEpochAtSlot(slot) < this.config.ALTAIR_FORK_EPOCH) {
|
|
67
64
|
return;
|
|
68
65
|
}
|
|
69
66
|
|
|
@@ -80,32 +77,25 @@ export class SyncCommitteeService {
|
|
|
80
77
|
// This will run in parallel to other sync committee tasks but must be finished before starting
|
|
81
78
|
// sync committee contributions as it is required to correctly determine if validator is aggregator
|
|
82
79
|
// and to produce a ContributionAndProof that can be threshold aggregated by the middleware client.
|
|
83
|
-
this.runDistributedAggregationSelectionTasks(
|
|
80
|
+
this.runDistributedAggregationSelectionTasks(dutiesAtSlot, slot, signal).catch((e) =>
|
|
84
81
|
this.logger.error("Error on sync committee aggregation selection", {slot}, e)
|
|
85
82
|
);
|
|
86
83
|
}
|
|
87
84
|
|
|
88
85
|
// unlike Attestation, SyncCommitteeSignature could be published asap
|
|
89
|
-
// especially with lodestar, it's very busy at
|
|
86
|
+
// especially with lodestar, it's very busy at 1/3 of slot
|
|
90
87
|
// see https://github.com/ChainSafe/lodestar/issues/4608
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
sleep(syncMessageDueMs - this.clock.msFromSlot(slot), signal),
|
|
94
|
-
this.emitter.waitForBlockSlot(slot),
|
|
95
|
-
]);
|
|
96
|
-
this.metrics?.syncCommitteeStepCallProduceMessage.observe(this.clock.secFromSlot(slot) - syncMessageDueMs / 1000);
|
|
88
|
+
await Promise.race([sleep(this.clock.msToSlot(slot + 1 / 3), signal), this.emitter.waitForBlockSlot(slot)]);
|
|
89
|
+
this.metrics?.syncCommitteeStepCallProduceMessage.observe(this.clock.secFromSlot(slot + 1 / 3));
|
|
97
90
|
|
|
98
91
|
// Step 1. Download, sign and publish an `SyncCommitteeMessage` for each validator.
|
|
99
92
|
// Differs from AttestationService, `SyncCommitteeMessage` are equal for all
|
|
100
|
-
const beaconBlockRoot = await this.produceAndPublishSyncCommittees(
|
|
93
|
+
const beaconBlockRoot = await this.produceAndPublishSyncCommittees(slot, dutiesAtSlot);
|
|
101
94
|
|
|
102
95
|
// Step 2. If an attestation was produced, make an aggregate.
|
|
103
|
-
// First, wait until the `
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
this.metrics?.syncCommitteeStepCallProduceAggregate.observe(
|
|
107
|
-
this.clock.secFromSlot(slot) - syncContributionDueMs / 1000
|
|
108
|
-
);
|
|
96
|
+
// First, wait until the `aggregation_production_instant` (2/3rds of the way though the slot)
|
|
97
|
+
await sleep(this.clock.msToSlot(slot + 2 / 3), signal);
|
|
98
|
+
this.metrics?.syncCommitteeStepCallProduceAggregate.observe(this.clock.secFromSlot(slot + 2 / 3));
|
|
109
99
|
|
|
110
100
|
// await for all so if the Beacon node is overloaded it auto-throttles
|
|
111
101
|
// TODO: This approach is conservative to reduce the node's load, review
|
|
@@ -115,11 +105,9 @@ export class SyncCommitteeService {
|
|
|
115
105
|
if (duties.length === 0) return;
|
|
116
106
|
// Then download, sign and publish a `SignedAggregateAndProof` for each
|
|
117
107
|
// validator that is elected to aggregate for this `slot` and `subcommitteeIndex`.
|
|
118
|
-
await this.produceAndPublishAggregates(
|
|
119
|
-
(
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
);
|
|
108
|
+
await this.produceAndPublishAggregates(slot, subcommitteeIndex, beaconBlockRoot, duties).catch((e: Error) => {
|
|
109
|
+
this.logger.error("Error on SyncCommitteeContribution", {slot, index: subcommitteeIndex}, e);
|
|
110
|
+
});
|
|
123
111
|
})
|
|
124
112
|
);
|
|
125
113
|
} catch (e) {
|
|
@@ -136,11 +124,7 @@ export class SyncCommitteeService {
|
|
|
136
124
|
* Only one `SyncCommittee` is downloaded from the BN. It is then signed by each
|
|
137
125
|
* validator and the list of individually-signed `SyncCommittee` objects is returned to the BN.
|
|
138
126
|
*/
|
|
139
|
-
private async produceAndPublishSyncCommittees(
|
|
140
|
-
fork: ForkName,
|
|
141
|
-
slot: Slot,
|
|
142
|
-
duties: SyncDutyAndProofs[]
|
|
143
|
-
): Promise<Root> {
|
|
127
|
+
private async produceAndPublishSyncCommittees(slot: Slot, duties: SyncDutyAndProofs[]): Promise<Root> {
|
|
144
128
|
const logCtx = {slot};
|
|
145
129
|
|
|
146
130
|
// /eth/v1/beacon/blocks/:blockId/root -> at slot -1
|
|
@@ -172,15 +156,14 @@ export class SyncCommitteeService {
|
|
|
172
156
|
// by default we want to submit SyncCommitteeSignature asap after we receive block
|
|
173
157
|
// provide a delay option just in case any client implementation validate the existence of block in
|
|
174
158
|
// SyncCommitteeSignature gossip validation.
|
|
175
|
-
const
|
|
176
|
-
const msToCutoffTime = syncMessageDueMs - this.clock.msFromSlot(slot);
|
|
159
|
+
const msToOneThirdSlot = this.clock.msToSlot(slot + 1 / 3);
|
|
177
160
|
const afterBlockDelayMs = 1000 * this.clock.secondsPerSlot * (this.opts?.scAfterBlockDelaySlotFraction ?? 0);
|
|
178
|
-
const toDelayMs = Math.min(
|
|
161
|
+
const toDelayMs = Math.min(msToOneThirdSlot, afterBlockDelayMs);
|
|
179
162
|
if (toDelayMs > 0) {
|
|
180
163
|
await sleep(toDelayMs);
|
|
181
164
|
}
|
|
182
165
|
|
|
183
|
-
this.metrics?.syncCommitteeStepCallPublishMessage.observe(this.clock.secFromSlot(slot
|
|
166
|
+
this.metrics?.syncCommitteeStepCallPublishMessage.observe(this.clock.secFromSlot(slot + 1 / 3));
|
|
184
167
|
|
|
185
168
|
if (signatures.length > 0) {
|
|
186
169
|
try {
|
|
@@ -206,7 +189,6 @@ export class SyncCommitteeService {
|
|
|
206
189
|
* returned to the BN.
|
|
207
190
|
*/
|
|
208
191
|
private async produceAndPublishAggregates(
|
|
209
|
-
fork: ForkName,
|
|
210
192
|
slot: Slot,
|
|
211
193
|
subcommitteeIndex: CommitteeIndex,
|
|
212
194
|
beaconBlockRoot: Root,
|
|
@@ -241,9 +223,7 @@ export class SyncCommitteeService {
|
|
|
241
223
|
})
|
|
242
224
|
);
|
|
243
225
|
|
|
244
|
-
this.metrics?.syncCommitteeStepCallPublishAggregate.observe(
|
|
245
|
-
this.clock.secFromSlot(slot) - this.config.getSyncContributionDueMs(fork) / 1000
|
|
246
|
-
);
|
|
226
|
+
this.metrics?.syncCommitteeStepCallPublishAggregate.observe(this.clock.secFromSlot(slot + 2 / 3));
|
|
247
227
|
|
|
248
228
|
if (signedContributions.length > 0) {
|
|
249
229
|
try {
|
|
@@ -268,7 +248,6 @@ export class SyncCommitteeService {
|
|
|
268
248
|
* See https://docs.google.com/document/d/1q9jOTPcYQa-3L8luRvQJ-M0eegtba4Nmon3dpO79TMk/mobilebasic
|
|
269
249
|
*/
|
|
270
250
|
private async runDistributedAggregationSelectionTasks(
|
|
271
|
-
fork: ForkName,
|
|
272
251
|
duties: SyncDutyAndProofs[],
|
|
273
252
|
slot: number,
|
|
274
253
|
signal: AbortSignal
|
|
@@ -291,18 +270,18 @@ export class SyncCommitteeService {
|
|
|
291
270
|
|
|
292
271
|
const res = await Promise.race([
|
|
293
272
|
this.api.validator.submitSyncCommitteeSelections({selections: partialSelections}),
|
|
294
|
-
// Exit sync committee contributions flow if there is no response after
|
|
295
|
-
// This is in contrast to attestations aggregations flow which is already exited at
|
|
273
|
+
// Exit sync committee contributions flow if there is no response after 2/3 of slot.
|
|
274
|
+
// This is in contrast to attestations aggregations flow which is already exited at 1/3 of the slot
|
|
296
275
|
// because for sync committee is not required to resubscribe to subnets as beacon node will assume
|
|
297
276
|
// validator always aggregates. This allows us to wait until we have to produce sync committee contributions.
|
|
298
277
|
// Note that the sync committee contributions flow is not explicitly exited but rather will be skipped
|
|
299
278
|
// due to the fact that calculation of `is_sync_committee_aggregator` in SyncCommitteeDutiesService is not done
|
|
300
279
|
// and selectionProof is set to null, meaning no validator will be considered an aggregator.
|
|
301
|
-
sleep(this.
|
|
280
|
+
sleep(this.clock.msToSlot(slot + 2 / 3), signal),
|
|
302
281
|
]);
|
|
303
282
|
|
|
304
283
|
if (!res) {
|
|
305
|
-
throw new Error("Failed to receive combined selection proofs before
|
|
284
|
+
throw new Error("Failed to receive combined selection proofs before 2/3 of slot");
|
|
306
285
|
}
|
|
307
286
|
|
|
308
287
|
const combinedSelections = res.value();
|
package/src/util/clock.ts
CHANGED
|
@@ -16,7 +16,6 @@ export interface IClock {
|
|
|
16
16
|
runEverySlot(fn: (slot: Slot, signal: AbortSignal) => Promise<void>): void;
|
|
17
17
|
runEveryEpoch(fn: (epoch: Epoch, signal: AbortSignal) => Promise<void>): void;
|
|
18
18
|
msToSlot(slot: Slot): number;
|
|
19
|
-
msFromSlot(slot: Slot): number;
|
|
20
19
|
secFromSlot(slot: Slot): number;
|
|
21
20
|
getCurrentSlot(): Slot;
|
|
22
21
|
getCurrentEpoch(): Epoch;
|
|
@@ -77,11 +76,6 @@ export class Clock implements IClock {
|
|
|
77
76
|
return timeAt * 1000 - Date.now();
|
|
78
77
|
}
|
|
79
78
|
|
|
80
|
-
/** Milliseconds elapsed from a specific slot to now */
|
|
81
|
-
msFromSlot(slot: Slot): number {
|
|
82
|
-
return Date.now() - (this.genesisTime * 1000 + this.config.SLOT_DURATION_MS * slot);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
79
|
/** Seconds elapsed from a specific slot to now */
|
|
86
80
|
secFromSlot(slot: Slot): number {
|
|
87
81
|
return Date.now() / 1000 - (this.genesisTime + this.config.SECONDS_PER_SLOT * slot);
|
package/src/util/params.ts
CHANGED
|
@@ -146,18 +146,11 @@ function getSpecCriticalParams(localConfig: ChainConfig): Record<keyof ConfigWit
|
|
|
146
146
|
GLOAS_FORK_EPOCH: gloasForkRelevant,
|
|
147
147
|
|
|
148
148
|
// Time parameters
|
|
149
|
-
SECONDS_PER_SLOT: true,
|
|
150
|
-
SLOT_DURATION_MS: true,
|
|
149
|
+
SECONDS_PER_SLOT: true,
|
|
151
150
|
SECONDS_PER_ETH1_BLOCK: false, // Legacy
|
|
152
151
|
MIN_VALIDATOR_WITHDRAWABILITY_DELAY: true,
|
|
153
152
|
SHARD_COMMITTEE_PERIOD: true,
|
|
154
153
|
ETH1_FOLLOW_DISTANCE: true,
|
|
155
|
-
PROPOSER_REORG_CUTOFF_BPS: true,
|
|
156
|
-
ATTESTATION_DUE_BPS: true,
|
|
157
|
-
AGGREGATE_DUE_BPS: true,
|
|
158
|
-
// Altair
|
|
159
|
-
SYNC_MESSAGE_DUE_BPS: altairForkRelevant,
|
|
160
|
-
CONTRIBUTION_DUE_BPS: altairForkRelevant,
|
|
161
154
|
|
|
162
155
|
// Validator cycle
|
|
163
156
|
INACTIVITY_SCORE_BIAS: true,
|