From 92cebd3f14ef37144b04a0cd87f8f541bcef12cb Mon Sep 17 00:00:00 2001 From: Glory Agatevure Date: Thu, 31 Oct 2024 03:05:45 +0100 Subject: [PATCH] create EphemerySlotValidationService and test (#8759) Signed-off-by: gconnect Co-authored-by: Paul Harris --- .../beaconchain/BeaconChainController.java | 10 +++ .../EphemeryLifecycleException.java | 20 ++++++ .../EphemerySlotValidationService.java | 44 +++++++++++++ .../EphemerySlotValidationServiceTest.java | 63 +++++++++++++++++++ .../teku/TekuDefaultExceptionHandler.java | 4 ++ 5 files changed, 141 insertions(+) create mode 100644 services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/EphemeryLifecycleException.java create mode 100644 services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/EphemerySlotValidationService.java create mode 100644 services/beaconchain/src/test/java/tech/pegasys/teku/services/beaconchain/EphemerySlotValidationServiceTest.java diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainController.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainController.java index 1e3c7a8f399..084e2e6b6ae 100644 --- a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainController.java +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainController.java @@ -114,6 +114,7 @@ import tech.pegasys.teku.spec.logic.common.statetransition.results.BlockImportResult; import tech.pegasys.teku.spec.logic.common.util.BlockRewardCalculatorUtil; import tech.pegasys.teku.spec.logic.versions.deneb.helpers.MiscHelpersDeneb; +import tech.pegasys.teku.spec.networks.Eth2Network; import tech.pegasys.teku.statetransition.EpochCachePrimer; import tech.pegasys.teku.statetransition.LocalOperationAcceptedFilter; import tech.pegasys.teku.statetransition.MappedOperationPool; @@ -219,6 +220,7 @@ public class BeaconChainController extends Service implements BeaconChainControllerFacade { private static final Logger LOG = LogManager.getLogger(); + private final EphemerySlotValidationService ephemerySlotValidationService; protected static final String KEY_VALUE_STORE_SUBDIRECTORY = "kvstore"; @@ -335,6 +337,7 @@ public BeaconChainController( "future_items_size", "Current number of items held for future slots, labelled by type", "type"); + this.ephemerySlotValidationService = new EphemerySlotValidationService(); } @Override @@ -364,6 +367,12 @@ protected void startServices() { blobSidecar -> recentBlobSidecarsFetcher.cancelRecentBlobSidecarRequest( new BlobIdentifier(blobSidecar.getBlockRoot(), blobSidecar.getIndex()))); + + final Optional network = beaconConfig.eth2NetworkConfig().getEth2Network(); + if (network.isPresent() && network.get() == Eth2Network.EPHEMERY) { + LOG.debug("BeaconChainController: subscribing to slot events"); + eventChannels.subscribe(SlotEventsChannel.class, ephemerySlotValidationService); + } SafeFuture.allOfFailFast( attestationManager.start(), p2pNetwork.start(), @@ -401,6 +410,7 @@ protected SafeFuture doStop() { attestationManager.stop(), p2pNetwork.stop(), timerService.stop(), + ephemerySlotValidationService.doStop(), SafeFuture.fromRunnable( () -> terminalPowBlockMonitor.ifPresent(TerminalPowBlockMonitor::stop))) .thenRun(forkChoiceExecutor::stop); diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/EphemeryLifecycleException.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/EphemeryLifecycleException.java new file mode 100644 index 00000000000..66fc67919f3 --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/EphemeryLifecycleException.java @@ -0,0 +1,20 @@ +/* + * Copyright Consensys Software Inc., 2024 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package tech.pegasys.teku.services.beaconchain; + +public class EphemeryLifecycleException extends RuntimeException { + public EphemeryLifecycleException(final String format) { + super(format); + } +} diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/EphemerySlotValidationService.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/EphemerySlotValidationService.java new file mode 100644 index 00000000000..e2113cfe62b --- /dev/null +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/EphemerySlotValidationService.java @@ -0,0 +1,44 @@ +/* + * Copyright Consensys Software Inc., 2024 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package tech.pegasys.teku.services.beaconchain; + +import static tech.pegasys.teku.networks.EphemeryNetwork.MAX_EPHEMERY_SLOT; + +import tech.pegasys.teku.ethereum.events.SlotEventsChannel; +import tech.pegasys.teku.infrastructure.async.SafeFuture; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.service.serviceutils.Service; + +public class EphemerySlotValidationService extends Service implements SlotEventsChannel { + + @Override + public void onSlot(final UInt64 slot) { + if (slot.isGreaterThan(MAX_EPHEMERY_SLOT)) { + throw new EphemeryLifecycleException( + String.format( + "Slot %s exceeds maximum allowed slot %s for ephemery network", + slot, MAX_EPHEMERY_SLOT)); + } + } + + @Override + protected SafeFuture doStart() { + return SafeFuture.COMPLETE; + } + + @Override + protected SafeFuture doStop() { + return SafeFuture.COMPLETE; + } +} diff --git a/services/beaconchain/src/test/java/tech/pegasys/teku/services/beaconchain/EphemerySlotValidationServiceTest.java b/services/beaconchain/src/test/java/tech/pegasys/teku/services/beaconchain/EphemerySlotValidationServiceTest.java new file mode 100644 index 00000000000..beec1565876 --- /dev/null +++ b/services/beaconchain/src/test/java/tech/pegasys/teku/services/beaconchain/EphemerySlotValidationServiceTest.java @@ -0,0 +1,63 @@ +/* + * Copyright Consensys Software Inc., 2024 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package tech.pegasys.teku.services.beaconchain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static tech.pegasys.teku.networks.EphemeryNetwork.MAX_EPHEMERY_SLOT; + +import java.util.Optional; +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.networks.Eth2Network; + +class EphemerySlotValidationServiceTest { + private EphemerySlotValidationService ephemerySlotValidationService; + + @BeforeEach + void setUp() { + ephemerySlotValidationService = new EphemerySlotValidationService(); + } + + @Test + public void onSlot_shouldNotThrow_whenSlotIsValid() { + ephemerySlotValidationService.onSlot(UInt64.valueOf(MAX_EPHEMERY_SLOT)); + } + + @Test + public void onSlot_shouldThrowException_whenSlotExceedsMaxEphemerySlot_forEphemeryNetwork() { + final Eth2Network network = Eth2Network.EPHEMERY; + final Optional ephemeryNetwork = Optional.of(network); + final UInt64 invalidSlot = UInt64.valueOf(MAX_EPHEMERY_SLOT + 1); + + assertThat(ephemeryNetwork).contains(Eth2Network.EPHEMERY); + assertThatThrownBy(() -> ephemerySlotValidationService.onSlot(invalidSlot)) + .isInstanceOf(EphemeryLifecycleException.class) + .hasMessageContaining( + String.format( + "Slot %s exceeds maximum allowed slot %s for ephemery network", + invalidSlot, MAX_EPHEMERY_SLOT)); + } + + @Test + void shouldCompleteWhenServiceStartsAndStops() { + final SafeFuture startFuture = ephemerySlotValidationService.doStart(); + assertTrue(startFuture.isDone()); + final SafeFuture stopFuture = ephemerySlotValidationService.doStop(); + assertTrue(stopFuture.isDone()); + } +} diff --git a/teku/src/main/java/tech/pegasys/teku/TekuDefaultExceptionHandler.java b/teku/src/main/java/tech/pegasys/teku/TekuDefaultExceptionHandler.java index 7dd85615e62..5ed2d93d177 100644 --- a/teku/src/main/java/tech/pegasys/teku/TekuDefaultExceptionHandler.java +++ b/teku/src/main/java/tech/pegasys/teku/TekuDefaultExceptionHandler.java @@ -29,6 +29,7 @@ import tech.pegasys.teku.infrastructure.exceptions.ExceptionUtil; import tech.pegasys.teku.infrastructure.exceptions.FatalServiceFailureException; import tech.pegasys.teku.infrastructure.logging.StatusLogger; +import tech.pegasys.teku.services.beaconchain.EphemeryLifecycleException; import tech.pegasys.teku.storage.server.DatabaseStorageException; import tech.pegasys.teku.storage.server.ShuttingDownException; @@ -85,6 +86,9 @@ private void handleException(final Throwable exception, final String subscriberD } else if (exception instanceof OutOfMemoryError) { statusLog.fatalError(subscriberDescription, exception); System.exit(ERROR_EXIT_CODE); + } else if (exception instanceof EphemeryLifecycleException) { + statusLog.fatalError(subscriberDescription, exception); + System.exit(ERROR_EXIT_CODE); } else if (exception instanceof ShuttingDownException) { LOG.debug("Shutting down", exception); } else if (isExpectedNettyError(exception)) {