@lodestar/validator 1.45.0-dev.d7859e978b → 1.45.0-dev.e6c77a6488

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.
@@ -29,10 +29,6 @@ export declare class ValidatorEventEmitter extends ValidatorEventEmitter_base {
29
29
  * Wait for the first block to come with slot >= provided slot.
30
30
  */
31
31
  waitForBlockSlot(slot: Slot): Promise<void>;
32
- /**
33
- * Wait for the first execution payload availability event to come with slot >= provided slot.
34
- */
35
- waitForExecutionPayloadAvailableSlot(slot: Slot): Promise<void>;
36
32
  }
37
33
  export {};
38
34
  //# sourceMappingURL=emitter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../../src/services/emitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AACzC,OAAO,EAAC,kBAAkB,EAAC,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAC,IAAI,EAAC,MAAM,iBAAiB,CAAC;AACrC,OAAO,EAAC,aAAa,EAAC,MAAM,yBAAyB,CAAC;AAEtD,oBAAY,cAAc;IACxB;;OAEG;IACH,SAAS,cAAc;IACvB;;OAEG;IACH,yBAAyB,8BAA8B;CACxD;AAED,MAAM,MAAM,kCAAkC,GAAG;IAC/C,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,CAAC,cAAc,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1D,CAAC,cAAc,CAAC,yBAAyB,CAAC,EAAE,CAAC,OAAO,EAAE,kCAAkC,KAAK,IAAI,CAAC;CACnG,CAAC;;AAEF;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,0BAEzC;IACA;;OAEG;IACG,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhD;IAED;;OAEG;IACG,oCAAoC,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBpE;CACF"}
1
+ {"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../../src/services/emitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AACzC,OAAO,EAAC,kBAAkB,EAAC,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAC,IAAI,EAAC,MAAM,iBAAiB,CAAC;AACrC,OAAO,EAAC,aAAa,EAAC,MAAM,yBAAyB,CAAC;AAEtD,oBAAY,cAAc;IACxB;;OAEG;IACH,SAAS,cAAc;IACvB;;OAEG;IACH,yBAAyB,8BAA8B;CACxD;AAED,MAAM,MAAM,kCAAkC,GAAG;IAC/C,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,CAAC,cAAc,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1D,CAAC,cAAc,CAAC,yBAAyB,CAAC,EAAE,CAAC,OAAO,EAAE,kCAAkC,KAAK,IAAI,CAAC;CACnG,CAAC;;AAEF;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,0BAEzC;IACA;;OAEG;IACG,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhD;CACF"}
@@ -33,23 +33,5 @@ export class ValidatorEventEmitter extends EventEmitter {
33
33
  this.on(ValidatorEvent.chainHead, headListener);
34
34
  });
35
35
  }
36
- /**
37
- * Wait for the first execution payload availability event to come with slot >= provided slot.
38
- */
39
- async waitForExecutionPayloadAvailableSlot(slot) {
40
- let payloadListener;
41
- const onDone = () => {
42
- this.off(ValidatorEvent.executionPayloadAvailable, payloadListener);
43
- };
44
- return new Promise((resolve) => {
45
- payloadListener = (payload) => {
46
- if (payload.slot >= slot) {
47
- onDone();
48
- resolve();
49
- }
50
- };
51
- this.on(ValidatorEvent.executionPayloadAvailable, payloadListener);
52
- });
53
- }
54
36
  }
