diff --git a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/topics/Eth2GossipTopicFilter.java b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/topics/Eth2GossipTopicFilter.java index 1aa08b34474..d1209d1e0ec 100644 --- a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/topics/Eth2GossipTopicFilter.java +++ b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/topics/Eth2GossipTopicFilter.java @@ -25,6 +25,7 @@ import tech.pegasys.teku.networking.p2p.libp2p.gossip.GossipTopicFilter; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.SpecVersion; import tech.pegasys.teku.spec.datastructures.state.ForkInfo; import tech.pegasys.teku.storage.client.RecentChainData; @@ -57,15 +58,18 @@ private Set computeRelevantTopics( recentChainData.getMilestoneByForkDigest(forkDigest).orElseThrow(); final Set topics = getAllTopics(gossipEncoding, forkDigest, spec, specMilestone); spec.getForkSchedule().getForks().stream() - .filter(fork -> fork.getEpoch().isGreaterThanOrEqualTo(forkInfo.getFork().getEpoch())) + .filter(fork -> fork.getEpoch().isGreaterThan(forkInfo.getFork().getEpoch())) .forEach( futureFork -> { + final SpecVersion futureSpecVersion = spec.atEpoch(futureFork.getEpoch()); final Bytes4 futureForkDigest = - spec.atEpoch(futureFork.getEpoch()) + futureSpecVersion .miscHelpers() .computeForkDigest( futureFork.getCurrentVersion(), forkInfo.getGenesisValidatorsRoot()); - topics.addAll(getAllTopics(gossipEncoding, futureForkDigest, spec, specMilestone)); + topics.addAll( + getAllTopics( + gossipEncoding, futureForkDigest, spec, futureSpecVersion.getMilestone())); }); return topics; } 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 2eac5f144db..b32868eef90 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 @@ -14,18 +14,20 @@ package tech.pegasys.teku.networking.eth2.gossip.topics; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assumptions.assumeThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.spy; import static tech.pegasys.teku.networking.eth2.gossip.encoding.GossipEncoding.SSZ_SNAPPY; import static tech.pegasys.teku.networking.eth2.gossip.topics.GossipTopicName.getAttestationSubnetTopicName; import static tech.pegasys.teku.networking.eth2.gossip.topics.GossipTopicName.getBlobSidecarSubnetTopicName; import static tech.pegasys.teku.networking.eth2.gossip.topics.GossipTopicName.getSyncCommitteeSubnetTopicName; +import static tech.pegasys.teku.spec.SpecMilestone.DENEB; +import static tech.pegasys.teku.spec.SpecMilestone.ELECTRA; import static tech.pegasys.teku.spec.constants.NetworkConstants.SYNC_COMMITTEE_SUBNET_COUNT; import java.util.List; import java.util.Optional; -import org.apache.tuweni.bytes.Bytes32; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.TestTemplate; import tech.pegasys.teku.infrastructure.bytes.Bytes4; @@ -34,58 +36,60 @@ 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.TestSpecInvocationContextProvider.SpecContext; import tech.pegasys.teku.spec.config.SpecConfig; import tech.pegasys.teku.spec.config.SpecConfigDeneb; 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; +import tech.pegasys.teku.storage.storageSystem.InMemoryStorageSystemBuilder; +import tech.pegasys.teku.storage.storageSystem.StorageSystem; -@TestSpecContext(milestone = {SpecMilestone.DENEB, SpecMilestone.ELECTRA}) +@TestSpecContext(milestone = {DENEB, ELECTRA}) class Eth2GossipTopicFilterTest { - - private final RecentChainData recentChainData = mock(RecentChainData.class); - private final UInt64 currentForkEpoch = UInt64.valueOf(10); + private final UInt64 nextMilestoneForkEpoch = UInt64.valueOf(10); + private RecentChainData recentChainData; private Spec spec; - private SpecMilestone specMilestone; - private DataStructureUtil dataStructureUtil; - private ForkInfo forkInfo; + private SpecMilestone currentSpecMilestone; + private SpecMilestone nextSpecMilestone; + private ForkInfo currentForkInfo; private Eth2GossipTopicFilter filter; - private Bytes4 currentForkDigest; private Bytes4 nextForkDigest; @BeforeEach - void setUp(final TestSpecInvocationContextProvider.SpecContext specContext) { - specMilestone = specContext.getSpecMilestone(); + void setUp(final SpecContext specContext) { + // we set up a spec that will be transitioning to specContext.getSpecMilestone() in + // currentForkEpoch epochs + // current milestone is actually the previous milestone + currentSpecMilestone = specContext.getSpecMilestone().getPreviousMilestone(); + nextSpecMilestone = specContext.getSpecMilestone(); spec = - switch (specContext.getSpecMilestone()) { + switch (nextSpecMilestone) { 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); + case DENEB -> TestSpecFactory.createMinimalWithDenebForkEpoch(nextMilestoneForkEpoch); + case ELECTRA -> TestSpecFactory.createMinimalWithElectraForkEpoch(nextMilestoneForkEpoch); }; - dataStructureUtil = new DataStructureUtil(spec); + + final StorageSystem storageSystem = InMemoryStorageSystemBuilder.buildDefault(spec); + storageSystem.chainUpdater().initializeGenesis(); + + recentChainData = spy(storageSystem.recentChainData()); 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); - currentForkDigest = forkInfo.getForkDigest(spec); + currentForkInfo = recentChainData.getCurrentForkInfo().orElseThrow(); 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)); - when(recentChainData.getMilestoneByForkDigest(currentForkDigest)) - .thenReturn(Optional.of(specMilestone)); + .computeForkDigest( + nextFork.getCurrentVersion(), + recentChainData.getGenesisData().orElseThrow().getGenesisValidatorsRoot()); } @TestTemplate @@ -95,7 +99,7 @@ void shouldNotAllowIrrelevantTopics() { @TestTemplate void shouldNotRequireNextForkToBePresent() { - when(recentChainData.getNextFork(any())).thenReturn(Optional.empty()); + doAnswer(invocation -> Optional.empty()).when(recentChainData).getNextFork(any()); assertThat(filter.isRelevantTopic(getTopicName(GossipTopicName.BEACON_BLOCK))).isTrue(); } @@ -129,18 +133,33 @@ void shouldConsiderAllSyncCommitteeSubnetsRelevant() { } @TestTemplate - void shouldConsiderAllBlobSidecarSubnetsRelevant() { - final SpecConfig config = spec.forMilestone(SpecMilestone.DENEB).getConfig(); + void shouldConsiderAllBlobSidecarSubnetsRelevantForCurrentMilestone() { + final SpecConfig config = spec.forMilestone(currentSpecMilestone).getConfig(); + assumeThat(config.toVersionDeneb()).isPresent(); final SpecConfigDeneb specConfigDeneb = SpecConfigDeneb.required(config); for (int i = 0; i < specConfigDeneb.getBlobSidecarSubnetCount(); i++) { assertThat(filter.isRelevantTopic(getTopicName(getBlobSidecarSubnetTopicName(i)))).isTrue(); } } + @TestTemplate + void shouldConsiderAllBlobSidecarSubnetsRelevantForNextMilestone() { + final SpecConfig config = spec.forMilestone(nextSpecMilestone).getConfig(); + assumeThat(config.toVersionDeneb()).isPresent(); + final SpecConfigDeneb specConfigDeneb = SpecConfigDeneb.required(config); + for (int i = 0; i < specConfigDeneb.getBlobSidecarSubnetCount(); i++) { + assertThat(filter.isRelevantTopic(getNextForkTopicName(getBlobSidecarSubnetTopicName(i)))) + .isTrue(); + } + } + @TestTemplate void shouldNotConsiderBlobSidecarWithIncorrectSubnetIdRelevant() { final int blobSidecarSubnetCount = - SpecConfigDeneb.required(spec.forMilestone(specMilestone).getConfig()) + spec.forMilestone(nextSpecMilestone) + .getConfig() + .toVersionDeneb() + .orElseThrow() .getBlobSidecarSubnetCount(); assertThat( filter.isRelevantTopic( @@ -162,11 +181,11 @@ void shouldNotAllowTopicsWithUnknownForkDigest() { } private String getTopicName(final GossipTopicName name) { - return GossipTopics.getTopic(forkInfo.getForkDigest(spec), name, SSZ_SNAPPY); + return GossipTopics.getTopic(currentForkInfo.getForkDigest(spec), name, SSZ_SNAPPY); } private String getTopicName(final String name) { - return GossipTopics.getTopic(forkInfo.getForkDigest(spec), name, SSZ_SNAPPY); + return GossipTopics.getTopic(currentForkInfo.getForkDigest(spec), name, SSZ_SNAPPY); } private String getNextForkTopicName(final GossipTopicName name) {