From e895b424cba784ad987d1c2db49e1c30e0432c56 Mon Sep 17 00:00:00 2001 From: Paul Harris Date: Fri, 3 Mar 2023 15:54:18 +1000 Subject: [PATCH] Adds sync committee metrics (#6891) `validator_scheduled_sync_committee_duties_current` This new metric is a visualisation to see how many validators are currently assigned to performing sync committee duties. There were already metrics showing the attestation duties and block production duties, named in the same way, so this was just filling in the gap, although the other duties are accounted in a slightly different way, as they work differently. This new metric is effectively the number of validators that you have that are in a sync committee currently. Also added `validator_current_sync_committee_last_epoch`, which will be the last epoch of the current sync committee. This will allow validator maintainers to better determine if they are in a sync committee currently, and when the sync committee will be changing. Fixes #6270 Signed-off-by: Paul Harris --- CHANGELOG.md | 2 + .../teku/spec/config/configs/prater.yaml | 2 +- .../client/SyncCommitteeDutyLoader.java | 34 +++++++++++++--- .../client/ValidatorClientService.java | 3 +- .../client/SyncCommitteeDutyLoaderTest.java | 39 ++++++++++++++++++- 5 files changed, 71 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15489228fc9..7790f2441ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,8 @@ For information on changes in released versions of Teku, see the [releases page] - Added `/eth/v1/builder/states/{state_id}/expected_withdrawals` rest api endpoint. - Added `/eth/v1/beacon/rewards/sync_committee/{block_id}` rest api endpoint. - Added Capella fork information for Goerli network configuration. +- Added `validator_scheduled_sync_committee_duties_current` metric - the number of validators that you have active with current sync committee duties +- Added `validator_current_sync_committee_last_epoch` metric - the last epoch of the current sync committee. ### Bug Fixes - Included All forks in fork schedule if they're defined in configuration. \ No newline at end of file diff --git a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/prater.yaml b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/prater.yaml index fa4fd46da9a..f70158e6f44 100644 --- a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/prater.yaml +++ b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/prater.yaml @@ -37,7 +37,7 @@ ALTAIR_FORK_EPOCH: 36660 # BELLATRIX BELLATRIX_FORK_VERSION: 0x02001020 BELLATRIX_FORK_EPOCH: 112260 -#CAPELLA +# CAPELLA CAPELLA_FORK_VERSION: 0x03001020 CAPELLA_FORK_EPOCH: 162304 # Sharding diff --git a/validator/client/src/main/java/tech/pegasys/teku/validator/client/SyncCommitteeDutyLoader.java b/validator/client/src/main/java/tech/pegasys/teku/validator/client/SyncCommitteeDutyLoader.java index 9444e8d41a1..4a54e6141bc 100644 --- a/validator/client/src/main/java/tech/pegasys/teku/validator/client/SyncCommitteeDutyLoader.java +++ b/validator/client/src/main/java/tech/pegasys/teku/validator/client/SyncCommitteeDutyLoader.java @@ -15,7 +15,9 @@ import it.unimi.dsi.fastutil.ints.IntCollection; import java.util.Optional; +import org.hyperledger.besu.plugin.services.MetricsSystem; import tech.pegasys.teku.infrastructure.async.SafeFuture; +import tech.pegasys.teku.infrastructure.metrics.TekuMetricCategory; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.validator.api.SyncCommitteeDuties; @@ -33,42 +35,62 @@ public class SyncCommitteeDutyLoader private final ChainHeadTracker chainHeadTracker; private final ForkProvider forkProvider; + private final MetricsSystem metricsSystem; + public SyncCommitteeDutyLoader( final OwnedValidators validators, final ValidatorIndexProvider validatorIndexProvider, final Spec spec, final ValidatorApiChannel validatorApiChannel, final ChainHeadTracker chainHeadTracker, - final ForkProvider forkProvider) { + final ForkProvider forkProvider, + final MetricsSystem metricsSystem) { super(validators, validatorIndexProvider); this.spec = spec; this.validatorApiChannel = validatorApiChannel; this.chainHeadTracker = chainHeadTracker; this.forkProvider = forkProvider; + this.metricsSystem = metricsSystem; } @Override protected SafeFuture> requestDuties( final UInt64 epoch, final IntCollection validatorIndices) { - return validatorApiChannel.getSyncCommitteeDuties(epoch, validatorIndices); + return validatorApiChannel + .getSyncCommitteeDuties(epoch, validatorIndices) + .thenPeek( + maybeDuties -> { + metricsSystem.createIntegerGauge( + TekuMetricCategory.VALIDATOR, + "scheduled_sync_committee_duties_current", + "Current number of Sync committee members performing duties", + () -> maybeDuties.map(d -> d.getDuties().size()).orElse(0)); + }); } @Override protected SafeFuture scheduleAllDuties( final UInt64 epoch, final SyncCommitteeDuties duties) { + final UInt64 lastEpochInCommitteePeriod = + spec.getSyncCommitteeUtilRequired(spec.computeStartSlotAtEpoch(epoch)) + .computeFirstEpochOfNextSyncCommitteePeriod(epoch) + .minusMinZero(1); final SyncCommitteeScheduledDuties.Builder dutyBuilder = SyncCommitteeScheduledDuties.builder() .forkProvider(forkProvider) .validatorApiChannel(validatorApiChannel) .chainHeadTracker(chainHeadTracker) .spec(spec) - .lastEpochInCommitteePeriod( - spec.getSyncCommitteeUtilRequired(spec.computeStartSlotAtEpoch(epoch)) - .computeFirstEpochOfNextSyncCommitteePeriod(epoch) - .minusMinZero(1)); + .lastEpochInCommitteePeriod(lastEpochInCommitteePeriod); duties.getDuties().forEach(duty -> scheduleDuty(dutyBuilder, duty)); final SyncCommitteeScheduledDuties scheduledDuties = dutyBuilder.build(); scheduledDuties.subscribeToSubnets(); + + metricsSystem.createIntegerGauge( + TekuMetricCategory.VALIDATOR, + "current_sync_committee_last_epoch", + "The final epoch of the current sync committee period", + lastEpochInCommitteePeriod::intValue); return SafeFuture.completedFuture(scheduledDuties); } diff --git a/validator/client/src/main/java/tech/pegasys/teku/validator/client/ValidatorClientService.java b/validator/client/src/main/java/tech/pegasys/teku/validator/client/ValidatorClientService.java index 7063db8c335..47ba5fa1326 100644 --- a/validator/client/src/main/java/tech/pegasys/teku/validator/client/ValidatorClientService.java +++ b/validator/client/src/main/java/tech/pegasys/teku/validator/client/ValidatorClientService.java @@ -404,7 +404,8 @@ private void scheduleValidatorsDuties( spec, validatorApiChannel, chainHeadTracker, - forkProvider)); + forkProvider, + metricsSystem)); validatorTimingChannels.add( new SyncCommitteeScheduler( metricsSystem, spec, syncCommitteeDutyLoader, new Random()::nextInt)); diff --git a/validator/client/src/test/java/tech/pegasys/teku/validator/client/SyncCommitteeDutyLoaderTest.java b/validator/client/src/test/java/tech/pegasys/teku/validator/client/SyncCommitteeDutyLoaderTest.java index c2de7539bbc..2a3c7f55aae 100644 --- a/validator/client/src/test/java/tech/pegasys/teku/validator/client/SyncCommitteeDutyLoaderTest.java +++ b/validator/client/src/test/java/tech/pegasys/teku/validator/client/SyncCommitteeDutyLoaderTest.java @@ -28,6 +28,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import tech.pegasys.teku.infrastructure.async.SafeFuture; +import tech.pegasys.teku.infrastructure.metrics.StubMetricsSystem; +import tech.pegasys.teku.infrastructure.metrics.TekuMetricCategory; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.TestSpecFactory; @@ -60,6 +62,8 @@ class SyncCommitteeDutyLoaderTest { private final ChainHeadTracker chainHeadTracker = mock(ChainHeadTracker.class); private final ForkProvider forkProvider = mock(ForkProvider.class); + private final StubMetricsSystem metricsSystem = new StubMetricsSystem(); + private final SyncCommitteeDutyLoader dutyLoader = new SyncCommitteeDutyLoader( validators, @@ -67,7 +71,8 @@ class SyncCommitteeDutyLoaderTest { spec, validatorApiChannel, chainHeadTracker, - forkProvider); + forkProvider, + metricsSystem); @BeforeEach void setUp() { @@ -109,6 +114,38 @@ void shouldRetrieveDuties() { validator1Index, IntSet.of(1, 6, 25), untilEpoch.increment()), new SyncCommitteeSubnetSubscription( validator2Index, IntSet.of(7, 50, 38), untilEpoch.increment()))); + assertThat( + metricsSystem + .getGauge(TekuMetricCategory.VALIDATOR, "scheduled_sync_committee_duties_current") + .getValue()) + .isEqualTo(2.0); + + assertThat( + metricsSystem + .getGauge(TekuMetricCategory.VALIDATOR, "current_sync_committee_last_epoch") + .getValue()) + .isEqualTo(63.0); + } + + @Test + void shouldGetCountOfValidatorsInSyncCommitteeThroughMetrics() { + final UInt64 epoch = UInt64.valueOf(56); + when(validatorApiChannel.getSyncCommitteeDuties(epoch, validatorIndices)) + .thenReturn( + SafeFuture.completedFuture(Optional.of(new SyncCommitteeDuties(false, List.of())))); + final SyncCommitteeScheduledDuties duties = loadDuties(epoch); + assertThat(duties.countDuties()).isEqualTo(0); + assertThat( + metricsSystem + .getGauge(TekuMetricCategory.VALIDATOR, "scheduled_sync_committee_duties_current") + .getValue()) + .isEqualTo(0.0); + + assertThat( + metricsSystem + .getGauge(TekuMetricCategory.VALIDATOR, "current_sync_committee_last_epoch") + .getValue()) + .isEqualTo(63.0); } private SyncCommitteeScheduledDuties loadDuties(final UInt64 epoch) {