55
37
  //# sourceMappingURL=emitter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"emitter.js","sourceRoot":"","sources":["../../src/services/emitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;SAK7B,cAAc;AAA1B,IAAY,cASX;AATD,WAAY,cAAc;IACxB;;OAEG;IACH,yCAAuB,CAAA;IACvB;;OAEG;IACH,yEAAuD,CAAA;AAAC,CAC1D,EATY,cAAc,KAAd,cAAc,QASzB;AAYD;;GAEG;AACH,MAAM,OAAO,qBAAsB,SAAS,YAE1C;IACA;;OAEG;IACH,KAAK,CAAC,gBAAgB,CAAC,IAAU,EAAiB;QAChD,IAAI,YAA2C,CAAC;QAEhD,MAAM,MAAM,GAAG,GAAS,EAAE,CAAC;YACzB,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QAAA,CAClD,CAAC;QAEF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;YAC9B,YAAY,GAAG,CAAC,IAAmB,EAAQ,EAAE,CAAC;gBAC5C,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;oBACtB,MAAM,EAAE,CAAC;oBACT,OAAO,EAAE,CAAC;gBACZ,CAAC;YAAA,CACF,CAAC;YACF,IAAI,CAAC,EAAE,CAAC,cAAc,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QAAA,CACjD,CAAC,CAAC;IAAA,CACJ;IAED;;OAEG;IACH,KAAK,CAAC,oCAAoC,CAAC,IAAU,EAAiB;QACpE,IAAI,eAAsE,CAAC;QAE3E,MAAM,MAAM,GAAG,GAAS,EAAE,CAAC;YACzB,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,yBAAyB,EAAE,eAAe,CAAC,CAAC;QAAA,CACrE,CAAC;QAEF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;YAC9B,eAAe,GAAG,CAAC,OAAO,EAAQ,EAAE,CAAC;gBACnC,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;oBACzB,MAAM,EAAE,CAAC;oBACT,OAAO,EAAE,CAAC;gBACZ,CAAC;YAAA,CACF,CAAC;YACF,IAAI,CAAC,EAAE,CAAC,cAAc,CAAC,yBAAyB,EAAE,eAAe,CAAC,CAAC;QAAA,CACpE,CAAC,CAAC;IAAA,CACJ;CACF"}
