From 37c3683a408fb52701ca15ab91a3fd6d9f1835d4 Mon Sep 17 00:00:00 2001 From: Mehdi AOUADI Date: Tue, 5 Nov 2024 15:39:34 +0100 Subject: [PATCH] update blobs rpc methods --- .../rpc/beaconchain/BeaconChainMethods.java | 11 +- .../BlobSidecarsByRangeMessageHandler.java | 46 +++++--- .../BlobSidecarsByRootMessageHandler.java | 14 ++- ...BlobSidecarsByRangeMessageHandlerTest.java | 101 ++++++++--------- .../BlobSidecarsByRootMessageHandlerTest.java | 104 +++++++++++------- 5 files changed, 157 insertions(+), 119 deletions(-) diff --git a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/BeaconChainMethods.java b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/BeaconChainMethods.java index 631730f8ef9..63ba993db5f 100644 --- a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/BeaconChainMethods.java +++ b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/BeaconChainMethods.java @@ -42,7 +42,6 @@ import tech.pegasys.teku.networking.p2p.rpc.RpcMethod; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.SpecMilestone; -import tech.pegasys.teku.spec.config.SpecConfigDeneb; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.datastructures.networking.libp2p.rpc.BeaconBlocksByRangeRequestMessage; @@ -284,8 +283,7 @@ private static Eth2RpcMethod createGoodBye( RpcContextCodec.forkDigest(spec, recentChainData, ForkDigestPayloadContext.BLOB_SIDECAR); final BlobSidecarsByRootMessageHandler blobSidecarsByRootHandler = - new BlobSidecarsByRootMessageHandler( - spec, getSpecConfigDeneb(spec), metricsSystem, combinedChainDataClient); + new BlobSidecarsByRootMessageHandler(spec, metricsSystem, combinedChainDataClient); final BlobSidecarsByRootRequestMessageSchema blobSidecarsByRootRequestMessageSchema = SchemaDefinitionsDeneb.required( spec.forMilestone(SpecMilestone.DENEB).getSchemaDefinitions()) @@ -326,8 +324,7 @@ private static Eth2RpcMethod createGoodBye( RpcContextCodec.forkDigest(spec, recentChainData, ForkDigestPayloadContext.BLOB_SIDECAR); final BlobSidecarsByRangeMessageHandler blobSidecarsByRangeHandler = - new BlobSidecarsByRangeMessageHandler( - spec, getSpecConfigDeneb(spec), metricsSystem, combinedChainDataClient); + new BlobSidecarsByRangeMessageHandler(spec, metricsSystem, combinedChainDataClient); return Optional.of( new SingleProtocolEth2RpcMethod<>( @@ -426,10 +423,6 @@ private static Eth2RpcMethod createPing( spec.getNetworkingConfig()); } - private static SpecConfigDeneb getSpecConfigDeneb(final Spec spec) { - return SpecConfigDeneb.required(spec.forMilestone(SpecMilestone.DENEB).getConfig()); - } - public Collection> all() { return Collections.unmodifiableCollection(allMethods); } diff --git a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeMessageHandler.java b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeMessageHandler.java index 4012667c728..ca4a7b7c0c7 100644 --- a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeMessageHandler.java +++ b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeMessageHandler.java @@ -41,10 +41,11 @@ import tech.pegasys.teku.networking.eth2.rpc.core.RpcException.ResourceUnavailableException; import tech.pegasys.teku.networking.p2p.rpc.StreamClosedException; import tech.pegasys.teku.spec.Spec; -import tech.pegasys.teku.spec.config.SpecConfigDeneb; +import tech.pegasys.teku.spec.SpecMilestone; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.networking.libp2p.rpc.BlobSidecarsByRangeRequestMessage; import tech.pegasys.teku.spec.datastructures.util.SlotAndBlockRootAndBlobIndex; +import tech.pegasys.teku.spec.logic.common.helpers.MiscHelpers; import tech.pegasys.teku.storage.client.CombinedChainDataClient; /** @@ -58,18 +59,15 @@ public class BlobSidecarsByRangeMessageHandler private static final Logger LOG = LogManager.getLogger(); private final Spec spec; - private final SpecConfigDeneb specConfigDeneb; private final CombinedChainDataClient combinedChainDataClient; private final LabelledMetric requestCounter; private final Counter totalBlobSidecarsRequestedCounter; public BlobSidecarsByRangeMessageHandler( final Spec spec, - final SpecConfigDeneb specConfigDeneb, final MetricsSystem metricsSystem, final CombinedChainDataClient combinedChainDataClient) { this.spec = spec; - this.specConfigDeneb = specConfigDeneb; this.combinedChainDataClient = combinedChainDataClient; requestCounter = metricsSystem.createLabelledCounter( @@ -88,16 +86,23 @@ public BlobSidecarsByRangeMessageHandler( public Optional validateRequest( final String protocolId, final BlobSidecarsByRangeRequestMessage request) { - final long requestedCount = calculateRequestedCount(request); + final SpecMilestone latestMilestoneRequested = + spec.getForkSchedule().getSpecMilestoneAtSlot(request.getMaxSlot()); + final MiscHelpers miscHelpers = spec.forMilestone(latestMilestoneRequested).miscHelpers(); - if (requestedCount > specConfigDeneb.getMaxRequestBlobSidecars()) { + final int maxRequestBlobSidecars = miscHelpers.getMaxRequestBlobSidecars(); + final int maxBlobsPerBlock = miscHelpers.getMaxBlobsPerBlock(); + + final long requestedCount = calculateRequestedCount(request, maxBlobsPerBlock); + + if (requestedCount > maxRequestBlobSidecars) { requestCounter.labels("count_too_big").inc(); return Optional.of( new RpcException( INVALID_REQUEST_CODE, String.format( "Only a maximum of %s blob sidecars can be requested per request", - specConfigDeneb.getMaxRequestBlobSidecars()))); + maxRequestBlobSidecars))); } return Optional.empty(); @@ -118,7 +123,10 @@ public void onIncomingMessage( message.getCount(), startSlot); - final long requestedCount = calculateRequestedCount(message); + final SpecMilestone latestMilestoneRequested = + spec.getForkSchedule().getSpecMilestoneAtSlot(endSlot); + final MiscHelpers miscHelpers = spec.forMilestone(latestMilestoneRequested).miscHelpers(); + final long requestedCount = calculateRequestedCount(message, miscHelpers.getMaxBlobsPerBlock()); final Optional blobSidecarsRequestApproval = peer.approveBlobSidecarsRequest(callback, requestedCount); @@ -159,9 +167,15 @@ public void onIncomingMessage( } else { canonicalHotRoots = ImmutableSortedMap.of(); } - + final int maxRequestBlobSidecars = miscHelpers.getMaxRequestBlobSidecars(); final RequestState initialState = - new RequestState(callback, startSlot, endSlot, canonicalHotRoots, finalizedSlot); + new RequestState( + callback, + startSlot, + endSlot, + canonicalHotRoots, + finalizedSlot, + maxRequestBlobSidecars); if (message.getCount().isZero()) { return SafeFuture.completedFuture(initialState); } @@ -182,8 +196,9 @@ public void onIncomingMessage( }); } - private long calculateRequestedCount(final BlobSidecarsByRangeRequestMessage message) { - return specConfigDeneb.getMaxBlobsPerBlock() * message.getCount().longValue(); + private long calculateRequestedCount( + final BlobSidecarsByRangeRequestMessage message, final int maxBlobsPerBlock) { + return maxBlobsPerBlock * message.getCount().longValue(); } private boolean checkBlobSidecarsAreAvailable( @@ -234,6 +249,7 @@ class RequestState { private final UInt64 endSlot; private final UInt64 finalizedSlot; private final Map canonicalHotRoots; + private final int maxRequestBlobSidecars; private final AtomicInteger sentBlobSidecars = new AtomicInteger(0); @@ -247,12 +263,14 @@ class RequestState { final UInt64 startSlot, final UInt64 endSlot, final Map canonicalHotRoots, - final UInt64 finalizedSlot) { + final UInt64 finalizedSlot, + final int maxRequestBlobSidecars) { this.callback = callback; this.startSlot = startSlot; this.endSlot = endSlot; this.finalizedSlot = finalizedSlot; this.canonicalHotRoots = canonicalHotRoots; + this.maxRequestBlobSidecars = maxRequestBlobSidecars; } SafeFuture sendBlobSidecar(final BlobSidecar blobSidecar) { @@ -262,7 +280,7 @@ SafeFuture sendBlobSidecar(final BlobSidecar blobSidecar) { SafeFuture> loadNextBlobSidecar() { if (blobSidecarKeysIterator.isEmpty()) { return combinedChainDataClient - .getBlobSidecarKeys(startSlot, endSlot, specConfigDeneb.getMaxRequestBlobSidecars()) + .getBlobSidecarKeys(startSlot, endSlot, maxRequestBlobSidecars) .thenCompose( keys -> { blobSidecarKeysIterator = Optional.of(keys.iterator()); diff --git a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootMessageHandler.java b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootMessageHandler.java index 0ca9071724c..d19587974df 100644 --- a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootMessageHandler.java +++ b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootMessageHandler.java @@ -34,7 +34,7 @@ import tech.pegasys.teku.networking.eth2.rpc.core.RpcException; import tech.pegasys.teku.networking.p2p.rpc.StreamClosedException; import tech.pegasys.teku.spec.Spec; -import tech.pegasys.teku.spec.config.SpecConfigDeneb; +import tech.pegasys.teku.spec.SpecMilestone; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.datastructures.networking.libp2p.rpc.BlobIdentifier; @@ -52,7 +52,6 @@ public class BlobSidecarsByRootMessageHandler private static final Logger LOG = LogManager.getLogger(); private final Spec spec; - private final SpecConfigDeneb specConfigDeneb; private final CombinedChainDataClient combinedChainDataClient; private final LabelledMetric requestCounter; @@ -60,11 +59,9 @@ public class BlobSidecarsByRootMessageHandler public BlobSidecarsByRootMessageHandler( final Spec spec, - final SpecConfigDeneb specConfigDeneb, final MetricsSystem metricsSystem, final CombinedChainDataClient combinedChainDataClient) { this.spec = spec; - this.specConfigDeneb = specConfigDeneb; this.combinedChainDataClient = combinedChainDataClient; requestCounter = metricsSystem.createLabelledCounter( @@ -82,7 +79,7 @@ public BlobSidecarsByRootMessageHandler( @Override public Optional validateRequest( final String protocolId, final BlobSidecarsByRootRequestMessage request) { - final int maxRequestBlobSidecars = specConfigDeneb.getMaxRequestBlobSidecars(); + final int maxRequestBlobSidecars = getMaxRequestBlobSidecars(); if (request.size() > maxRequestBlobSidecars) { requestCounter.labels("count_too_big").inc(); return Optional.of( @@ -156,6 +153,13 @@ public void onIncomingMessage( }); } + private int getMaxRequestBlobSidecars() { + final UInt64 epoch = + combinedChainDataClient.getRecentChainData().getCurrentEpoch().orElse(UInt64.ZERO); + final SpecMilestone specMilestone = spec.getForkSchedule().getSpecMilestoneAtEpoch(epoch); + return spec.forMilestone(specMilestone).miscHelpers().getMaxRequestBlobSidecars(); + } + private UInt64 getFinalizedEpoch() { return combinedChainDataClient .getFinalizedBlock() diff --git a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeMessageHandlerTest.java b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeMessageHandlerTest.java index aba5df4010b..752ee33c16b 100644 --- a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeMessageHandlerTest.java +++ b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRangeMessageHandlerTest.java @@ -34,10 +34,9 @@ import java.util.Optional; import java.util.stream.Collectors; import org.apache.commons.lang3.tuple.Pair; -import org.apache.tuweni.bytes.Bytes32; import org.assertj.core.api.AssertionsForInterfaceTypes; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestTemplate; import org.mockito.ArgumentCaptor; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.metrics.StubMetricsSystem; @@ -51,81 +50,81 @@ import tech.pegasys.teku.networking.eth2.rpc.core.encodings.RpcEncoding; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.TestSpecContext; import tech.pegasys.teku.spec.TestSpecFactory; -import tech.pegasys.teku.spec.config.SpecConfigDeneb; +import tech.pegasys.teku.spec.TestSpecInvocationContextProvider; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlockHeader; import tech.pegasys.teku.spec.datastructures.blocks.SlotAndBlockRoot; import tech.pegasys.teku.spec.datastructures.networking.libp2p.rpc.BlobSidecarsByRangeRequestMessage; import tech.pegasys.teku.spec.datastructures.util.SlotAndBlockRootAndBlobIndex; -import tech.pegasys.teku.spec.logic.versions.deneb.helpers.MiscHelpersDeneb; import tech.pegasys.teku.spec.util.DataStructureUtil; import tech.pegasys.teku.storage.client.CombinedChainDataClient; import tech.pegasys.teku.storage.store.UpdatableStore; +@TestSpecContext(milestone = {SpecMilestone.DENEB, SpecMilestone.ELECTRA}) public class BlobSidecarsByRangeMessageHandlerTest { private static final RequestApproval ZERO_OBJECTS_REQUEST_APPROVAL = new RequestApproval.RequestApprovalBuilder().timeSeconds(ZERO).objectsCount(0).build(); - private static final RpcEncoding RPC_ENCODING = RpcEncoding.createSszSnappyEncoding( TestSpecFactory.createDefault().getNetworkingConfig().getMaxChunkSize()); - private final UInt64 genesisTime = UInt64.valueOf(1982239L); - private final UInt64 denebForkEpoch = UInt64.valueOf(1); - - private final Spec spec = TestSpecFactory.createMinimalWithDenebForkEpoch(denebForkEpoch); - - private final SpecConfigDeneb specConfigDeneb = - SpecConfigDeneb.required(spec.forMilestone(SpecMilestone.DENEB).getConfig()); - - private final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec); - - private final int maxBlobsPerBlock = specConfigDeneb.getMaxBlobsPerBlock(); - - private final int slotsPerEpoch = spec.getSlotsPerEpoch(ZERO); - - private final UInt64 startSlot = denebForkEpoch.increment().times(slotsPerEpoch); - - private final Bytes32 headBlockRoot = dataStructureUtil.randomBytes32(); - + private final UInt64 currentForkEpoch = UInt64.valueOf(1); private final UInt64 count = UInt64.valueOf(5); - private final StubMetricsSystem metricsSystem = new StubMetricsSystem(); - private final Eth2Peer peer = mock(Eth2Peer.class); - private final MiscHelpersDeneb miscHelpers = - spec.forMilestone(SpecMilestone.DENEB).miscHelpers().toVersionDeneb().orElseThrow(); - @SuppressWarnings("unchecked") private final ResponseCallback listener = mock(ResponseCallback.class); private final CombinedChainDataClient combinedChainDataClient = mock(CombinedChainDataClient.class); private final UpdatableStore store = mock(UpdatableStore.class); - private final String protocolId = BeaconChainMethodIds.getBlobSidecarsByRangeMethodId(1, RPC_ENCODING); - - private final BlobSidecarsByRangeMessageHandler handler = - new BlobSidecarsByRangeMessageHandler( - spec, specConfigDeneb, metricsSystem, combinedChainDataClient); private final Optional allowedObjectsRequest = Optional.of( new RequestApproval.RequestApprovalBuilder().objectsCount(100).timeSeconds(ZERO).build()); + private SpecMilestone specMilestone; + private Spec spec; + private BlobSidecarsByRangeMessageHandler handler; + private DataStructureUtil dataStructureUtil; + private int maxBlobsPerBlock; + private int slotsPerEpoch; + private UInt64 startSlot; + @BeforeEach - public void setUp() { + public void setUp(final TestSpecInvocationContextProvider.SpecContext specContext) { + specMilestone = specContext.getSpecMilestone(); + spec = + switch (specContext.getSpecMilestone()) { + case PHASE0 -> throw new IllegalArgumentException("Phase0 is an unsupported milestone"); + case ALTAIR -> throw new IllegalArgumentException("Altair is an unsupported milestone"); + case BELLATRIX -> + throw new IllegalArgumentException("Bellatrix is an unsupported milestone"); + case CAPELLA -> throw new IllegalArgumentException("Capella is an unsupported milestone"); + case DENEB -> TestSpecFactory.createMinimalWithDenebForkEpoch(currentForkEpoch); + case ELECTRA -> TestSpecFactory.createMinimalWithElectraForkEpoch(currentForkEpoch); + }; + + dataStructureUtil = new DataStructureUtil(spec); + maxBlobsPerBlock = spec.forMilestone(specMilestone).miscHelpers().getMaxBlobsPerBlock(); + slotsPerEpoch = spec.getSlotsPerEpoch(ZERO); + startSlot = currentForkEpoch.increment().times(slotsPerEpoch); + handler = new BlobSidecarsByRangeMessageHandler(spec, metricsSystem, combinedChainDataClient); + when(peer.approveRequest()).thenReturn(true); when(peer.approveBlobSidecarsRequest(eq(listener), anyLong())) .thenReturn(allowedObjectsRequest); when(combinedChainDataClient.getEarliestAvailableBlobSidecarSlot()) .thenReturn(SafeFuture.completedFuture(Optional.of(ZERO))); when(combinedChainDataClient.getStore()).thenReturn(store); - when(combinedChainDataClient.getBestBlockRoot()).thenReturn(Optional.of(headBlockRoot)); + when(combinedChainDataClient.getBestBlockRoot()) + .thenReturn(Optional.of(dataStructureUtil.randomBytes32())); // everything is finalized by default when(combinedChainDataClient.getFinalizedBlockSlot()) .thenReturn(Optional.of(startSlot.plus(count))); @@ -133,10 +132,10 @@ public void setUp() { // mock store when(store.getGenesisTime()).thenReturn(genesisTime); - setCurrentEpoch(denebForkEpoch.increment()); + setCurrentEpoch(currentForkEpoch.increment()); } - @Test + @TestTemplate public void validateRequest_validRequest() { final Optional result = handler.validateRequest( @@ -144,10 +143,10 @@ public void validateRequest_validRequest() { assertThat(result).isEmpty(); } - @Test + @TestTemplate public void validateRequest_shouldRejectRequestWhenCountIsTooBig() { final UInt64 maxRequestBlobSidecars = - UInt64.valueOf(specConfigDeneb.getMaxRequestBlobSidecars()); + UInt64.valueOf(spec.forMilestone(specMilestone).miscHelpers().getMaxRequestBlobSidecars()); final BlobSidecarsByRangeRequestMessage request = new BlobSidecarsByRangeRequestMessage( startSlot, maxRequestBlobSidecars.increment(), maxBlobsPerBlock); @@ -170,7 +169,7 @@ public void validateRequest_shouldRejectRequestWhenCountIsTooBig() { assertThat(rateLimitedCount).isOne(); } - @Test + @TestTemplate public void shouldNotSendBlobSidecarsIfPeerIsRateLimited() { when(peer.approveBlobSidecarsRequest(listener, count.times(maxBlobsPerBlock).longValue())) @@ -197,7 +196,7 @@ public void shouldNotSendBlobSidecarsIfPeerIsRateLimited() { verifyNoInteractions(listener); } - @Test + @TestTemplate public void shouldSendResourceUnavailableIfBlobSidecarsAreNotAvailable() { // current epoch is 5020 @@ -207,7 +206,7 @@ public void shouldSendResourceUnavailableIfBlobSidecarsAreNotAvailable() { when(combinedChainDataClient.getEarliestAvailableBlobSidecarSlot()) .thenReturn( SafeFuture.completedFuture( - Optional.of(denebForkEpoch.plus(5009).times(slotsPerEpoch)))); + Optional.of(currentForkEpoch.plus(5009).times(slotsPerEpoch)))); // start slot in epoch 5000 within MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS range final BlobSidecarsByRangeRequestMessage request = @@ -231,12 +230,13 @@ public void shouldSendResourceUnavailableIfBlobSidecarsAreNotAvailable() { "Requested blob sidecars are not available.")); } - @Test + @TestTemplate public void shouldCompleteSuccessfullyIfNoBlobSidecarsInRange() { when(combinedChainDataClient.getBlobSidecarKeys(any(), any(), anyLong())) .thenReturn(SafeFuture.completedFuture(Collections.emptyList())); final BlobSidecarsByRangeRequestMessage request = - new BlobSidecarsByRangeRequestMessage(ZERO, count, maxBlobsPerBlock); + new BlobSidecarsByRangeRequestMessage( + currentForkEpoch.plus(1).times(slotsPerEpoch), count, maxBlobsPerBlock); handler.onIncomingMessage(protocolId, peer, request, listener); @@ -254,7 +254,7 @@ public void shouldCompleteSuccessfullyIfNoBlobSidecarsInRange() { verify(listener).completeSuccessfully(); } - @Test + @TestTemplate public void shouldSendToPeerRequestedNumberOfFinalizedBlobSidecars() { final BlobSidecarsByRangeRequestMessage request = @@ -283,7 +283,7 @@ public void shouldSendToPeerRequestedNumberOfFinalizedBlobSidecars() { AssertionsForInterfaceTypes.assertThat(actualSent).containsExactlyElementsOf(expectedSent); } - @Test + @TestTemplate public void shouldSendToPeerRequestedNumberOfCanonicalBlobSidecars() { final UInt64 latestFinalizedSlot = startSlot.plus(count).minus(3); @@ -341,7 +341,7 @@ public void shouldSendToPeerRequestedNumberOfCanonicalBlobSidecars() { AssertionsForInterfaceTypes.assertThat(actualSent).containsExactlyElementsOf(expectedSent); } - @Test + @TestTemplate public void shouldIgnoreRequestWhenCountIsZero() { final BlobSidecarsByRangeRequestMessage request = @@ -367,7 +367,7 @@ public void shouldIgnoreRequestWhenCountIsZero() { AssertionsForInterfaceTypes.assertThat(actualSent).isEmpty(); } - @Test + @TestTemplate public void shouldIgnoreRequestWhenCountIsZeroAndHotSlotRequested() { // not finalized final UInt64 hotStartSlot = startSlot.plus(7); @@ -430,7 +430,10 @@ private List> setupK UInt64.rangeClosed( ZERO, dataStructureUtil - .randomUInt64(miscHelpers.getBlobKzgCommitmentsCount(block)) + .randomUInt64( + spec.forMilestone(specMilestone) + .miscHelpers() + .getBlobKzgCommitmentsCount(block)) .minusMinZero(1)) .forEach( index -> diff --git a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootMessageHandlerTest.java b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootMessageHandlerTest.java index 8ab1801777e..0369973730a 100644 --- a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootMessageHandlerTest.java +++ b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/rpc/beaconchain/methods/BlobSidecarsByRootMessageHandlerTest.java @@ -32,7 +32,7 @@ import java.util.stream.IntStream; import org.apache.tuweni.bytes.Bytes32; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestTemplate; import org.mockito.ArgumentCaptor; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.metrics.StubMetricsSystem; @@ -46,66 +46,82 @@ import tech.pegasys.teku.networking.eth2.rpc.core.encodings.RpcEncoding; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.TestSpecContext; import tech.pegasys.teku.spec.TestSpecFactory; +import tech.pegasys.teku.spec.TestSpecInvocationContextProvider; import tech.pegasys.teku.spec.config.SpecConfigDeneb; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.datastructures.networking.libp2p.rpc.BlobIdentifier; import tech.pegasys.teku.spec.datastructures.networking.libp2p.rpc.BlobSidecarsByRootRequestMessage; import tech.pegasys.teku.spec.datastructures.networking.libp2p.rpc.BlobSidecarsByRootRequestMessageSchema; -import tech.pegasys.teku.spec.schemas.SchemaDefinitionsDeneb; import tech.pegasys.teku.spec.util.DataStructureUtil; import tech.pegasys.teku.storage.client.CombinedChainDataClient; +import tech.pegasys.teku.storage.client.RecentChainData; import tech.pegasys.teku.storage.store.UpdatableStore; +@TestSpecContext(milestone = {SpecMilestone.DENEB, SpecMilestone.ELECTRA}) public class BlobSidecarsByRootMessageHandlerTest { private final UInt64 genesisTime = UInt64.valueOf(1982239L); - private final UInt64 denebForkEpoch = UInt64.valueOf(1); - - private final Spec spec = TestSpecFactory.createMinimalWithDenebForkEpoch(denebForkEpoch); - private final SpecConfigDeneb specConfigDeneb = - SpecConfigDeneb.required(spec.forMilestone(SpecMilestone.DENEB).getConfig()); - private final int maxChunkSize = spec.getNetworkingConfig().getMaxChunkSize(); - private final BlobSidecarsByRootRequestMessageSchema messageSchema = - SchemaDefinitionsDeneb.required(spec.forMilestone(SpecMilestone.DENEB).getSchemaDefinitions()) - .getBlobSidecarsByRootRequestMessageSchema(); - private final RpcEncoding rpcEncoding = RpcEncoding.createSszSnappyEncoding(maxChunkSize); - - private final String protocolId = - BeaconChainMethodIds.getBlobSidecarsByRootMethodId(1, rpcEncoding); - - private final UInt64 denebFirstSlot = spec.computeStartSlotAtEpoch(denebForkEpoch); - - private final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec); - + private final UInt64 currentForkEpoch = UInt64.valueOf(1); + private BlobSidecarsByRootRequestMessageSchema messageSchema; private final ArgumentCaptor blobSidecarCaptor = ArgumentCaptor.forClass(BlobSidecar.class); - private final ArgumentCaptor rpcExceptionCaptor = ArgumentCaptor.forClass(RpcException.class); + private final Optional allowedObjectsRequest = + Optional.of( + new RequestApproval.RequestApprovalBuilder().objectsCount(100).timeSeconds(ZERO).build()); @SuppressWarnings("unchecked") private final ResponseCallback callback = mock(ResponseCallback.class); private final CombinedChainDataClient combinedChainDataClient = mock(CombinedChainDataClient.class); + private final RecentChainData recentChainData = mock(RecentChainData.class); private final UpdatableStore store = mock(UpdatableStore.class); - private final Eth2Peer peer = mock(Eth2Peer.class); - private final StubMetricsSystem metricsSystem = new StubMetricsSystem(); - - private final BlobSidecarsByRootMessageHandler handler = - new BlobSidecarsByRootMessageHandler( - spec, specConfigDeneb, metricsSystem, combinedChainDataClient); - - private final Optional allowedObjectsRequest = - Optional.of( - new RequestApproval.RequestApprovalBuilder().objectsCount(100).timeSeconds(ZERO).build()); + private String protocolId; + private UInt64 currentForkFirstSlot; + private DataStructureUtil dataStructureUtil; + private BlobSidecarsByRootMessageHandler handler; + private SpecMilestone specMilestone; + private Spec spec; @BeforeEach - public void setup() { + public void setup(final TestSpecInvocationContextProvider.SpecContext specContext) { + specMilestone = specContext.getSpecMilestone(); + spec = + switch (specContext.getSpecMilestone()) { + case PHASE0 -> throw new IllegalArgumentException("Phase0 is an unsupported milestone"); + case ALTAIR -> throw new IllegalArgumentException("Altair is an unsupported milestone"); + case BELLATRIX -> + throw new IllegalArgumentException("Bellatrix is an unsupported milestone"); + case CAPELLA -> throw new IllegalArgumentException("Capella is an unsupported milestone"); + case DENEB -> TestSpecFactory.createMinimalWithDenebForkEpoch(currentForkEpoch); + case ELECTRA -> TestSpecFactory.createMinimalWithElectraForkEpoch(currentForkEpoch); + }; + dataStructureUtil = new DataStructureUtil(spec); + messageSchema = + specMilestone.equals(SpecMilestone.DENEB) + ? spec.atEpoch(currentForkEpoch) + .getSchemaDefinitions() + .toVersionDeneb() + .orElseThrow() + .getBlobSidecarsByRootRequestMessageSchema() + : spec.atEpoch(currentForkEpoch) + .getSchemaDefinitions() + .toVersionElectra() + .orElseThrow() + .getBlobSidecarsByRootRequestMessageSchema(); + currentForkFirstSlot = spec.computeStartSlotAtEpoch(currentForkEpoch); + final int maxChunkSize = spec.getNetworkingConfig().getMaxChunkSize(); + final RpcEncoding rpcEncoding = RpcEncoding.createSszSnappyEncoding(maxChunkSize); + protocolId = BeaconChainMethodIds.getBlobSidecarsByRootMethodId(1, rpcEncoding); + handler = new BlobSidecarsByRootMessageHandler(spec, metricsSystem, combinedChainDataClient); + when(peer.approveRequest()).thenReturn(true); when(peer.approveBlobSidecarsRequest(eq(callback), anyLong())) .thenReturn(allowedObjectsRequest); @@ -113,11 +129,12 @@ public void setup() { when(combinedChainDataClient.getBlockByBlockRoot(any())) .thenReturn( SafeFuture.completedFuture( - Optional.of(dataStructureUtil.randomSignedBeaconBlock(denebFirstSlot)))); + Optional.of(dataStructureUtil.randomSignedBeaconBlock(currentForkFirstSlot)))); // deneb fork epoch is finalized when(combinedChainDataClient.getFinalizedBlock()) - .thenReturn(Optional.of(dataStructureUtil.randomSignedBeaconBlock(denebFirstSlot))); + .thenReturn(Optional.of(dataStructureUtil.randomSignedBeaconBlock(currentForkFirstSlot))); when(combinedChainDataClient.getStore()).thenReturn(store); + when(combinedChainDataClient.getRecentChainData()).thenReturn(recentChainData); when(callback.respond(any())).thenReturn(SafeFuture.COMPLETE); // mock store @@ -126,12 +143,15 @@ public void setup() { when(store.getTimeSeconds()) .thenReturn( spec.getSlotStartTime( - denebForkEpoch.increment().times(spec.getSlotsPerEpoch(ZERO)), genesisTime)); + currentForkEpoch.increment().times(spec.getSlotsPerEpoch(ZERO)), genesisTime)); } - @Test + @TestTemplate public void validateRequest_shouldNotAllowRequestLargerThanMaximumAllowed() { - final int maxRequestBlobSidecars = specConfigDeneb.getMaxRequestBlobSidecars(); + final int maxRequestBlobSidecars = + spec.forMilestone(specMilestone).miscHelpers().getMaxRequestBlobSidecars(); + when(recentChainData.getCurrentEpoch()) + .thenReturn(Optional.of(dataStructureUtil.randomEpoch())); final BlobSidecarsByRootRequestMessage request = new BlobSidecarsByRootRequestMessage( messageSchema, dataStructureUtil.randomBlobIdentifiers(maxRequestBlobSidecars + 1)); @@ -156,7 +176,7 @@ public void validateRequest_shouldNotAllowRequestLargerThanMaximumAllowed() { assertThat(countTooBigCount).isOne(); } - @Test + @TestTemplate public void shouldNotSendBlobSidecarsIfPeerIsRateLimited() { when(peer.approveBlobSidecarsRequest(callback, 5)).thenReturn(Optional.empty()); @@ -182,7 +202,7 @@ public void shouldNotSendBlobSidecarsIfPeerIsRateLimited() { verifyNoInteractions(callback); } - @Test + @TestTemplate public void shouldSendAvailableOnlyResources() { final List blobIdentifiers = prepareBlobIdentifiers(4); @@ -223,7 +243,7 @@ public void shouldSendAvailableOnlyResources() { .containsExactlyInAnyOrderElementsOf(blobIdentifiersBlockRoots); } - @Test + @TestTemplate public void shouldSendResourceUnavailableIfBlockRootReferencesBlockEarlierThanTheMinimumRequestEpoch() { final List blobIdentifiers = prepareBlobIdentifiers(3); @@ -264,7 +284,7 @@ public void shouldSendAvailableOnlyResources() { blobIdentifiers.get(0).getBlockRoot()); } - @Test + @TestTemplate public void shouldSendResourceUnavailableIfBlobSidecarBlockRootReferencesBlockEarlierThanTheMinimumRequestEpoch() { final List blobIdentifiers = prepareBlobIdentifiers(3); @@ -301,7 +321,7 @@ public void shouldSendAvailableOnlyResources() { blobIdentifiers.get(0).getBlockRoot()); } - @Test + @TestTemplate public void shouldSendToPeerRequestedBlobSidecars() { final List blobIdentifiers = prepareBlobIdentifiers(5);