From 496f3c964fe2b6dfbbf2d10c548b1b4f479d66ab Mon Sep 17 00:00:00 2001 From: Mehdi AOUADI Date: Wed, 10 Jul 2024 17:21:10 +0200 Subject: [PATCH 1/2] add retrive attestations and metadata --- .../tech/pegasys/teku/api/DataProvider.java | 3 +- .../pegasys/teku/api/NodeDataProvider.java | 24 +++++- .../teku/api/NodeDataProviderTest.java | 80 ++++++++++++++++++- 3 files changed, 104 insertions(+), 3 deletions(-) diff --git a/data/provider/src/main/java/tech/pegasys/teku/api/DataProvider.java b/data/provider/src/main/java/tech/pegasys/teku/api/DataProvider.java index 7ea6bf77856..7b1440b1063 100644 --- a/data/provider/src/main/java/tech/pegasys/teku/api/DataProvider.java +++ b/data/provider/src/main/java/tech/pegasys/teku/api/DataProvider.java @@ -233,7 +233,8 @@ public DataProvider build() { isLivenessTrackingEnabled, activeValidatorChannel, proposersDataManager, - forkChoiceNotifier); + forkChoiceNotifier, + spec); final ChainDataProvider chainDataProvider = new ChainDataProvider(spec, recentChainData, combinedChainDataClient, rewardCalculator); final SyncDataProvider syncDataProvider = diff --git a/data/provider/src/main/java/tech/pegasys/teku/api/NodeDataProvider.java b/data/provider/src/main/java/tech/pegasys/teku/api/NodeDataProvider.java index cd33ea4cdd1..fb0c4b56278 100644 --- a/data/provider/src/main/java/tech/pegasys/teku/api/NodeDataProvider.java +++ b/data/provider/src/main/java/tech/pegasys/teku/api/NodeDataProvider.java @@ -15,6 +15,7 @@ import static tech.pegasys.teku.statetransition.validatorcache.ActiveValidatorCache.TRACKED_EPOCHS; +import com.google.common.base.Preconditions; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -26,7 +27,9 @@ import tech.pegasys.teku.api.migrated.ValidatorLivenessAtEpoch; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.datastructures.attestation.ProcessedAttestationListener; +import tech.pegasys.teku.spec.datastructures.metadata.ObjectAndMetaData; import tech.pegasys.teku.spec.datastructures.operations.Attestation; import tech.pegasys.teku.spec.datastructures.operations.AttesterSlashing; import tech.pegasys.teku.spec.datastructures.operations.ProposerSlashing; @@ -63,6 +66,7 @@ public class NodeDataProvider { private final boolean isLivenessTrackingEnabled; private final ProposersDataManager proposersDataManager; private final ForkChoiceNotifier forkChoiceNotifier; + private final Spec spec; public NodeDataProvider( final AggregatingAttestationPool attestationPool, @@ -76,7 +80,8 @@ public NodeDataProvider( final boolean isLivenessTrackingEnabled, final ActiveValidatorChannel activeValidatorChannel, final ProposersDataManager proposersDataManager, - final ForkChoiceNotifier forkChoiceNotifier) { + final ForkChoiceNotifier forkChoiceNotifier, + final Spec spec) { this.attestationPool = attestationPool; this.attesterSlashingPool = attesterSlashingsPool; this.proposerSlashingPool = proposerSlashingPool; @@ -89,6 +94,7 @@ public NodeDataProvider( this.isLivenessTrackingEnabled = isLivenessTrackingEnabled; this.proposersDataManager = proposersDataManager; this.forkChoiceNotifier = forkChoiceNotifier; + this.spec = spec; } public List getAttestations( @@ -96,6 +102,22 @@ public List getAttestations( return attestationPool.getAttestations(maybeSlot, maybeCommitteeIndex); } + public ObjectAndMetaData> getAttestationsAndMetaData( + final Optional maybeSlot, final Optional maybeCommitteeIndex) { + return lookupMetaData( + attestationPool.getAttestations(maybeSlot, maybeCommitteeIndex), maybeSlot); + } + + private ObjectAndMetaData> lookupMetaData( + final List attestations, final Optional maybeSlot) { + Preconditions.checkArgument( + !attestations.isEmpty() || maybeSlot.isPresent(), + "Unable to determine spec version. No slot parameter provided and no attestations found"); + final UInt64 slot = maybeSlot.orElse(attestations.get(0).getData().getSlot()); + return new ObjectAndMetaData<>( + attestations, spec.atSlot(slot).getMilestone(), false, false, false); + } + public List getAttesterSlashings() { return new ArrayList<>(attesterSlashingPool.getAll()); } diff --git a/data/provider/src/test/java/tech/pegasys/teku/api/NodeDataProviderTest.java b/data/provider/src/test/java/tech/pegasys/teku/api/NodeDataProviderTest.java index a43ee09a500..e0449e7c8e2 100644 --- a/data/provider/src/test/java/tech/pegasys/teku/api/NodeDataProviderTest.java +++ b/data/provider/src/test/java/tech/pegasys/teku/api/NodeDataProviderTest.java @@ -14,17 +14,24 @@ package tech.pegasys.teku.api; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.concurrent.ExecutionException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.SpecVersion; import tech.pegasys.teku.spec.TestSpecFactory; import tech.pegasys.teku.spec.datastructures.operations.AttesterSlashing; import tech.pegasys.teku.spec.datastructures.operations.ProposerSlashing; @@ -82,7 +89,8 @@ public void setup() { false, validatorChannel, proposersDataManager, - forkChoiceNotifier); + forkChoiceNotifier, + spec); } @Test @@ -113,4 +121,74 @@ void blsToExecutionChanges_ReturnsListOfErrors() throws ExecutionException, Inte assertThat(future.get()) .isEqualTo(List.of(new SubmitDataError(UInt64.ONE, "Computer says no"))); } + + @Test + void attestationsMetaDataLookUp_ThrowsWhenNoSlotAndNoAttestations() { + when(attestationPool.getAttestations(any(), any())).thenReturn(Collections.emptyList()); + assertThatThrownBy( + () -> provider.getAttestationsAndMetaData(Optional.empty(), Optional.empty())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining( + "Unable to determine spec version. No slot parameter provided and no attestations found"); + } + + @Test + void attestationsMetaDataLookUp_UseFirstAttestationSlot_WhenSlotParamNotProvided() { + final Spec specMock = mock(Spec.class); + final SpecVersion specVersionMock = mock(SpecVersion.class); + final SpecMilestone specMilestone = mock(SpecMilestone.class); + when(specVersionMock.getMilestone()).thenReturn(specMilestone); + when(specMock.atSlot(any())).thenReturn(specVersionMock); + provider = + new NodeDataProvider( + attestationPool, + attesterSlashingPool, + proposerSlashingPool, + voluntaryExitPool, + blsToExecutionChangePool, + syncCommitteeContributionPool, + blockBlobSidecarsTrackersPool, + attestationManager, + false, + validatorChannel, + proposersDataManager, + forkChoiceNotifier, + specMock); + when(attestationPool.getAttestations(any(), any())) + .thenReturn( + List.of( + dataStructureUtil.randomAttestation(5), dataStructureUtil.randomAttestation(10))); + provider.getAttestationsAndMetaData(Optional.empty(), Optional.empty()); + verify(specMock).atSlot(eq(UInt64.valueOf(5))); + } + + @Test + void attestationsMetaDataLookUp_UseSlot_WhenSlotParamNotProvided() { + final Spec specMock = mock(Spec.class); + final SpecVersion specVersionMock = mock(SpecVersion.class); + final SpecMilestone specMilestone = mock(SpecMilestone.class); + when(specVersionMock.getMilestone()).thenReturn(specMilestone); + when(specMock.atSlot(any())).thenReturn(specVersionMock); + provider = + new NodeDataProvider( + attestationPool, + attesterSlashingPool, + proposerSlashingPool, + voluntaryExitPool, + blsToExecutionChangePool, + syncCommitteeContributionPool, + blockBlobSidecarsTrackersPool, + attestationManager, + false, + validatorChannel, + proposersDataManager, + forkChoiceNotifier, + specMock); + when(attestationPool.getAttestations(any(), any())) + .thenReturn( + List.of( + dataStructureUtil.randomAttestation(5), dataStructureUtil.randomAttestation(10))); + provider.getAttestationsAndMetaData(Optional.of(UInt64.valueOf(8)), Optional.empty()); + verify(specMock).atSlot(eq(UInt64.valueOf(8))); + } } From a924a5cfaadc2352311e546a761a0b84ba74d631 Mon Sep 17 00:00:00 2001 From: Mehdi AOUADI Date: Mon, 22 Jul 2024 11:04:43 +0200 Subject: [PATCH 2/2] use current slot --- .../tech/pegasys/teku/api/DataProvider.java | 1 + .../pegasys/teku/api/NodeDataProvider.java | 19 +++-- .../teku/api/NodeDataProviderTest.java | 72 +++++++++---------- 3 files changed, 50 insertions(+), 42 deletions(-) diff --git a/data/provider/src/main/java/tech/pegasys/teku/api/DataProvider.java b/data/provider/src/main/java/tech/pegasys/teku/api/DataProvider.java index 7b1440b1063..9fa42ea52c3 100644 --- a/data/provider/src/main/java/tech/pegasys/teku/api/DataProvider.java +++ b/data/provider/src/main/java/tech/pegasys/teku/api/DataProvider.java @@ -234,6 +234,7 @@ public DataProvider build() { activeValidatorChannel, proposersDataManager, forkChoiceNotifier, + recentChainData, spec); final ChainDataProvider chainDataProvider = new ChainDataProvider(spec, recentChainData, combinedChainDataClient, rewardCalculator); diff --git a/data/provider/src/main/java/tech/pegasys/teku/api/NodeDataProvider.java b/data/provider/src/main/java/tech/pegasys/teku/api/NodeDataProvider.java index fb0c4b56278..220807bb76d 100644 --- a/data/provider/src/main/java/tech/pegasys/teku/api/NodeDataProvider.java +++ b/data/provider/src/main/java/tech/pegasys/teku/api/NodeDataProvider.java @@ -15,7 +15,6 @@ import static tech.pegasys.teku.statetransition.validatorcache.ActiveValidatorCache.TRACKED_EPOCHS; -import com.google.common.base.Preconditions; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -50,6 +49,7 @@ import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeContributionPool; import tech.pegasys.teku.statetransition.validation.InternalValidationResult; import tech.pegasys.teku.statetransition.validatorcache.ActiveValidatorChannel; +import tech.pegasys.teku.storage.client.RecentChainData; import tech.pegasys.teku.validator.api.SubmitDataError; public class NodeDataProvider { @@ -66,6 +66,7 @@ public class NodeDataProvider { private final boolean isLivenessTrackingEnabled; private final ProposersDataManager proposersDataManager; private final ForkChoiceNotifier forkChoiceNotifier; + private final RecentChainData recentChainData; private final Spec spec; public NodeDataProvider( @@ -81,6 +82,7 @@ public NodeDataProvider( final ActiveValidatorChannel activeValidatorChannel, final ProposersDataManager proposersDataManager, final ForkChoiceNotifier forkChoiceNotifier, + final RecentChainData recentChainData, final Spec spec) { this.attestationPool = attestationPool; this.attesterSlashingPool = attesterSlashingsPool; @@ -94,6 +96,7 @@ public NodeDataProvider( this.isLivenessTrackingEnabled = isLivenessTrackingEnabled; this.proposersDataManager = proposersDataManager; this.forkChoiceNotifier = forkChoiceNotifier; + this.recentChainData = recentChainData; this.spec = spec; } @@ -110,14 +113,20 @@ public ObjectAndMetaData> getAttestationsAndMetaData( private ObjectAndMetaData> lookupMetaData( final List attestations, final Optional maybeSlot) { - Preconditions.checkArgument( - !attestations.isEmpty() || maybeSlot.isPresent(), - "Unable to determine spec version. No slot parameter provided and no attestations found"); - final UInt64 slot = maybeSlot.orElse(attestations.get(0).getData().getSlot()); + final UInt64 slot = getSlot(attestations, maybeSlot); return new ObjectAndMetaData<>( attestations, spec.atSlot(slot).getMilestone(), false, false, false); } + private UInt64 getSlot(final List attestations, final Optional maybeSlot) { + return maybeSlot.orElseGet( + () -> + attestations.stream() + .findFirst() + .map(attestation -> attestation.getData().getSlot()) + .orElseGet(() -> recentChainData.getCurrentSlot().orElse(UInt64.ZERO))); + } + public List getAttesterSlashings() { return new ArrayList<>(attesterSlashingPool.getAll()); } diff --git a/data/provider/src/test/java/tech/pegasys/teku/api/NodeDataProviderTest.java b/data/provider/src/test/java/tech/pegasys/teku/api/NodeDataProviderTest.java index e0449e7c8e2..051cfb5617c 100644 --- a/data/provider/src/test/java/tech/pegasys/teku/api/NodeDataProviderTest.java +++ b/data/provider/src/test/java/tech/pegasys/teku/api/NodeDataProviderTest.java @@ -14,7 +14,6 @@ package tech.pegasys.teku.api; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -47,6 +46,7 @@ import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeContributionPool; import tech.pegasys.teku.statetransition.validation.InternalValidationResult; import tech.pegasys.teku.statetransition.validatorcache.ActiveValidatorChannel; +import tech.pegasys.teku.storage.client.RecentChainData; import tech.pegasys.teku.validator.api.SubmitDataError; @SuppressWarnings("unchecked") @@ -60,6 +60,7 @@ public class NodeDataProviderTest { private final ActiveValidatorChannel validatorChannel = mock(ActiveValidatorChannel.class); private final ProposersDataManager proposersDataManager = mock(ProposersDataManager.class); private final ForkChoiceNotifier forkChoiceNotifier = mock(ForkChoiceNotifier.class); + private final RecentChainData recentChainData = mock(RecentChainData.class); private final OperationPool attesterSlashingPool = mock(OperationPool.class); @@ -90,6 +91,7 @@ public void setup() { validatorChannel, proposersDataManager, forkChoiceNotifier, + recentChainData, spec); } @@ -123,47 +125,47 @@ void blsToExecutionChanges_ReturnsListOfErrors() throws ExecutionException, Inte } @Test - void attestationsMetaDataLookUp_ThrowsWhenNoSlotAndNoAttestations() { - when(attestationPool.getAttestations(any(), any())).thenReturn(Collections.emptyList()); - assertThatThrownBy( - () -> provider.getAttestationsAndMetaData(Optional.empty(), Optional.empty())) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining( - "Unable to determine spec version. No slot parameter provided and no attestations found"); + void attestationsMetaDataLookUp_UseFirstAttestationSlot_WhenSlotParamNotProvided() { + final Spec specMock = setUpMockedSpec(); + when(attestationPool.getAttestations(any(), any())) + .thenReturn( + List.of( + dataStructureUtil.randomAttestation(5), dataStructureUtil.randomAttestation(10))); + provider.getAttestationsAndMetaData(Optional.empty(), Optional.empty()); + verify(specMock).atSlot(eq(UInt64.valueOf(5))); } @Test - void attestationsMetaDataLookUp_UseFirstAttestationSlot_WhenSlotParamNotProvided() { - final Spec specMock = mock(Spec.class); - final SpecVersion specVersionMock = mock(SpecVersion.class); - final SpecMilestone specMilestone = mock(SpecMilestone.class); - when(specVersionMock.getMilestone()).thenReturn(specMilestone); - when(specMock.atSlot(any())).thenReturn(specVersionMock); - provider = - new NodeDataProvider( - attestationPool, - attesterSlashingPool, - proposerSlashingPool, - voluntaryExitPool, - blsToExecutionChangePool, - syncCommitteeContributionPool, - blockBlobSidecarsTrackersPool, - attestationManager, - false, - validatorChannel, - proposersDataManager, - forkChoiceNotifier, - specMock); + void attestationsMetaDataLookUp_UseSlot_WhenSlotParamProvided() { + final Spec specMock = setUpMockedSpec(); when(attestationPool.getAttestations(any(), any())) .thenReturn( List.of( dataStructureUtil.randomAttestation(5), dataStructureUtil.randomAttestation(10))); + provider.getAttestationsAndMetaData(Optional.of(UInt64.valueOf(8)), Optional.empty()); + verify(specMock).atSlot(eq(UInt64.valueOf(8))); + } + + @Test + void attestationsMetaDataLookUp_UseCurrentSlot_WhenSlotParamNotProvided_EmptyList() { + final Spec specMock = setUpMockedSpec(); + when(attestationPool.getAttestations(any(), any())).thenReturn(Collections.emptyList()); + final UInt64 currentSlot = UInt64.valueOf(8); + when(recentChainData.getCurrentSlot()).thenReturn(Optional.of(currentSlot)); provider.getAttestationsAndMetaData(Optional.empty(), Optional.empty()); - verify(specMock).atSlot(eq(UInt64.valueOf(5))); + verify(specMock).atSlot(eq(currentSlot)); } @Test - void attestationsMetaDataLookUp_UseSlot_WhenSlotParamNotProvided() { + void attestationsMetaDataLookUp_UseSlotZero_WhenSlotParamNotProvided_EmptyList_NoCurrentSlot() { + final Spec specMock = setUpMockedSpec(); + when(attestationPool.getAttestations(any(), any())).thenReturn(Collections.emptyList()); + when(recentChainData.getCurrentSlot()).thenReturn(Optional.empty()); + provider.getAttestationsAndMetaData(Optional.empty(), Optional.empty()); + verify(specMock).atSlot(eq(UInt64.ZERO)); + } + + private Spec setUpMockedSpec() { final Spec specMock = mock(Spec.class); final SpecVersion specVersionMock = mock(SpecVersion.class); final SpecMilestone specMilestone = mock(SpecMilestone.class); @@ -183,12 +185,8 @@ void attestationsMetaDataLookUp_UseSlot_WhenSlotParamNotProvided() { validatorChannel, proposersDataManager, forkChoiceNotifier, + recentChainData, specMock); - when(attestationPool.getAttestations(any(), any())) - .thenReturn( - List.of( - dataStructureUtil.randomAttestation(5), dataStructureUtil.randomAttestation(10))); - provider.getAttestationsAndMetaData(Optional.of(UInt64.valueOf(8)), Optional.empty()); - verify(specMock).atSlot(eq(UInt64.valueOf(8))); + return specMock; } }