diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandler.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandler.java index 04648dd04fb..91515842187 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandler.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandler.java @@ -586,13 +586,20 @@ public SafeFuture> sendSignedAttestations( } private SafeFuture processAttestation(final Attestation attestation) { + final ValidatableAttestation validatableAttestation = + ValidatableAttestation.fromValidator(spec, attestation); return attestationManager - .addAttestation(ValidatableAttestation.fromValidator(spec, attestation), Optional.empty()) + .addAttestation(validatableAttestation, Optional.empty()) .thenPeek( result -> { if (!result.isReject()) { - dutyMetrics.onAttestationPublished(attestation.getData().getSlot()); - performanceTracker.saveProducedAttestation(attestation); + // When saving the attestation in performance tracker, we want to make sure we save + // the converted attestation. + // The conversion happens during processing and is saved in the validatable + // attestation. + final Attestation convertedAttestation = validatableAttestation.getAttestation(); + dutyMetrics.onAttestationPublished(convertedAttestation.getData().getSlot()); + performanceTracker.saveProducedAttestation(convertedAttestation); } else { VALIDATOR_LOGGER.producedInvalidAttestation( attestation.getData().getSlot(), diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/performance/DefaultPerformanceTracker.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/performance/DefaultPerformanceTracker.java index 381229ffca5..8031f4f0a30 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/performance/DefaultPerformanceTracker.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/performance/DefaultPerformanceTracker.java @@ -14,6 +14,7 @@ package tech.pegasys.teku.validator.coordinator.performance; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; import com.google.common.annotations.VisibleForTesting; import it.unimi.dsi.fastutil.ints.Int2IntMap; @@ -424,6 +425,7 @@ private SafeFuture>> getAttestationsIncludedInEpoc @Override public void saveProducedAttestation(final Attestation attestation) { + checkState(!attestation.isSingleAttestation(), "Single attestation is not supported"); final UInt64 epoch = spec.computeEpochAtSlot(attestation.getData().getSlot()); final Set attestationsInEpoch = producedAttestationsByEpoch.computeIfAbsent(epoch, __ -> concurrentSet()); diff --git a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandlerTest.java b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandlerTest.java index d882571c41d..770ecbad932 100644 --- a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandlerTest.java +++ b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/ValidatorApiHandlerTest.java @@ -819,6 +819,53 @@ public void sendSignedAttestations_shouldAddAttestationToAttestationManager() { .addAttestation(ValidatableAttestation.from(spec, attestation), Optional.empty()); } + @Test + void sendSignedAttestations_shouldSaveConvertedAttestationFromSingleAttestation() { + spec = TestSpecFactory.createMinimalElectra(); + dataStructureUtil = new DataStructureUtil(spec); + validatorApiHandler = + new ValidatorApiHandler( + chainDataProvider, + nodeDataProvider, + networkDataProvider, + chainDataClient, + syncStateProvider, + blockFactory, + attestationPool, + attestationManager, + attestationTopicSubscriptions, + activeValidatorTracker, + dutyMetrics, + performanceTracker, + spec, + forkChoiceTrigger, + proposersDataManager, + syncCommitteeMessagePool, + syncCommitteeContributionPool, + syncCommitteeSubscriptionManager, + blockProductionPerformanceFactory, + blockPublisher); + + final Attestation attestation = dataStructureUtil.randomSingleAttestation(); + final Attestation convertedAttestation = dataStructureUtil.randomAttestation(); + doAnswer( + invocation -> { + invocation + .getArgument(0, ValidatableAttestation.class) + .convertToAggregatedFormatFromSingleAttestation(convertedAttestation); + return completedFuture(InternalValidationResult.ACCEPT); + }) + .when(attestationManager) + .addAttestation(any(ValidatableAttestation.class), any()); + + final SafeFuture> result = + validatorApiHandler.sendSignedAttestations(List.of(attestation)); + assertThat(result).isCompletedWithValue(emptyList()); + + verify(dutyMetrics).onAttestationPublished(convertedAttestation.getData().getSlot()); + verify(performanceTracker).saveProducedAttestation(convertedAttestation); + } + @Test void sendSignedAttestations_shouldAddToDutyMetricsAndPerformanceTrackerWhenNotInvalid() { final Attestation attestation = dataStructureUtil.randomAttestation();