From c9dc8a4e3ec7275ae0948c783501d2d859ae5f8a Mon Sep 17 00:00:00 2001 From: Mehdi AOUADI Date: Tue, 5 Nov 2024 15:05:27 +0100 Subject: [PATCH] update blob sidecars gossip topics --- .../eth2/gossip/BlobSidecarGossipManager.java | 9 +- .../eth2/gossip/topics/GossipTopics.java | 26 +++++- .../gossip/BlobSidecarGossipManagerTest.java | 51 ++++++----- .../topics/Eth2GossipTopicFilterTest.java | 88 ++++++++++++------- 4 files changed, 115 insertions(+), 59 deletions(-) diff --git a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/BlobSidecarGossipManager.java b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/BlobSidecarGossipManager.java index 3354593d0c2..b46d3310976 100644 --- a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/BlobSidecarGossipManager.java +++ b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/BlobSidecarGossipManager.java @@ -32,8 +32,8 @@ import tech.pegasys.teku.networking.p2p.gossip.GossipNetwork; import tech.pegasys.teku.networking.p2p.gossip.TopicChannel; import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.SpecMilestone; import tech.pegasys.teku.spec.SpecVersion; -import tech.pegasys.teku.spec.config.SpecConfigDeneb; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecarSchema; import tech.pegasys.teku.spec.datastructures.state.ForkInfo; @@ -68,8 +68,11 @@ public static BlobSidecarGossipManager create( .getBlobSidecarSchema(); final Int2ObjectMap> subnetIdToTopicHandler = new Int2ObjectOpenHashMap<>(); - final SpecConfigDeneb specConfigDeneb = SpecConfigDeneb.required(forkSpecVersion.getConfig()); - IntStream.range(0, specConfigDeneb.getBlobSidecarSubnetCount()) + final SpecMilestone specMilestone = spec.atEpoch(forkInfo.getFork().getEpoch()).getMilestone(); + final int blobSidecarSubnetCount = + spec.forMilestone(specMilestone).miscHelpers().getBlobSidecarSubnetCount(); + + IntStream.range(0, blobSidecarSubnetCount) .forEach( subnetId -> { final Eth2TopicHandler topicHandler = diff --git a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/topics/GossipTopics.java b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/topics/GossipTopics.java index 8f71a1ac466..26505d54162 100644 --- a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/topics/GossipTopics.java +++ b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/topics/GossipTopics.java @@ -73,10 +73,18 @@ public static Set getAllTopics( for (int i = 0; i < NetworkConstants.SYNC_COMMITTEE_SUBNET_COUNT; i++) { topics.add(getSyncCommitteeSubnetTopic(forkDigest, i, gossipEncoding)); } - if (spec.getNetworkingConfigDeneb().isPresent()) { - for (int i = 0; i < spec.getNetworkingConfigDeneb().get().getBlobSidecarSubnetCount(); i++) { - topics.add(getBlobSidecarSubnetTopic(forkDigest, i, gossipEncoding)); - } + if (spec.getNetworkingConfigElectra().isPresent()) { + addBlobSidecarSubnetTopics( + spec.getNetworkingConfigElectra().get().getBlobSidecarSubnetCountElectra(), + topics, + forkDigest, + gossipEncoding); + } else if (spec.getNetworkingConfigDeneb().isPresent()) { + addBlobSidecarSubnetTopics( + spec.getNetworkingConfigDeneb().get().getBlobSidecarSubnetCount(), + topics, + forkDigest, + gossipEncoding); } for (GossipTopicName topicName : GossipTopicName.values()) { topics.add(GossipTopics.getTopic(forkDigest, topicName, gossipEncoding)); @@ -98,4 +106,14 @@ public static Bytes4 extractForkDigest(final String topic) throws IllegalArgumen return Bytes4.fromHexString(forkDigest); } + + private static void addBlobSidecarSubnetTopics( + final int blobSidecarSubnetCount, + final Set topics, + final Bytes4 forkDigest, + final GossipEncoding gossipEncoding) { + for (int i = 0; i < blobSidecarSubnetCount; i++) { + topics.add(getBlobSidecarSubnetTopic(forkDigest, i, gossipEncoding)); + } + } } diff --git a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/gossip/BlobSidecarGossipManagerTest.java b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/gossip/BlobSidecarGossipManagerTest.java index 3c42b72abe5..8eb22792125 100644 --- a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/gossip/BlobSidecarGossipManagerTest.java +++ b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/gossip/BlobSidecarGossipManagerTest.java @@ -30,7 +30,7 @@ import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestTemplate; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.async.StubAsyncRunner; import tech.pegasys.teku.infrastructure.unsigned.UInt64; @@ -41,9 +41,10 @@ import tech.pegasys.teku.networking.p2p.gossip.TopicChannel; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.SpecMilestone; -import tech.pegasys.teku.spec.TestSpecFactory; -import tech.pegasys.teku.spec.config.SpecConfig; +import tech.pegasys.teku.spec.TestSpecContext; +import tech.pegasys.teku.spec.TestSpecInvocationContextProvider; import tech.pegasys.teku.spec.config.SpecConfigDeneb; +import tech.pegasys.teku.spec.config.SpecConfigElectra; import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.state.ForkInfo; import tech.pegasys.teku.spec.util.DataStructureUtil; @@ -52,31 +53,30 @@ import tech.pegasys.teku.storage.storageSystem.InMemoryStorageSystemBuilder; import tech.pegasys.teku.storage.storageSystem.StorageSystem; +@TestSpecContext(milestone = {SpecMilestone.DENEB, SpecMilestone.ELECTRA}) public class BlobSidecarGossipManagerTest { private static final Pattern BLOB_SIDECAR_TOPIC_PATTERN = Pattern.compile("blob_sidecar_(\\d+)"); - private final Spec spec = TestSpecFactory.createMainnetDeneb(); - private final StorageSystem storageSystem = InMemoryStorageSystemBuilder.buildDefault(spec); - private final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec); - @SuppressWarnings("unchecked") private final OperationProcessor processor = mock(OperationProcessor.class); private final GossipNetwork gossipNetwork = mock(GossipNetwork.class); private final GossipEncoding gossipEncoding = GossipEncoding.SSZ_SNAPPY; - private final Map topicChannels = new HashMap<>(); - private final StubAsyncRunner asyncRunner = new StubAsyncRunner(); - private final ForkInfo forkInfo = - new ForkInfo(spec.fork(UInt64.ZERO), dataStructureUtil.randomBytes32()); - + private Spec spec; + private DataStructureUtil dataStructureUtil; private BlobSidecarGossipManager blobSidecarGossipManager; + private SpecMilestone specMilestone; @BeforeEach - public void setup() { + public void setup(final TestSpecInvocationContextProvider.SpecContext specContext) { + spec = specContext.getSpec(); + dataStructureUtil = specContext.getDataStructureUtil(); + specMilestone = specContext.getSpecMilestone(); + final StorageSystem storageSystem = InMemoryStorageSystemBuilder.buildDefault(spec); storageSystem.chainUpdater().initializeGenesis(); // return TopicChannel mock for each blob_sidecar_ topic doAnswer( @@ -96,6 +96,8 @@ public void setup() { .subscribe(any(), any()); when(processor.process(any(), any())) .thenReturn(SafeFuture.completedFuture(InternalValidationResult.ACCEPT)); + final ForkInfo forkInfo = + new ForkInfo(spec.fork(UInt64.ZERO), dataStructureUtil.randomBytes32()); blobSidecarGossipManager = BlobSidecarGossipManager.create( storageSystem.recentChainData(), @@ -109,7 +111,7 @@ public void setup() { blobSidecarGossipManager.subscribe(); } - @Test + @TestTemplate public void testGossipingBlobSidecarPublishesToCorrectSubnet() { final BlobSidecar blobSidecar = dataStructureUtil.createRandomBlobSidecarBuilder().index(UInt64.ONE).build(); @@ -127,19 +129,18 @@ public void testGossipingBlobSidecarPublishesToCorrectSubnet() { }); } - @Test + @TestTemplate public void testGossipingBlobSidecarWithLargeIndexGossipToCorrectSubnet() { final BlobSidecar blobSidecar = dataStructureUtil.createRandomBlobSidecarBuilder().index(UInt64.valueOf(10)).build(); final Bytes serialized = gossipEncoding.encode(blobSidecar); safeJoin(blobSidecarGossipManager.publishBlobSidecar(blobSidecar)); - final SpecConfig config = spec.forMilestone(SpecMilestone.DENEB).getConfig(); - final SpecConfigDeneb specConfigDeneb = SpecConfigDeneb.required(config); + final int blobSidecarSubnetCount = getBlobSidecarSubnetCount(); topicChannels.forEach( (subnetId, channel) -> { - if (subnetId == 10 % specConfigDeneb.getBlobSidecarSubnetCount()) { + if (subnetId == 10 % blobSidecarSubnetCount) { verify(channel).gossip(serialized); } else { verifyNoInteractions(channel); @@ -147,7 +148,7 @@ public void testGossipingBlobSidecarWithLargeIndexGossipToCorrectSubnet() { }); } - @Test + @TestTemplate public void testUnsubscribingClosesAllChannels() { blobSidecarGossipManager.unsubscribe(); @@ -159,7 +160,7 @@ public void testUnsubscribingClosesAllChannels() { }); } - @Test + @TestTemplate public void testAcceptingSidecarGossipIfOnTheCorrectTopic() { // topic handler for blob sidecars with subnet_id 1 final Eth2TopicHandler topicHandler = blobSidecarGossipManager.getTopicHandler(1); @@ -175,7 +176,7 @@ public void testAcceptingSidecarGossipIfOnTheCorrectTopic() { assertThat(validationResult).isEqualTo(InternalValidationResult.ACCEPT); } - @Test + @TestTemplate public void testRejectingSidecarGossipIfNotOnTheCorrectTopic() { // topic handler for blob sidecars with subnet_id 1 final Eth2TopicHandler topicHandler = blobSidecarGossipManager.getTopicHandler(1); @@ -190,4 +191,12 @@ public void testRejectingSidecarGossipIfNotOnTheCorrectTopic() { assertThat(validationResult.getDescription()) .hasValue("blob sidecar with subnet_id 2 does not match the topic subnet_id 1"); } + + private int getBlobSidecarSubnetCount() { + return specMilestone.isGreaterThanOrEqualTo(SpecMilestone.ELECTRA) + ? SpecConfigElectra.required(spec.forMilestone(SpecMilestone.ELECTRA).getConfig()) + .getBlobSidecarSubnetCountElectra() + : SpecConfigDeneb.required(spec.forMilestone(SpecMilestone.DENEB).getConfig()) + .getBlobSidecarSubnetCount(); + } } diff --git a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/gossip/topics/Eth2GossipTopicFilterTest.java b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/gossip/topics/Eth2GossipTopicFilterTest.java index 8dd30549a36..42cc5ed1183 100644 --- a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/gossip/topics/Eth2GossipTopicFilterTest.java +++ b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/gossip/topics/Eth2GossipTopicFilterTest.java @@ -27,60 +27,81 @@ import java.util.Optional; 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 tech.pegasys.teku.infrastructure.bytes.Bytes4; import tech.pegasys.teku.infrastructure.unsigned.UInt64; 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.SpecConfig; import tech.pegasys.teku.spec.config.SpecConfigDeneb; +import tech.pegasys.teku.spec.config.SpecConfigElectra; import tech.pegasys.teku.spec.datastructures.state.Fork; import tech.pegasys.teku.spec.datastructures.state.ForkInfo; import tech.pegasys.teku.spec.util.DataStructureUtil; import tech.pegasys.teku.storage.client.RecentChainData; +@TestSpecContext(milestone = {SpecMilestone.DENEB, SpecMilestone.ELECTRA}) class Eth2GossipTopicFilterTest { - private final UInt64 denebForkEpoch = UInt64.valueOf(10); - private final Spec spec = TestSpecFactory.createMinimalWithDenebForkEpoch(denebForkEpoch); - private final List forks = spec.getForkSchedule().getForks(); - private final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec); - private final Bytes32 genesisValidatorsRoot = dataStructureUtil.randomBytes32(); - private final ForkInfo forkInfo = new ForkInfo(forks.get(0), genesisValidatorsRoot); - private final Fork nextFork = forks.get(1); private final RecentChainData recentChainData = mock(RecentChainData.class); - private final Bytes4 nextForkDigest = - spec.atEpoch(nextFork.getEpoch()) - .miscHelpers() - .computeForkDigest(nextFork.getCurrentVersion(), genesisValidatorsRoot); - - private final Eth2GossipTopicFilter filter = - new Eth2GossipTopicFilter(recentChainData, SSZ_SNAPPY, spec); + private final UInt64 currentForkEpoch = UInt64.valueOf(10); + private Spec spec; + private SpecMilestone specMilestone; + private DataStructureUtil dataStructureUtil; + private ForkInfo forkInfo; + private Eth2GossipTopicFilter filter; + private Bytes4 nextForkDigest; @BeforeEach - void setUp() { + 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); + filter = new Eth2GossipTopicFilter(recentChainData, SSZ_SNAPPY, spec); + + final Bytes32 genesisValidatorsRoot = dataStructureUtil.randomBytes32(); + final List forks = spec.getForkSchedule().getForks(); + forkInfo = new ForkInfo(forks.get(0), genesisValidatorsRoot); + + final Fork nextFork = forks.get(1); + nextForkDigest = + spec.atEpoch(nextFork.getEpoch()) + .miscHelpers() + .computeForkDigest(nextFork.getCurrentVersion(), genesisValidatorsRoot); + when(recentChainData.getCurrentForkInfo()).thenReturn(Optional.of(forkInfo)); when(recentChainData.getNextFork(forkInfo.getFork())).thenReturn(Optional.of(nextFork)); } - @Test + @TestTemplate void shouldNotAllowIrrelevantTopics() { assertThat(filter.isRelevantTopic("abc")).isFalse(); } - @Test + @TestTemplate void shouldNotRequireNextForkToBePresent() { when(recentChainData.getNextFork(any())).thenReturn(Optional.empty()); assertThat(filter.isRelevantTopic(getTopicName(GossipTopicName.BEACON_BLOCK))).isTrue(); } - @Test + @TestTemplate void shouldConsiderTopicsForNextForkRelevant() { assertThat(filter.isRelevantTopic(getNextForkTopicName(GossipTopicName.BEACON_BLOCK))).isTrue(); } - @Test + @TestTemplate void shouldConsiderTopicsForSignedContributionAndProofRelevant() { assertThat( filter.isRelevantTopic( @@ -88,7 +109,7 @@ void shouldConsiderTopicsForSignedContributionAndProofRelevant() { .isTrue(); } - @Test + @TestTemplate void shouldConsiderAllAttestationSubnetsRelevant() { for (int i = 0; i < spec.getNetworkingConfig().getAttestationSubnetCount(); i++) { assertThat(filter.isRelevantTopic(getTopicName(getAttestationSubnetTopicName(i)))).isTrue(); @@ -97,14 +118,14 @@ void shouldConsiderAllAttestationSubnetsRelevant() { } } - @Test + @TestTemplate void shouldConsiderAllSyncCommitteeSubnetsRelevant() { for (int i = 0; i < SYNC_COMMITTEE_SUBNET_COUNT; i++) { assertThat(filter.isRelevantTopic(getTopicName(getSyncCommitteeSubnetTopicName(i)))).isTrue(); } } - @Test + @TestTemplate void shouldConsiderAllBlobSidecarSubnetsRelevant() { final SpecConfig config = spec.forMilestone(SpecMilestone.DENEB).getConfig(); final SpecConfigDeneb specConfigDeneb = SpecConfigDeneb.required(config); @@ -113,24 +134,21 @@ void shouldConsiderAllBlobSidecarSubnetsRelevant() { } } - @Test + @TestTemplate void shouldNotConsiderBlobSidecarWithIncorrectSubnetIdRelevant() { - final SpecConfig config = spec.forMilestone(SpecMilestone.DENEB).getConfig(); - final SpecConfigDeneb specConfigDeneb = SpecConfigDeneb.required(config); + final int blobSidecarSubnetCount = getBlobSidecarSubnetCount(); assertThat( filter.isRelevantTopic( - getTopicName( - getBlobSidecarSubnetTopicName( - specConfigDeneb.getBlobSidecarSubnetCount() + 1)))) + getTopicName(getBlobSidecarSubnetTopicName(blobSidecarSubnetCount + 1)))) .isFalse(); } - @Test + @TestTemplate void shouldNotConsiderBlobSidecarWithoutIndexTopicRelevant() { assertThat(filter.isRelevantTopic(getTopicName("blob_sidecar"))).isFalse(); } - @Test + @TestTemplate void shouldNotAllowTopicsWithUnknownForkDigest() { final String irrelevantTopic = GossipTopics.getTopic( @@ -138,6 +156,14 @@ void shouldNotAllowTopicsWithUnknownForkDigest() { assertThat(filter.isRelevantTopic(irrelevantTopic)).isFalse(); } + private int getBlobSidecarSubnetCount() { + return specMilestone.isGreaterThanOrEqualTo(SpecMilestone.ELECTRA) + ? SpecConfigElectra.required(spec.forMilestone(SpecMilestone.ELECTRA).getConfig()) + .getBlobSidecarSubnetCountElectra() + : SpecConfigDeneb.required(spec.forMilestone(SpecMilestone.DENEB).getConfig()) + .getBlobSidecarSubnetCount(); + } + private String getTopicName(final GossipTopicName name) { return GossipTopics.getTopic(forkInfo.getForkDigest(spec), name, SSZ_SNAPPY); }