diff --git a/packages/validator/src/metrics.ts b/packages/validator/src/metrics.ts index e1890289bd36..4ae4724fb1c1 100644 --- a/packages/validator/src/metrics.ts +++ b/packages/validator/src/metrics.ts @@ -206,6 +206,14 @@ export function getMetrics(register: MetricsRegister, gitData: LodestarGitData) help: "Total count of instances the attester duties dependant root changed", }), + attesterDutiesNextSlot: register.gauge({ + // Metric is used by Rocket Pool dashboard (18391) to determine seconds until next attestation. + // It works without requiring any modification to the dashboard as the metric name is the + // same as Lighthouse uses for this. + name: "vc_attestation_duty_slot", + help: "Slot of next scheduled attestation duty", + }), + // BlockProposingService blocksProduced: register.gauge({ diff --git a/packages/validator/src/services/attestationDuties.ts b/packages/validator/src/services/attestationDuties.ts index 0100ca45568e..6c9e12b3fafe 100644 --- a/packages/validator/src/services/attestationDuties.ts +++ b/packages/validator/src/services/attestationDuties.ts @@ -63,12 +63,25 @@ export class AttestationDutiesService { if (metrics) { metrics.attesterDutiesCount.addCollect(() => { + const currentSlot = this.clock.getCurrentSlot(); let duties = 0; - for (const attDutiesAtEpoch of this.dutiesByIndexByEpoch.values()) { + let nextDutySlot = null; + for (const [epoch, attDutiesAtEpoch] of this.dutiesByIndexByEpoch) { duties += attDutiesAtEpoch.dutiesByIndex.size; + + // Epochs are sorted, stop searching once a next duty slot is found + if (epoch < this.clock.currentEpoch || nextDutySlot !== null) continue; + + for (const {duty} of attDutiesAtEpoch.dutiesByIndex.values()) { + // Set next duty slot to the closest future slot found in all duties + if (duty.slot > currentSlot && (nextDutySlot === null || duty.slot < nextDutySlot)) { + nextDutySlot = duty.slot; + } + } } metrics.attesterDutiesCount.set(duties); metrics.attesterDutiesEpochCount.set(this.dutiesByIndexByEpoch.size); + if (nextDutySlot !== null) metrics.attesterDutiesNextSlot.set(nextDutySlot); }); } }