1
+ {"version":3,"file":"emitter.js","sourceRoot":"","sources":["../../src/services/emitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;SAK7B,cAAc;AAA1B,IAAY,cASX;AATD,WAAY,cAAc;IACxB;;OAEG;IACH,yCAAuB,CAAA;IACvB;;OAEG;IACH,yEAAuD,CAAA;AAAC,CAC1D,EATY,cAAc,KAAd,cAAc,QASzB;AAYD;;GAEG;AACH,MAAM,OAAO,qBAAsB,SAAS,YAE1C;IACA;;OAEG;IACH,KAAK,CAAC,gBAAgB,CAAC,IAAU,EAAiB;QAChD,IAAI,YAA2C,CAAC;QAEhD,MAAM,MAAM,GAAG,GAAS,EAAE,CAAC;YACzB,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QAAA,CAClD,CAAC;QAEF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;YAC9B,YAAY,GAAG,CAAC,IAAmB,EAAQ,EAAE,CAAC;gBAC5C,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;oBACtB,MAAM,EAAE,CAAC;oBACT,OAAO,EAAE,CAAC;gBACZ,CAAC;YAAA,CACF,CAAC;YACF,IAAI,CAAC,EAAE,CAAC,cAAc,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QAAA,CACjD,CAAC,CAAC;IAAA,CACJ;CACF"}
@@ -10,7 +10,10 @@ import { ValidatorStore } from "./validatorStore.js";
10
10
  * dependent root for an epoch shifts (e.g. after a reorg) — detected by comparing the cached
11
11
  * `dependentRoot` reported by `BlockDutiesService` against the one we last submitted under.
12
12
  *
13
- * No-op pre-gloas.
13
+ * Proposers should broadcast their preferences before the fork so the proposer preference caches
14
+ * of beacon nodes and builders are warm for the first Gloas slots. We start submitting
15
+ * as soon as a duty's proposal slot is in Gloas, which is up to `SUBMIT_BEFORE_PROPOSAL_SLOTS`
16
+ * before the fork, so only the first few Gloas slots are affected by this pre-fork submission.
14
17
  */
15
18
  export declare class ProposerPreferencesService {
16
19
  private readonly config;
@@ -21,5 +24,7 @@ export declare class ProposerPreferencesService {
21
24
  private readonly submitted;
22
25
  constructor(config: ChainForkConfig, logger: LoggerVc, api: ApiClient, clock: IClock, validatorStore: ValidatorStore, blockDutiesService: BlockDutiesService, _metrics: Metrics | null);
23
26
  private runProposerPreferencesTask;
27
+ /** Drop tracking for past epochs; only currentEpoch and currentEpoch + 1 are ever processed. */
28
+ private pruneSubmitted;
24
29
  }
25
30
  //# sourceMappingURL=proposerPreferences.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"proposerPreferences.d.ts","sourceRoot":"","sources":["../../src/services/proposerPreferences.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,eAAe,CAAC;AACxC,OAAO,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAKjD,OAAO,EAAC,OAAO,EAAC,MAAM,eAAe,CAAC;AACtC,OAAO,EAAC,MAAM,EAAE,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAC,kBAAkB,EAAC,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAgBnD;;;;;;;GAOG;AACH,qBAAa,0BAA0B;IAInC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAEpB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IARrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsC;IAEhE,YACmB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,QAAQ,EAChB,GAAG,EAAE,SAAS,EAC/B,KAAK,EAAE,MAAM,EACI,cAAc,EAAE,cAAc,EAC9B,kBAAkB,EAAE,kBAAkB,EACvD,QAAQ,EAAE,OAAO,GAAG,IAAI,EAGzB;IAED,OAAO,CAAC,0BAA0B,CA0EhC;CACH"}
1
+ {"version":3,"file":"proposerPreferences.d.ts","sourceRoot":"","sources":["../../src/services/proposerPreferences.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,eAAe,CAAC;AACxC,OAAO,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAKjD,OAAO,EAAC,OAAO,EAAC,MAAM,eAAe,CAAC;AACtC,OAAO,EAAC,MAAM,EAAE,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAC,kBAAkB,EAAC,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAgBnD;;;;;;;;;;GAUG;AACH,qBAAa,0BAA0B;IAInC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAEpB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IARrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsC;IAEhE,YACmB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,QAAQ,EAChB,GAAG,EAAE,SAAS,EAC/B,KAAK,EAAE,MAAM,EACI,cAAc,EAAE,cAAc,EAC9B,kBAAkB,EAAE,kBAAkB,EACvD,QAAQ,EAAE,OAAO,GAAG,IAAI,EAIzB;IAED,OAAO,CAAC,0BAA0B,CA8EhC;IAEF,gGAAgG;IAChG,OAAO,CAAC,cAAc,CAMpB;CACH"}
@@ -17,7 +17,10 @@ const SUBMIT_BEFORE_PROPOSAL_SLOTS = Math.floor(SLOTS_PER_EPOCH / 4);
17
17
  * dependent root for an epoch shifts (e.g. after a reorg) — detected by comparing the cached
18
18
  * `dependentRoot` reported by `BlockDutiesService` against the one we last submitted under.
19
19
  *
20
- * No-op pre-gloas.
20
+ * Proposers should broadcast their preferences before the fork so the proposer preference caches
21
+ * of beacon nodes and builders are warm for the first Gloas slots. We start submitting
22
+ * as soon as a duty's proposal slot is in Gloas, which is up to `SUBMIT_BEFORE_PROPOSAL_SLOTS`
23
+ * before the fork, so only the first few Gloas slots are affected by this pre-fork submission.
21
24
  */
22
25
  export class ProposerPreferencesService {
23
26
  config;
@@ -33,9 +36,13 @@ export class ProposerPreferencesService {
33
36
  this.validatorStore = validatorStore;
34
37
  this.blockDutiesService = blockDutiesService;
35
38
  clock.runEverySlot(this.runProposerPreferencesTask);
39
+ clock.runEveryEpoch(this.pruneSubmitted);
36
40
  }
37
41
  runProposerPreferencesTask = async (slot) => {
38
- if (!isForkPostGloas(this.config.getForkName(slot))) {
42
+ // Start running once the submission window (`slot + SUBMIT_BEFORE_PROPOSAL_SLOTS`) reaches
43
+ // Gloas, i.e. already in the epoch before the fork. This allows builders to prepare and
44
+ // submit bids for the first Gloas slots.
45
+ if (!isForkPostGloas(this.config.getForkName(slot + SUBMIT_BEFORE_PROPOSAL_SLOTS))) {
39
46
  return;
40
47
  }
41
48
  const currentEpoch = computeEpochAtSlot(slot);
@@ -68,6 +75,8 @@ export class ProposerPreferencesService {
68
75
  continue;
69
76
  if (duty.slot > slot + SUBMIT_BEFORE_PROPOSAL_SLOTS)
70
77
  continue;
78
+ if (!isForkPostGloas(this.config.getForkName(duty.slot)))
79
+ continue;
71
80
  if (submission.slots.has(duty.slot))
72
81
  continue;
73
82
  try {
@@ -85,7 +94,7 @@ export class ProposerPreferencesService {
85
94
  return;
86
95
  }
87
96
  try {
88
- await this.api.beacon.submitSignedProposerPreferences({ signedProposerPreferences: batch });
97
+ await this.api.validator.submitProposerPreferences({ signedProposerPreferences: batch });
89
98
  // Only mark as submitted after the API call succeeds; a thrown error leaves the
90
99
  // slot eligible for retry on the next tick.
91
100
  for (const { submission, slot: submittedSlot } of pending) {
@@ -97,5 +106,13 @@ export class ProposerPreferencesService {
97
106
  this.logger.error("Error submitting signed proposer preferences", { count: batch.length }, e);
98
107
  }
99
108
  };
109
+ /** Drop tracking for past epochs; only currentEpoch and currentEpoch + 1 are ever processed. */
110
+ pruneSubmitted = async (epoch) => {
111
+ for (const trackedEpoch of this.submitted.keys()) {
112
+ if (trackedEpoch < epoch) {
113
+ this.submitted.delete(trackedEpoch);
114
+ }
115
+ }
116
+ };
100
117
  }
101
118
  //# sourceMappingURL=proposerPreferences.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"proposerPreferences.js","sourceRoot":"","sources":["../../src/services/proposerPreferences.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"proposerPreferences.js","sourceRoot":"","sources":["../../src/services/proposerPreferences.ts"],"names":[],"mappings":"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"}
@@ -17,12 +17,16 @@ export declare class PtcService {
17
17
  private readonly clock;
18
18
  private readonly validatorStore;
19
19
  private readonly emitter;
20
+ private readonly chainHeadTracker;
20
21
  private readonly metrics;
21
22
  private readonly dutiesService;
22
23
  constructor(config: ChainForkConfig, logger: LoggerVc, api: ApiClient, clock: IClock, validatorStore: ValidatorStore, emitter: ValidatorEventEmitter, chainHeadTracker: ChainHeaderTracker, syncingStatusTracker: SyncingStatusTracker, metrics: Metrics | null);
23
24
  removeDutiesForKey(pubkey: PubkeyHex): void;
24
25
  private runPtcTasks;
25
- private producePayloadAttestationData;
26
+ /**
27
+ * Resolve when the payload for the canonical head block at `slot` is available
28
+ */
29
+ private waitForCanonicalPayload;
26
30
  private signAndPublishPayloadAttestations;
27
31
  }
28
32
  //# sourceMappingURL=ptc.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ptc.d.ts","sourceRoot":"","sources":["../../src/services/ptc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAyB,MAAM,eAAe,CAAC;AAChE,OAAO,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAIjD,OAAO,EAAC,OAAO,EAAC,MAAM,eAAe,CAAC;AACtC,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AACtC,OAAO,EAAC,MAAM,EAAE,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAC,kBAAkB,EAAC,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAC,qBAAqB,EAAC,MAAM,cAAc,CAAC;AAEnD,OAAO,EAAC,oBAAoB,EAAC,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAEnD;;GAEG;AACH,qBAAa,UAAU;IAInB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAGxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAX1B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAmB;IAEjD,YACmB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,QAAQ,EAChB,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,qBAAqB,EAC/C,gBAAgB,EAAE,kBAAkB,EACpC,oBAAoB,EAAE,oBAAoB,EACzB,OAAO,EAAE,OAAO,GAAG,IAAI,EAczC;IAED,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAE1C;IAED,OAAO,CAAC,WAAW,CAgCjB;YAEY,6BAA6B;YAQ7B,iCAAiC;CAkDhD"}
1
+ {"version":3,"file":"ptc.d.ts","sourceRoot":"","sources":["../../src/services/ptc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAS,MAAM,eAAe,CAAC;AAChD,OAAO,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAIjD,OAAO,EAAC,OAAO,EAAC,MAAM,eAAe,CAAC;AACtC,OAAO,EAAC,SAAS,EAAC,MAAM,aAAa,CAAC;AACtC,OAAO,EAAC,MAAM,EAAE,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAC,kBAAkB,EAAC,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAqD,qBAAqB,EAAC,MAAM,cAAc,CAAC;AAEvG,OAAO,EAAC,oBAAoB,EAAC,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAEnD;;GAEG;AACH,qBAAa,UAAU;IAInB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAEjC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAX1B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAmB;IAEjD,YACmB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,QAAQ,EAChB,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,cAAc,EAC9B,OAAO,EAAE,qBAAqB,EAC9B,gBAAgB,EAAE,kBAAkB,EACrD,oBAAoB,EAAE,oBAAoB,EACzB,OAAO,EAAE,OAAO,GAAG,IAAI,EAczC;IAED,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAE1C;IAED,OAAO,CAAC,WAAW,CAuCjB;IAEF;;OAEG;IACH,OAAO,CAAC,uBAAuB;YAkBjB,iCAAiC;CAkDhD"}
@@ -1,6 +1,6 @@
1
- import { HttpStatusCode } from "@lodestar/api";
2
1
  import { isForkPostGloas } from "@lodestar/params";
3
2
  import { prettyBytes, sleep, toRootHex } from "@lodestar/utils";
3
+ import { ValidatorEvent } from "./emitter.js";
4
4
  import { PtcDutiesService } from "./ptcDuties.js";
5
5
  /**
6
6
  * Service that sets up and handles validator Payload Timeliness Committee duties.
@@ -12,6 +12,7 @@ export class PtcService {
12
12
  clock;
13
13
  validatorStore;
14
14
  emitter;
15
+ chainHeadTracker;
15
16
  metrics;
16
17
  dutiesService;
17
18
  constructor(config, logger, api, clock, validatorStore, emitter, chainHeadTracker, syncingStatusTracker, metrics) {
@@ -21,6 +22,7 @@ export class PtcService {
21
22
  this.clock = clock;
22
23
  this.validatorStore = validatorStore;
23
24
  this.emitter = emitter;
25
+ this.chainHeadTracker = chainHeadTracker;
24
26
  this.metrics = metrics;
25
27
  this.dutiesService = new PtcDutiesService(config, logger, api, clock, validatorStore, chainHeadTracker, syncingStatusTracker, metrics);
26
28
  clock.runEverySlot(this.runPtcTasks);
@@ -38,15 +40,23 @@ export class PtcService {
38
40
  return;
39
41
  }
40
42
  const payloadAttestationDueMs = this.config.getSlotComponentDurationMs(this.config.PAYLOAD_ATTESTATION_DUE_BPS);
41
- await Promise.race([
42
- sleep(payloadAttestationDueMs - this.clock.msFromSlot(slot), signal),
43
- this.emitter.waitForExecutionPayloadAvailableSlot(slot),
44
- ]);
43
+ // Submit as soon as the canonical head block's payload is available, or at the deadline
44
+ const payloadAvailable = new AbortController();
45
+ try {
46
+ await Promise.race([
47
+ sleep(payloadAttestationDueMs - this.clock.msFromSlot(slot), signal),
48
+ this.waitForCanonicalPayload(slot, payloadAvailable.signal),
49
+ ]);
50
+ }
51
+ finally {
52
+ payloadAvailable.abort();
53
+ }
45
54
  this.metrics?.ptcStepCallProducePayloadAttestation.observe(this.clock.secFromSlot(slot) - payloadAttestationDueMs / 1000);
46
55
  try {
47
- const payloadAttestationData = await this.producePayloadAttestationData(slot);
56
+ // No canonical block at slot resolves to `undefined`
57
+ const payloadAttestationData = (await this.api.validator.producePayloadAttestationData({ slot })).value();
48
58
  // If no beacon block was seen for the assigned slot, do not submit a payload attestation
49
- if (payloadAttestationData === null) {
59
+ if (!payloadAttestationData) {
50
60
  this.logger.debug("Skipping payload attestation, no beacon block seen for slot", { slot });
51
61
  return;
52
62
  }
@@ -56,12 +66,21 @@ export class PtcService {
56
66
  this.logger.error("Error on PTC routine", { slot }, e);
57
67
  }
58
68
  };
59
- async producePayloadAttestationData(slot) {
60
- const res = await this.api.validator.producePayloadAttestationData({ slot });
61
- if (!res.ok && res.status === HttpStatusCode.NOT_FOUND) {
62
- return null;
63
- }
64
- return res.value();
69
+ /**
70
+ * Resolve when the payload for the canonical head block at `slot` is available
71
+ */
72
+ waitForCanonicalPayload(slot, signal) {
73
+ return new Promise((resolve) => {
74
+ const onPayloadAvailable = (payload) => {
75
+ const head = this.chainHeadTracker.getCurrentChainHead(slot);
76
+ if (payload.slot === slot && head !== null && payload.blockRoot === toRootHex(head)) {
77
+ this.emitter.off(ValidatorEvent.executionPayloadAvailable, onPayloadAvailable);
78
+ resolve();
79
+ }
80
+ };
81
+ signal.addEventListener("abort", () => this.emitter.off(ValidatorEvent.executionPayloadAvailable, onPayloadAvailable), { once: true });
82
+ this.emitter.on(ValidatorEvent.executionPayloadAvailable, onPayloadAvailable);
83
+ });
65
84
  }
66
85
  async signAndPublishPayloadAttestations(slot, payloadAttestationData, duties) {
67
86
  const payloadAttestationMessages = [];
@@ -1 +1 @@
1
- {"version":3,"file":"ptc.js","sourceRoot":"","sources":["../../src/services/ptc.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ptc.js","sourceRoot":"","sources":["../../src/services/ptc.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lodestar/validator",
3
- "version": "1.45.0-dev.d7859e978b",
3
+ "version": "1.45.0-dev.e6c77a6488",
4
4
  "description": "A Typescript implementation of the validator client",
5
5
  "author": "ChainSafe Systems",
6
6
  "license": "Apache-2.0",
@@ -49,22 +49,22 @@
49
49
  ],
50
50
  "dependencies": {
51
51
  "@chainsafe/blst": "^2.2.0",
52
- "@chainsafe/ssz": "^1.4.0",
53
- "@lodestar/api": "^1.45.0-dev.d7859e978b",
54
- "@lodestar/config": "^1.45.0-dev.d7859e978b",
55
- "@lodestar/db": "^1.45.0-dev.d7859e978b",
56
- "@lodestar/params": "^1.45.0-dev.d7859e978b",
57
- "@lodestar/state-transition": "^1.45.0-dev.d7859e978b",
58
- "@lodestar/types": "^1.45.0-dev.d7859e978b",
59
- "@lodestar/utils": "^1.45.0-dev.d7859e978b",
52
+ "@chainsafe/ssz": "^1.6.2",
53
+ "@lodestar/api": "^1.45.0-dev.e6c77a6488",
54
+ "@lodestar/config": "^1.45.0-dev.e6c77a6488",
55
+ "@lodestar/db": "^1.45.0-dev.e6c77a6488",
56
+ "@lodestar/params": "^1.45.0-dev.e6c77a6488",
57
+ "@lodestar/state-transition": "^1.45.0-dev.e6c77a6488",
58
+ "@lodestar/types": "^1.45.0-dev.e6c77a6488",
59
+ "@lodestar/utils": "^1.45.0-dev.e6c77a6488",
60
60
  "strict-event-emitter-types": "^2.0.0"
61
61
  },
62
62
  "devDependencies": {
63
- "@lodestar/logger": "^1.45.0-dev.d7859e978b",
64
- "@lodestar/spec-test-util": "^1.45.0-dev.d7859e978b",
65
- "@lodestar/test-utils": "^1.45.0-dev.d7859e978b",
63
+ "@lodestar/logger": "^1.45.0-dev.e6c77a6488",
64
+ "@lodestar/spec-test-util": "^1.45.0-dev.e6c77a6488",
65
+ "@lodestar/test-utils": "^1.45.0-dev.e6c77a6488",
66
66
  "@vekexasia/bigint-buffer2": "^1.1.1",
67
67
  "rimraf": "^4.4.1"
68
68
  },
69
- "gitHead": "a084de98990af5e709286b5edc7293db1b100df8"
69
+ "gitHead": "53e801ee0ca938f1c3e3844f2bdb0388b302e948"
70
70
  }
@@ -50,25 +50,4 @@ export class ValidatorEventEmitter extends (EventEmitter as {
50
50
  this.on(ValidatorEvent.chainHead, headListener);
51
51
  });
52
52
  }
53
-
54
- /**
55
- * Wait for the first execution payload availability event to come with slot >= provided slot.
56
- */
57
- async waitForExecutionPayloadAvailableSlot(slot: Slot): Promise<void> {
58
- let payloadListener: (payload: ExecutionPayloadAvailableEventData) => void;
59
-
60
- const onDone = (): void => {
61
- this.off(ValidatorEvent.executionPayloadAvailable, payloadListener);
62
- };
63
-
64
- return new Promise((resolve) => {
65
- payloadListener = (payload): void => {
66
- if (payload.slot >= slot) {
67
- onDone();
68
- resolve();
69
- }
70
- };
71
- this.on(ValidatorEvent.executionPayloadAvailable, payloadListener);
72
- });
73
- }
74
53
  }
@@ -29,7 +29,10 @@ type SubmittedAtEpoch = {dependentRoot: RootHex; slots: Set<Slot>};
29
29
  * dependent root for an epoch shifts (e.g. after a reorg) — detected by comparing the cached
30
30
  * `dependentRoot` reported by `BlockDutiesService` against the one we last submitted under.
31
31
  *
32
- * No-op pre-gloas.
32
+ * Proposers should broadcast their preferences before the fork so the proposer preference caches
33
+ * of beacon nodes and builders are warm for the first Gloas slots. We start submitting
34
+ * as soon as a duty's proposal slot is in Gloas, which is up to `SUBMIT_BEFORE_PROPOSAL_SLOTS`
35
+ * before the fork, so only the first few Gloas slots are affected by this pre-fork submission.
33
36
  */
34
37
  export class ProposerPreferencesService {
35
38
  private readonly submitted = new Map<Epoch, SubmittedAtEpoch>();
@@ -44,10 +47,14 @@ export class ProposerPreferencesService {
44
47
  _metrics: Metrics | null
45
48
  ) {
46
49
  clock.runEverySlot(this.runProposerPreferencesTask);
50
+ clock.runEveryEpoch(this.pruneSubmitted);
47
51
  }
48
52
 
49
53
  private runProposerPreferencesTask = async (slot: Slot): Promise<void> => {
50
- if (!isForkPostGloas(this.config.getForkName(slot))) {
54
+ // Start running once the submission window (`slot + SUBMIT_BEFORE_PROPOSAL_SLOTS`) reaches
55
+ // Gloas, i.e. already in the epoch before the fork. This allows builders to prepare and
56
+ // submit bids for the first Gloas slots.
57
+ if (!isForkPostGloas(this.config.getForkName(slot + SUBMIT_BEFORE_PROPOSAL_SLOTS))) {
51
58
  return;
52
59
  }
53
60
 
@@ -82,6 +89,7 @@ export class ProposerPreferencesService {
82
89
  for (const duty of dutiesAtEpoch.data) {
83
90
  if (duty.slot <= slot) continue;
84
91
  if (duty.slot > slot + SUBMIT_BEFORE_PROPOSAL_SLOTS) continue;
92
+ if (!isForkPostGloas(this.config.getForkName(duty.slot))) continue;
85
93
  if (submission.slots.has(duty.slot)) continue;
86
94
 
87
95
  try {
@@ -110,7 +118,7 @@ export class ProposerPreferencesService {
110
118
  }
111
119
 
112
120
  try {
113
- await this.api.beacon.submitSignedProposerPreferences({signedProposerPreferences: batch});
121
+ await this.api.validator.submitProposerPreferences({signedProposerPreferences: batch});
114
122
  // Only mark as submitted after the API call succeeds; a thrown error leaves the
115
123
  // slot eligible for retry on the next tick.
116
124
  for (const {submission, slot: submittedSlot} of pending) {
@@ -121,4 +129,13 @@ export class ProposerPreferencesService {
121
129
  this.logger.error("Error submitting signed proposer preferences", {count: batch.length}, e as Error);
122
130
  }
123
131
  };
132
+
133
+ /** Drop tracking for past epochs; only currentEpoch and currentEpoch + 1 are ever processed. */
134
+ private pruneSubmitted = async (epoch: Epoch): Promise<void> => {
135
+ for (const trackedEpoch of this.submitted.keys()) {
136
+ if (trackedEpoch < epoch) {
137
+ this.submitted.delete(trackedEpoch);
138
+ }
139
+ }
140
+ };
124
141
  }
@@ -1,4 +1,4 @@
1
- import {ApiClient, HttpStatusCode, routes} from "@lodestar/api";
1
+ import {ApiClient, routes} from "@lodestar/api";
2
2
  import {ChainForkConfig} from "@lodestar/config";
3
3
  import {isForkPostGloas} from "@lodestar/params";
4
4
  import {Slot, gloas} from "@lodestar/types";
@@ -7,7 +7,7 @@ import {Metrics} from "../metrics.js";
7
7
  import {PubkeyHex} from "../types.js";
8
8
  import {IClock, LoggerVc} from "../util/index.js";
9
9
  import {ChainHeaderTracker} from "./chainHeaderTracker.js";
10
- import {ValidatorEventEmitter} from "./emitter.js";
10
+ import {ExecutionPayloadAvailableEventData, ValidatorEvent, ValidatorEventEmitter} from "./emitter.js";
11
11
  import {PtcDutiesService} from "./ptcDuties.js";
12
12
  import {SyncingStatusTracker} from "./syncingStatusTracker.js";
13
13
  import {ValidatorStore} from "./validatorStore.js";
@@ -25,7 +25,7 @@ export class PtcService {
25
25
  private readonly clock: IClock,
26
26
  private readonly validatorStore: ValidatorStore,
27
27
  private readonly emitter: ValidatorEventEmitter,
28
- chainHeadTracker: ChainHeaderTracker,
28
+ private readonly chainHeadTracker: ChainHeaderTracker,
29
29
  syncingStatusTracker: SyncingStatusTracker,
30
30
  private readonly metrics: Metrics | null
31
31
  ) {
@@ -59,19 +59,26 @@ export class PtcService {
59
59
  }
60
60
 
61
61
  const payloadAttestationDueMs = this.config.getSlotComponentDurationMs(this.config.PAYLOAD_ATTESTATION_DUE_BPS);
62
- await Promise.race([
63
- sleep(payloadAttestationDueMs - this.clock.msFromSlot(slot), signal),
64
- this.emitter.waitForExecutionPayloadAvailableSlot(slot),
65
- ]);
62
+ // Submit as soon as the canonical head block's payload is available, or at the deadline
63
+ const payloadAvailable = new AbortController();
64
+ try {
65
+ await Promise.race([
66
+ sleep(payloadAttestationDueMs - this.clock.msFromSlot(slot), signal),
67
+ this.waitForCanonicalPayload(slot, payloadAvailable.signal),
68
+ ]);
69
+ } finally {
70
+ payloadAvailable.abort();
71
+ }
66
72
 
67
73
  this.metrics?.ptcStepCallProducePayloadAttestation.observe(
68
74
  this.clock.secFromSlot(slot) - payloadAttestationDueMs / 1000
69
75
  );
70
76
 
71
77
  try {
72
- const payloadAttestationData = await this.producePayloadAttestationData(slot);
78
+ // No canonical block at slot resolves to `undefined`
79
+ const payloadAttestationData = (await this.api.validator.producePayloadAttestationData({slot})).value();
73
80
  // If no beacon block was seen for the assigned slot, do not submit a payload attestation
74
- if (payloadAttestationData === null) {
81
+ if (!payloadAttestationData) {
75
82
  this.logger.debug("Skipping payload attestation, no beacon block seen for slot", {slot});
76
83
  return;
77
84
  }
@@ -81,12 +88,25 @@ export class PtcService {
81
88
  }
82
89
  };
83
90
 
84
- private async producePayloadAttestationData(slot: Slot): Promise<gloas.PayloadAttestationData | null> {
85
- const res = await this.api.validator.producePayloadAttestationData({slot});
86
- if (!res.ok && res.status === HttpStatusCode.NOT_FOUND) {
87
- return null;
88
- }
89
- return res.value();
91
+ /**
92
+ * Resolve when the payload for the canonical head block at `slot` is available
93
+ */
94
+ private waitForCanonicalPayload(slot: Slot, signal: AbortSignal): Promise<void> {
95
+ return new Promise((resolve) => {
96
+ const onPayloadAvailable = (payload: ExecutionPayloadAvailableEventData): void => {
97
+ const head = this.chainHeadTracker.getCurrentChainHead(slot);
98
+ if (payload.slot === slot && head !== null && payload.blockRoot === toRootHex(head)) {
99
+ this.emitter.off(ValidatorEvent.executionPayloadAvailable, onPayloadAvailable);
100
+ resolve();
101
+ }
102
+ };
103
+ signal.addEventListener(
104
+ "abort",
105
+ () => this.emitter.off(ValidatorEvent.executionPayloadAvailable, onPayloadAvailable),
106
+ {once: true}
107
+ );
108
+ this.emitter.on(ValidatorEvent.executionPayloadAvailable, onPayloadAvailable);
109
+ });
90
110
  }
91
111
 
92
112
  private async signAndPublishPayloadAttestations(