From 855bf5a69f4ae553fa459b63eadc0889d2dbf329 Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Thu, 30 Oct 2025 21:55:05 -0400
Subject: [PATCH 01/23] refactor: 21915 Introduce StateLifecycleManager
Signed-off-by: Ivan Malygin
---
.../node/app/HederaVirtualMapState.java | 14 --
.../app/state/merkle/SerializationTest.java | 47 ++--
.../node/app/fixtures/state/FakeState.java | 6 -
.../BlockStreamRecoveryWorkflow.java | 10 +-
.../otter/fixtures/app/OtterAppState.java | 6 -
.../ConsistencyTestingToolState.java | 6 -
.../swirlds/demo/iss/ISSTestingToolState.java | 6 -
.../migration/MigrationTestingToolState.java | 6 -
.../com/swirlds/platform/SwirldsPlatform.java | 21 +-
.../platform/builder/PlatformBuilder.java | 8 +-
.../builder/PlatformBuildingBlocks.java | 9 +-
.../builder/PlatformComponentBuilder.java | 7 +-
.../cli/GenesisPlatformStateCommand.java | 11 +-
.../DefaultSavedStateController.java | 10 +-
.../DefaultTransactionHandler.java | 27 +--
.../platform/gossip/SyncGossipModular.java | 14 +-
.../protocol/ReconnectStateSyncProtocol.java | 10 +-
.../reconnect/ReconnectController.java | 12 +-
.../reconnect/ReconnectStatePeerProtocol.java | 10 +-
.../recovery/EventRecoveryWorkflow.java | 13 +-
.../platform/state/SwirldStateManager.java | 131 -----------
.../state/editor/StateEditorSave.java | 12 +-
.../platform/state/signed/SignedState.java | 3 +-
.../snapshot/DefaultStateSnapshotManager.java | 14 +-
.../state/snapshot/SignedStateFileWriter.java | 67 +++---
.../SignedStateFileReadWriteTest.java | 26 ++-
.../platform/StateFileManagerTests.java | 32 +--
.../DefaultTransactionHandlerTests.java | 6 +-
.../TransactionHandlerTester.java | 21 +-
.../reconnect/ReconnectControllerTest.java | 14 +-
.../ReconnectStatePeerProtocolTests.java | 24 +--
...s.java => StateLifecycleManagerTests.java} | 33 +--
.../state/StateSignatureCollectorTester.java | 2 +-
.../state/signed/StartupStateUtilsTests.java | 16 +-
.../com/swirlds/state/MerkleNodeState.java | 8 -
.../swirlds/state/MerkleNodeStateAware.java | 16 ++
.../main/java/com/swirlds/state/State.java | 18 --
.../swirlds/state/StateLifecycleManager.java | 63 ++++++
.../merkle/StateLifecycleManagerImpl.java | 203 ++++++++++++++++++
.../swirlds/state/merkle/VirtualMapState.java | 60 ------
.../test/fixtures/merkle/MerkleStateRoot.java | 26 ---
.../fixtures/merkle/TestVirtualMapState.java | 8 -
42 files changed, 571 insertions(+), 485 deletions(-)
delete mode 100644 platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/SwirldStateManager.java
rename platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/{SwirldsStateManagerTests.java => StateLifecycleManagerTests.java} (81%)
create mode 100644 platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeStateAware.java
create mode 100644 platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
create mode 100644 platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/HederaVirtualMapState.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/HederaVirtualMapState.java
index bea9ddc01cdb..f00f53f28b08 100644
--- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/HederaVirtualMapState.java
+++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/HederaVirtualMapState.java
@@ -70,20 +70,6 @@ protected HederaVirtualMapState copyingConstructor() {
return new HederaVirtualMapState(this);
}
- /**
- * Creates a new instance of {@link HederaVirtualMapState} with the specified {@link VirtualMap}.
- *
- * @param virtualMap the virtual map whose metrics must already be registered
- * @param metrics the platform metric instance to use when creating the new instance of state
- * @param time the time instance to use when creating the new instance of state
- * @return a new instance of {@link HederaVirtualMapState}
- */
- @Override
- protected HederaVirtualMapState newInstance(
- @NonNull final VirtualMap virtualMap, @NonNull final Metrics metrics, @NonNull final Time time) {
- return new HederaVirtualMapState(virtualMap, metrics, time);
- }
-
/**
* {@inheritDoc}
*/
diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java
index 4eced062e267..22739d3011c9 100644
--- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java
+++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java
@@ -11,8 +11,10 @@
import com.hedera.node.app.spi.fixtures.TestSchema;
import com.hedera.node.app.spi.migrate.StartupNetworks;
import com.hedera.node.config.data.HederaConfig;
+import com.swirlds.base.test.fixtures.time.FakeTime;
import com.swirlds.common.io.utility.LegacyTemporaryFileBuilder;
import com.swirlds.common.merkle.utility.MerkleTreeSnapshotReader;
+import com.swirlds.common.metrics.noop.NoOpMetrics;
import com.swirlds.config.api.Configuration;
import com.swirlds.config.extensions.sources.SimpleConfigSource;
import com.swirlds.config.extensions.test.fixtures.TestConfigBuilder;
@@ -20,9 +22,11 @@
import com.swirlds.platform.test.fixtures.state.RandomSignedStateGenerator;
import com.swirlds.state.MerkleNodeState;
import com.swirlds.state.State;
+import com.swirlds.state.StateLifecycleManager;
import com.swirlds.state.lifecycle.MigrationContext;
import com.swirlds.state.lifecycle.Schema;
import com.swirlds.state.lifecycle.StateDefinition;
+import com.swirlds.state.merkle.StateLifecycleManagerImpl;
import com.swirlds.state.merkle.disk.OnDiskReadableKVState;
import com.swirlds.state.merkle.disk.OnDiskWritableKVState;
import com.swirlds.state.spi.ReadableKVState;
@@ -157,11 +161,12 @@ private void forceFlush(ReadableKVState, ?> state) {
@ParameterizedTest
@ValueSource(booleans = {true, false})
void simpleReadAndWrite(boolean forceFlush) throws IOException, ConstructableRegistryException {
- final var schemaV1 = createV1Schema();
- final var originalTree = createMerkleHederaState(schemaV1);
+ final Schema schemaV1 = createV1Schema();
+ final StateLifecycleManager stateLifecycleManager = createStateLifecycleManager(schemaV1);
+ final MerkleNodeState originalTree = stateLifecycleManager.getMutableState();
// When we serialize it to bytes and deserialize it back into a tree
- MerkleNodeState copy = originalTree.copy(); // make a copy to make VM flushable
+ MerkleNodeState copy = stateLifecycleManager.copyMutableState(); // make a copy to make VM flushable
final byte[] serializedBytes;
if (forceFlush) {
// Force flush the VMs to disk to test serialization and deserialization
@@ -188,21 +193,23 @@ void simpleReadAndWrite(boolean forceFlush) throws IOException, ConstructableReg
@Test
void snapshot() throws IOException {
- final var schemaV1 = createV1Schema();
- final var originalTree = createMerkleHederaState(schemaV1);
- final var tempDir = LegacyTemporaryFileBuilder.buildTemporaryDirectory(config);
+ final Schema schemaV1 = createV1Schema();
+ final StateLifecycleManager stateLifecycleManager = createStateLifecycleManager(schemaV1);
+ final Path tempDir = LegacyTemporaryFileBuilder.buildTemporaryDirectory(config);
+ final MerkleNodeState originalTree = stateLifecycleManager.getLatestImmutableState();
// prepare the tree and create a snapshot
- originalTree.copy().release();
+ stateLifecycleManager.getMutableState().release();
originalTree.computeHash();
- originalTree.createSnapshot(tempDir);
+ stateLifecycleManager.setSnapshotSource(() -> originalTree);
+ stateLifecycleManager.createSnapshot(tempDir);
+ originalTree.release();
final MerkleNodeState state =
- originalTree.loadSnapshot(tempDir.resolve(MerkleTreeSnapshotReader.SIGNED_STATE_FILE_NAME));
+ stateLifecycleManager.loadSnapshot(tempDir.resolve(MerkleTreeSnapshotReader.SIGNED_STATE_FILE_NAME));
initServices(schemaV1, state);
assertTree(state);
- originalTree.release();
state.release();
}
@@ -213,13 +220,16 @@ void snapshot() throws IOException {
*/
@Test
void dualReadAndWrite() throws IOException, ConstructableRegistryException {
- final var schemaV1 = createV1Schema();
- final var originalTree = createMerkleHederaState(schemaV1);
+ final Schema schemaV1 = createV1Schema();
+ final StateLifecycleManager stateLifecycleManager = createStateLifecycleManager(schemaV1);
+ final MerkleNodeState originalTree = stateLifecycleManager.getMutableState();
- MerkleNodeState copy = originalTree.copy(); // make a copy to make VM flushable
+ MerkleNodeState copy = stateLifecycleManager.copyMutableState(); // make a copy to make VM flushable
forceFlush(originalTree.getReadableStates(FIRST_SERVICE).get(FRUIT_STATE_ID));
- copy.copy().release(); // make a fast copy because we can only write to disk an immutable copy
+ stateLifecycleManager
+ .copyMutableState()
+ .release(); // make a fast copy because we can only write to disk an immutable copy
copy.getRoot().getHash();
final byte[] serializedBytes = writeTree(copy.getRoot(), dir);
@@ -275,7 +285,7 @@ private void initServices(Schema schemaV1, MerkleNodeState load
loadedTree.getRoot().migrate(MINIMUM_SUPPORTED_VERSION);
}
- private MerkleNodeState createMerkleHederaState(Schema schemaV1) {
+ private StateLifecycleManager createStateLifecycleManager(Schema schemaV1) {
final SignedState randomState =
new RandomSignedStateGenerator().setRound(1).build();
@@ -295,7 +305,12 @@ private MerkleNodeState createMerkleHederaState(Schema schemaV1) {
migrationStateChanges,
startupNetworks,
TEST_PLATFORM_STATE_FACADE);
- return originalTreeCopy;
+
+ final StateLifecycleManager stateLifecycleManager =
+ new StateLifecycleManagerImpl<>(new NoOpMetrics(), new FakeTime(), TestVirtualMapState::new);
+
+ stateLifecycleManager.initState(originalTreeCopy, true);
+ return stateLifecycleManager;
}
private static void populateVmCache(State loadedTree) {
diff --git a/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeState.java b/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeState.java
index 6a5dd4d7b5c4..a8c81e8bae5c 100644
--- a/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeState.java
+++ b/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeState.java
@@ -33,7 +33,6 @@
import com.swirlds.state.test.fixtures.MapWritableStates;
import com.swirlds.state.test.fixtures.merkle.TestVirtualMapState;
import edu.umd.cs.findbugs.annotations.NonNull;
-import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -303,9 +302,4 @@ public long queueElementPath(final int stateId, @NonNull final Bytes expectedVal
public void initializeState(@NonNull final StateMetadata, ?> md) {
// do nothing
}
-
- @Override
- public MerkleNodeState loadSnapshot(@NonNull final Path targetPath) {
- throw new UnsupportedOperationException();
- }
}
diff --git a/hedera-state-validator/src/main/java/com/hedera/statevalidation/blockstream/BlockStreamRecoveryWorkflow.java b/hedera-state-validator/src/main/java/com/hedera/statevalidation/blockstream/BlockStreamRecoveryWorkflow.java
index f62a848c74de..5449b1a0ca21 100644
--- a/hedera-state-validator/src/main/java/com/hedera/statevalidation/blockstream/BlockStreamRecoveryWorkflow.java
+++ b/hedera-state-validator/src/main/java/com/hedera/statevalidation/blockstream/BlockStreamRecoveryWorkflow.java
@@ -8,6 +8,7 @@
import com.hedera.hapi.block.stream.Block;
import com.hedera.hapi.block.stream.BlockItem;
import com.hedera.hapi.block.stream.output.StateChanges;
+import com.hedera.node.app.HederaVirtualMapState;
import com.hedera.node.app.hapi.utils.blocks.BlockStreamAccess;
import com.hedera.node.app.hapi.utils.blocks.BlockStreamUtils;
import com.hedera.statevalidation.util.PlatformContextHelper;
@@ -18,6 +19,8 @@
import com.swirlds.platform.state.snapshot.DeserializedSignedState;
import com.swirlds.platform.state.snapshot.SignedStateFileWriter;
import com.swirlds.state.MerkleNodeState;
+import com.swirlds.state.StateLifecycleManager;
+import com.swirlds.state.merkle.StateLifecycleManagerImpl;
import com.swirlds.state.spi.CommittableWritableStates;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.IOException;
@@ -149,9 +152,14 @@ public void applyBlocks(
false,
DEFAULT_PLATFORM_STATE_FACADE);
+ final StateLifecycleManager stateLifecycleManager = new StateLifecycleManagerImpl<>(
+ platformContext.getMetrics(),
+ platformContext.getTime(),
+ vm -> new HederaVirtualMapState(vm, platformContext.getMetrics(), platformContext.getTime()));
+ stateLifecycleManager.setSnapshotSource(signedState);
try {
SignedStateFileWriter.writeSignedStateFilesToDirectory(
- platformContext, selfId, outputPath, signedState, DEFAULT_PLATFORM_STATE_FACADE);
+ platformContext, selfId, outputPath, DEFAULT_PLATFORM_STATE_FACADE, stateLifecycleManager);
} catch (IOException e) {
throw new RuntimeException(e);
}
diff --git a/platform-sdk/consensus-otter-tests/src/testFixtures/java/org/hiero/otter/fixtures/app/OtterAppState.java b/platform-sdk/consensus-otter-tests/src/testFixtures/java/org/hiero/otter/fixtures/app/OtterAppState.java
index 80722bbaef99..ef753bd91844 100644
--- a/platform-sdk/consensus-otter-tests/src/testFixtures/java/org/hiero/otter/fixtures/app/OtterAppState.java
+++ b/platform-sdk/consensus-otter-tests/src/testFixtures/java/org/hiero/otter/fixtures/app/OtterAppState.java
@@ -82,12 +82,6 @@ protected OtterAppState copyingConstructor() {
return new OtterAppState(this);
}
- @Override
- protected OtterAppState newInstance(
- @NonNull final VirtualMap virtualMap, @NonNull final Metrics metrics, @NonNull final Time time) {
- return new OtterAppState(virtualMap, metrics, time);
- }
-
/**
* {@inheritDoc}
*/
diff --git a/platform-sdk/platform-apps/tests/ConsistencyTestingTool/src/main/java/com/swirlds/demo/consistency/ConsistencyTestingToolState.java b/platform-sdk/platform-apps/tests/ConsistencyTestingTool/src/main/java/com/swirlds/demo/consistency/ConsistencyTestingToolState.java
index 97778355e090..b41c9e329adc 100644
--- a/platform-sdk/platform-apps/tests/ConsistencyTestingTool/src/main/java/com/swirlds/demo/consistency/ConsistencyTestingToolState.java
+++ b/platform-sdk/platform-apps/tests/ConsistencyTestingTool/src/main/java/com/swirlds/demo/consistency/ConsistencyTestingToolState.java
@@ -119,12 +119,6 @@ protected ConsistencyTestingToolState copyingConstructor() {
return new ConsistencyTestingToolState(this);
}
- @Override
- protected ConsistencyTestingToolState newInstance(
- @NonNull final VirtualMap virtualMap, @NonNull final Metrics metrics, @NonNull final Time time) {
- return new ConsistencyTestingToolState(virtualMap, metrics, time);
- }
-
/**
* Initialize the state
*/
diff --git a/platform-sdk/platform-apps/tests/ISSTestingTool/src/main/java/com/swirlds/demo/iss/ISSTestingToolState.java b/platform-sdk/platform-apps/tests/ISSTestingTool/src/main/java/com/swirlds/demo/iss/ISSTestingToolState.java
index 9943d4786989..732781ed0a88 100644
--- a/platform-sdk/platform-apps/tests/ISSTestingTool/src/main/java/com/swirlds/demo/iss/ISSTestingToolState.java
+++ b/platform-sdk/platform-apps/tests/ISSTestingTool/src/main/java/com/swirlds/demo/iss/ISSTestingToolState.java
@@ -98,12 +98,6 @@ protected ISSTestingToolState copyingConstructor() {
return new ISSTestingToolState(this);
}
- @Override
- protected ISSTestingToolState newInstance(
- @NonNull final VirtualMap virtualMap, @NonNull final Metrics metrics, @NonNull final Time time) {
- return new ISSTestingToolState(virtualMap, metrics, time);
- }
-
public void initState(InitTrigger trigger, Platform platform) {
throwIfImmutable();
diff --git a/platform-sdk/platform-apps/tests/MigrationTestingTool/src/main/java/com/swirlds/demo/migration/MigrationTestingToolState.java b/platform-sdk/platform-apps/tests/MigrationTestingTool/src/main/java/com/swirlds/demo/migration/MigrationTestingToolState.java
index 951983d56bea..b705d02e2b52 100644
--- a/platform-sdk/platform-apps/tests/MigrationTestingTool/src/main/java/com/swirlds/demo/migration/MigrationTestingToolState.java
+++ b/platform-sdk/platform-apps/tests/MigrationTestingTool/src/main/java/com/swirlds/demo/migration/MigrationTestingToolState.java
@@ -32,12 +32,6 @@ protected MigrationTestingToolState copyingConstructor() {
return new MigrationTestingToolState(this);
}
- @Override
- protected MigrationTestingToolState newInstance(
- @NonNull final VirtualMap virtualMap, @NonNull final Metrics metrics, @NonNull final Time time) {
- return new MigrationTestingToolState(virtualMap, metrics, time);
- }
-
/**
* {@inheritDoc}
*/
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
index ae57f6da936d..b1b8a9e2203f 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
@@ -38,7 +38,6 @@
import com.swirlds.platform.publisher.PlatformPublisher;
import com.swirlds.platform.reconnect.DefaultSignedStateValidator;
import com.swirlds.platform.reconnect.ReconnectController;
-import com.swirlds.platform.state.SwirldStateManager;
import com.swirlds.platform.state.nexus.DefaultLatestCompleteStateNexus;
import com.swirlds.platform.state.nexus.LatestCompleteStateNexus;
import com.swirlds.platform.state.nexus.LockFreeStateNexus;
@@ -59,6 +58,7 @@
import com.swirlds.platform.wiring.PlatformComponents;
import com.swirlds.platform.wiring.PlatformCoordinator;
import com.swirlds.state.State;
+import com.swirlds.state.StateLifecycleManager;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.time.Duration;
import java.util.List;
@@ -138,6 +138,11 @@ public class SwirldsPlatform implements Platform {
*/
private final SavedStateController savedStateController;
+ /**
+ * Manages the lifecycle of the state.
+ */
+ private final StateLifecycleManager stateLifecycleManager;
+
/**
* Encapsulated wiring for the platform.
*/
@@ -185,7 +190,8 @@ public SwirldsPlatform(@NonNull final PlatformComponentBuilder builder) {
final LatestCompleteStateNexus latestCompleteStateNexus = new DefaultLatestCompleteStateNexus(platformContext);
- savedStateController = new DefaultSavedStateController(platformContext);
+ savedStateController = new DefaultSavedStateController(platformContext, blocks.stateLifecycleManager());
+ stateLifecycleManager = blocks.stateLifecycleManager();
final SignedStateMetrics signedStateMetrics = new SignedStateMetrics(platformContext.getMetrics());
final StateSignatureCollector stateSignatureCollector =
@@ -211,15 +217,15 @@ public SwirldsPlatform(@NonNull final PlatformComponentBuilder builder) {
initializeState(this, platformContext, initialState, blocks.consensusStateEventHandler(), platformStateFacade);
// This object makes a copy of the state. After this point, initialState becomes immutable.
- final SwirldStateManager swirldStateManager = blocks.swirldStateManager();
- swirldStateManager.setState(initialState.getState(), true);
- platformStateFacade.setCreationSoftwareVersionTo(swirldStateManager.getConsensusState(), blocks.appVersion());
+ final StateLifecycleManager stateLifecycleManager = blocks.stateLifecycleManager();
+ stateLifecycleManager.initState(initialState.getState(), true);
+ platformStateFacade.setCreationSoftwareVersionTo(stateLifecycleManager.getMutableState(), blocks.appVersion());
final EventWindowManager eventWindowManager = new DefaultEventWindowManager();
blocks.freezeCheckHolder()
.setFreezeCheckRef(instant ->
- platformStateFacade.isInFreezePeriod(instant, swirldStateManager.getConsensusState()));
+ platformStateFacade.isInFreezePeriod(instant, stateLifecycleManager.getMutableState()));
final AppNotifier appNotifier = new DefaultAppNotifier(blocks.notificationEngine());
@@ -231,7 +237,7 @@ public SwirldsPlatform(@NonNull final PlatformComponentBuilder builder) {
this,
platformContext,
platformCoordinator,
- swirldStateManager,
+ stateLifecycleManager,
savedStateController,
blocks.consensusStateEventHandler(),
blocks.reservedSignedStateResultPromise(),
@@ -382,6 +388,7 @@ public void performPcesRecovery() {
} else {
final SignedState signedState = reservedState.get();
signedState.markAsStateToSave(StateToDiskReason.PCES_RECOVERY_COMPLETE);
+ stateLifecycleManager.setSnapshotSource(signedState);
final StateDumpRequest request =
StateDumpRequest.create(signedState.reserve("dumping PCES recovery state"));
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuilder.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuilder.java
index e8eea69836a9..a8b680d980b6 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuilder.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuilder.java
@@ -34,7 +34,6 @@
import com.swirlds.platform.reconnect.FallenBehindMonitor;
import com.swirlds.platform.scratchpad.Scratchpad;
import com.swirlds.platform.state.ConsensusStateEventHandler;
-import com.swirlds.platform.state.SwirldStateManager;
import com.swirlds.platform.state.iss.IssScratchpad;
import com.swirlds.platform.state.service.PlatformStateFacade;
import com.swirlds.platform.state.signed.ReservedSignedState;
@@ -42,6 +41,8 @@
import com.swirlds.platform.wiring.PlatformComponents;
import com.swirlds.platform.wiring.PlatformWiring;
import com.swirlds.state.MerkleNodeState;
+import com.swirlds.state.StateLifecycleManager;
+import com.swirlds.state.merkle.StateLifecycleManagerImpl;
import com.swirlds.virtualmap.VirtualMap;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.IOException;
@@ -433,7 +434,8 @@ public PlatformComponentBuilder buildComponentBuilder() {
final ApplicationCallbacks callbacks =
new ApplicationCallbacks(preconsensusEventConsumer, snapshotOverrideConsumer, staleEventConsumer);
- final SwirldStateManager swirldStateManager = new SwirldStateManager(platformContext, currentRoster);
+ final StateLifecycleManager stateLifecycleManager = new StateLifecycleManagerImpl(
+ platformContext.getMetrics(), platformContext.getTime(), createStateFromVirtualMap);
if (model == null) {
final WiringConfig wiringConfig = platformContext.getConfiguration().getConfigData(WiringConfig.class);
@@ -495,7 +497,7 @@ public PlatformComponentBuilder buildComponentBuilder() {
issScratchpad,
NotificationEngine.buildEngine(getStaticThreadManager()),
new AtomicReference<>(),
- swirldStateManager,
+ stateLifecycleManager,
new AtomicReference<>(),
firstPlatform,
consensusStateEventHandler,
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuildingBlocks.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuildingBlocks.java
index 40a36c60952b..5cf9520c7cee 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuildingBlocks.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuildingBlocks.java
@@ -15,13 +15,14 @@
import com.swirlds.platform.reconnect.FallenBehindMonitor;
import com.swirlds.platform.scratchpad.Scratchpad;
import com.swirlds.platform.state.ConsensusStateEventHandler;
-import com.swirlds.platform.state.SwirldStateManager;
import com.swirlds.platform.state.iss.IssScratchpad;
import com.swirlds.platform.state.service.PlatformStateFacade;
import com.swirlds.platform.state.signed.ReservedSignedState;
+import com.swirlds.platform.state.signed.SignedState;
import com.swirlds.platform.system.status.StatusActionSubmitter;
import com.swirlds.platform.wiring.PlatformComponents;
import com.swirlds.state.MerkleNodeState;
+import com.swirlds.state.StateLifecycleManager;
import com.swirlds.virtualmap.VirtualMap;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
@@ -72,7 +73,7 @@
* @param notificationEngine for sending notifications to the application (legacy pattern)
* @param statusActionSubmitterReference a reference to the status action submitter, this can be deleted once
* platform status management is handled by the wiring framework
- * @param swirldStateManager responsible for the mutable state, this is exposed here due to
+ * @param stateLifecycleManager responsible for the mutable state, this is exposed here due to
* reconnect
* @param getLatestCompleteStateReference a reference to a supplier that supplies the latest immutable state,
* this is exposed here due to reconnect, can be removed once reconnect is
@@ -110,7 +111,7 @@ public record PlatformBuildingBlocks(
@NonNull Scratchpad issScratchpad,
@NonNull NotificationEngine notificationEngine,
@NonNull AtomicReference statusActionSubmitterReference,
- @NonNull SwirldStateManager swirldStateManager,
+ @NonNull StateLifecycleManager stateLifecycleManager,
@NonNull AtomicReference> getLatestCompleteStateReference,
boolean firstPlatform,
@NonNull ConsensusStateEventHandler consensusStateEventHandler,
@@ -140,7 +141,7 @@ public record PlatformBuildingBlocks(
requireNonNull(issScratchpad);
requireNonNull(notificationEngine);
requireNonNull(statusActionSubmitterReference);
- requireNonNull(swirldStateManager);
+ requireNonNull(stateLifecycleManager);
requireNonNull(getLatestCompleteStateReference);
requireNonNull(consensusStateEventHandler);
requireNonNull(platformStateFacade);
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformComponentBuilder.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformComponentBuilder.java
index ebef4d2efd28..cd9239c2a2ad 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformComponentBuilder.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformComponentBuilder.java
@@ -804,7 +804,7 @@ public Gossip buildGossip() {
blocks.rosterHistory().getCurrentRoster(),
blocks.selfId(),
blocks.appVersion(),
- blocks.swirldStateManager(),
+ blocks.stateLifecycleManager(),
() -> blocks.getLatestCompleteStateReference().get().get(),
blocks.intakeEventCounter(),
blocks.platformStateFacade(),
@@ -882,7 +882,8 @@ public StateSnapshotManager buildStateSnapshotManager() {
actualMainClassName,
blocks.selfId(),
blocks.swirldName(),
- blocks.platformStateFacade());
+ blocks.platformStateFacade(),
+ blocks.stateLifecycleManager());
}
return stateSnapshotManager;
}
@@ -1042,7 +1043,7 @@ public TransactionHandler buildTransactionHandler() {
if (transactionHandler == null) {
transactionHandler = new DefaultTransactionHandler(
blocks.platformContext(),
- blocks.swirldStateManager(),
+ blocks.stateLifecycleManager(),
blocks.statusActionSubmitterReference().get(),
blocks.appVersion(),
blocks.platformStateFacade(),
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/GenesisPlatformStateCommand.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/GenesisPlatformStateCommand.java
index 0885e4c845d0..192a2de7718e 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/GenesisPlatformStateCommand.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/GenesisPlatformStateCommand.java
@@ -16,10 +16,13 @@
import com.swirlds.platform.state.PlatformStateAccessor;
import com.swirlds.platform.state.service.PlatformStateFacade;
import com.swirlds.platform.state.signed.ReservedSignedState;
+import com.swirlds.platform.state.signed.SignedState;
import com.swirlds.platform.state.snapshot.DeserializedSignedState;
import com.swirlds.platform.state.snapshot.SignedStateFileReader;
import com.swirlds.platform.util.BootstrapUtils;
import com.swirlds.state.State;
+import com.swirlds.state.StateLifecycleManager;
+import com.swirlds.state.merkle.StateLifecycleManagerImpl;
import com.swirlds.state.spi.CommittableWritableStates;
import com.swirlds.state.spi.WritableStates;
import java.io.IOException;
@@ -60,6 +63,11 @@ public Integer call() throws IOException, ExecutionException, InterruptedExcepti
BootstrapUtils.setupConstructableRegistry();
final PlatformContext platformContext = PlatformContext.create(configuration);
+ final StateLifecycleManager stateLifecycleManager = new StateLifecycleManagerImpl<>(
+ platformContext.getMetrics(), platformContext.getTime(), (virtualMap) -> {
+ // FUTURE WORK: https://github.com/hiero-ledger/hiero-consensus-node/issues/19003
+ throw new UnsupportedOperationException();
+ });
System.out.printf("Reading from %s %n", statePath.toAbsolutePath());
final PlatformStateFacade stateFacade = DEFAULT_PLATFORM_STATE_FACADE;
@@ -91,8 +99,9 @@ public Integer call() throws IOException, ExecutionException, InterruptedExcepti
.digestTreeAsync(reservedSignedState.get().getState().getRoot())
.get();
System.out.printf("Writing modified state to %s %n", outputDir.toAbsolutePath());
+ stateLifecycleManager.setSnapshotSource(reservedSignedState.get());
writeSignedStateFilesToDirectory(
- platformContext, NO_NODE_ID, outputDir, reservedSignedState.get(), stateFacade);
+ platformContext, NO_NODE_ID, outputDir, stateFacade, stateLifecycleManager);
}
return 0;
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/DefaultSavedStateController.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/DefaultSavedStateController.java
index 3cb420a693f4..3dc076c4abdf 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/DefaultSavedStateController.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/DefaultSavedStateController.java
@@ -13,6 +13,7 @@
import com.swirlds.platform.state.signed.ReservedSignedState;
import com.swirlds.platform.state.signed.SignedState;
import com.swirlds.platform.state.snapshot.StateToDiskReason;
+import com.swirlds.state.StateLifecycleManager;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.time.Instant;
@@ -31,14 +32,19 @@ public class DefaultSavedStateController implements SavedStateController {
private Instant previousSavedStateTimestamp;
private final StateConfig stateConfig;
+ private final StateLifecycleManager stateLifecycleManager;
/**
* Constructor
*
- * @param platformContext the platform context
+ * @param platformContext the platform context
+ * @param stateLifecycleManager
*/
- public DefaultSavedStateController(@NonNull final PlatformContext platformContext) {
+ public DefaultSavedStateController(
+ @NonNull final PlatformContext platformContext,
+ @NonNull final StateLifecycleManager stateLifecycleManager) {
this.stateConfig = platformContext.getConfiguration().getConfigData(StateConfig.class);
+ this.stateLifecycleManager = stateLifecycleManager;
}
/**
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/DefaultTransactionHandler.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/DefaultTransactionHandler.java
index a1556b64b1a8..279360daa267 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/DefaultTransactionHandler.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/DefaultTransactionHandler.java
@@ -25,7 +25,6 @@
import com.swirlds.platform.metrics.TransactionMetrics;
import com.swirlds.platform.state.ConsensusStateEventHandler;
import com.swirlds.platform.state.PlatformStateModifier;
-import com.swirlds.platform.state.SwirldStateManager;
import com.swirlds.platform.state.service.PlatformStateFacade;
import com.swirlds.platform.state.signed.ReservedSignedState;
import com.swirlds.platform.state.signed.SignedState;
@@ -34,6 +33,7 @@
import com.swirlds.platform.wiring.PlatformSchedulersConfig;
import com.swirlds.state.MerkleNodeState;
import com.swirlds.state.State;
+import com.swirlds.state.StateLifecycleManager;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.time.Instant;
@@ -62,7 +62,7 @@ public class DefaultTransactionHandler implements TransactionHandler {
/**
* The class responsible for all interactions with the swirld state
*/
- private final SwirldStateManager swirldStateManager;
+ private final StateLifecycleManager stateLifecycleManager;
private final RoundHandlingMetrics handlerMetrics;
@@ -130,14 +130,14 @@ public class DefaultTransactionHandler implements TransactionHandler {
* Constructor
*
* @param platformContext contains various platform utilities
- * @param swirldStateManager the swirld state manager to send events to
+ * @param stateLifecycleManager the swirld state manager to send events to
* @param statusActionSubmitter enables submitting of platform status actions
* @param softwareVersion the current version of the software
* @param platformStateFacade enables access to the platform state
*/
public DefaultTransactionHandler(
@NonNull final PlatformContext platformContext,
- @NonNull final SwirldStateManager swirldStateManager,
+ @NonNull final StateLifecycleManager stateLifecycleManager,
@NonNull final StatusActionSubmitter statusActionSubmitter,
@NonNull final SemanticVersion softwareVersion,
@NonNull final PlatformStateFacade platformStateFacade,
@@ -145,7 +145,7 @@ public DefaultTransactionHandler(
@NonNull final NodeId selfId) {
this.platformContext = requireNonNull(platformContext);
- this.swirldStateManager = requireNonNull(swirldStateManager);
+ this.stateLifecycleManager = requireNonNull(stateLifecycleManager);
this.statusActionSubmitter = requireNonNull(statusActionSubmitter);
this.softwareVersion = requireNonNull(softwareVersion);
this.consensusStateEventHandler = requireNonNull(consensusStateEventHandler);
@@ -205,7 +205,7 @@ public TransactionHandlerResult handleConsensusRound(@NonNull final ConsensusRou
}
if (platformStateFacade.isInFreezePeriod(
- consensusRound.getConsensusTimestamp(), swirldStateManager.getConsensusState())) {
+ consensusRound.getConsensusTimestamp(), stateLifecycleManager.getMutableState())) {
statusActionSubmitter.submitStatusAction(new FreezePeriodEnteredAction(consensusRound.getRoundNum()));
freezeRoundReceived = true;
logger.info(
@@ -259,7 +259,7 @@ public TransactionHandlerResult handleConsensusRound(@NonNull final ConsensusRou
*/
private Queue> doHandleConsensusRound(
final ConsensusRound round) {
- final MerkleNodeState state = swirldStateManager.getConsensusState();
+ final MerkleNodeState state = stateLifecycleManager.getMutableState();
final Queue> scopedSystemTransactions =
new ConcurrentLinkedQueue<>();
try {
@@ -296,7 +296,7 @@ private Queue> doHandleConsen
* @param round the consensus round
*/
private void updatePlatformState(@NonNull final ConsensusRound round) {
- platformStateFacade.bulkUpdateOf(swirldStateManager.getConsensusState(), v -> {
+ platformStateFacade.bulkUpdateOf(stateLifecycleManager.getMutableState(), v -> {
v.setRound(round.getRoundNum());
v.setConsensusTimestamp(round.getConsensusTimestamp());
v.setCreationSoftwareVersion(softwareVersion);
@@ -312,7 +312,7 @@ private void updatePlatformState(@NonNull final ConsensusRound round) {
* @throws InterruptedException if this thread is interrupted
*/
private void updateRunningEventHash(@NonNull final ConsensusRound round) throws InterruptedException {
- final State consensusState = swirldStateManager.getConsensusState();
+ final State consensusState = stateLifecycleManager.getMutableState();
if (writeLegacyRunningEventHash) {
final CesEvent last = round.getStreamedEvents().getLast();
@@ -350,9 +350,9 @@ private TransactionHandlerResult createSignedState(
@NonNull final Queue> systemTransactions)
throws InterruptedException {
if (freezeRoundReceived) {
- platformStateFacade.updateLastFrozenTime(swirldStateManager.getConsensusState());
+ platformStateFacade.updateLastFrozenTime(stateLifecycleManager.getMutableState());
}
- final MerkleNodeState state = swirldStateManager.getConsensusState();
+ final MerkleNodeState state = stateLifecycleManager.getMutableState();
final boolean isBoundary = consensusStateEventHandler.onSealConsensusRound(consensusRound, state);
final ReservedSignedState reservedSignedState;
if (isBoundary || freezeRoundReceived) {
@@ -366,13 +366,14 @@ but the app may not have done some work that it needs to (like finishing a block
consensusRound.getRoundNum());
}
handlerMetrics.setPhase(GETTING_STATE_TO_SIGN);
- final MerkleNodeState immutableStateCons = swirldStateManager.getStateForSigning();
+ stateLifecycleManager.copyMutableState();
+ final MerkleNodeState immutableState = stateLifecycleManager.getLatestImmutableState();
handlerMetrics.setPhase(CREATING_SIGNED_STATE);
final SignedState signedState = new SignedState(
platformContext.getConfiguration(),
CryptoStatic::verifySignature,
- immutableStateCons,
+ immutableState,
"TransactionHandler.createSignedState()",
freezeRoundReceived,
true,
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gossip/SyncGossipModular.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gossip/SyncGossipModular.java
index 456cbc5f6473..6c2a408f1d7f 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gossip/SyncGossipModular.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gossip/SyncGossipModular.java
@@ -34,12 +34,12 @@
import com.swirlds.platform.network.protocol.rpc.RpcProtocol;
import com.swirlds.platform.reconnect.FallenBehindMonitor;
import com.swirlds.platform.reconnect.ReconnectStateTeacherThrottle;
-import com.swirlds.platform.state.SwirldStateManager;
import com.swirlds.platform.state.service.PlatformStateFacade;
import com.swirlds.platform.state.signed.ReservedSignedState;
import com.swirlds.platform.wiring.NoInput;
import com.swirlds.platform.wiring.components.Gossip;
import com.swirlds.state.MerkleNodeState;
+import com.swirlds.state.StateLifecycleManager;
import com.swirlds.virtualmap.VirtualMap;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.security.cert.X509Certificate;
@@ -72,7 +72,7 @@ public class SyncGossipModular implements Gossip {
private final AbstractSyncProtocol> syncProtocol;
private final FallenBehindMonitor fallenBehindMonitor;
private final AbstractShadowgraphSynchronizer synchronizer;
- private final SwirldStateManager swirldStateManager;
+ private final StateLifecycleManager stateLifecycleManager;
private final Function createStateFromVirtualMap;
// this is not a nice dependency, should be removed as well as the sharedState
@@ -88,7 +88,7 @@ public class SyncGossipModular implements Gossip {
* @param roster the current roster
* @param selfId this node's ID
* @param appVersion the version of the app
- * @param swirldStateManager manages the mutable state
+ * @param stateLifecycleManager manages the mutable state
* @param latestCompleteState holds the latest signed state that has enough signatures to be verifiable
* @param intakeEventCounter keeps track of the number of events in the intake pipeline from each peer
* @param platformStateFacade the facade to access the platform state
@@ -103,7 +103,7 @@ public SyncGossipModular(
@NonNull final Roster roster,
@NonNull final NodeId selfId,
@NonNull final SemanticVersion appVersion,
- @NonNull final SwirldStateManager swirldStateManager,
+ @NonNull final StateLifecycleManager stateLifecycleManager,
@NonNull final Supplier latestCompleteState,
@NonNull final IntakeEventCounter intakeEventCounter,
@NonNull final PlatformStateFacade platformStateFacade,
@@ -131,7 +131,7 @@ public SyncGossipModular(
this.network = new PeerCommunication(platformContext, peers, selfPeer, ownKeysAndCerts);
this.fallenBehindMonitor = fallenBehindMonitor;
- this.swirldStateManager = swirldStateManager;
+ this.stateLifecycleManager = stateLifecycleManager;
this.createStateFromVirtualMap = createStateFromVirtualMap;
final ProtocolConfig protocolConfig = platformContext.getConfiguration().getConfigData(ProtocolConfig.class);
@@ -146,7 +146,7 @@ public SyncGossipModular(
rosterSize,
syncMetrics,
event -> receivedEventHandler.accept(event),
- this.fallenBehindMonitor,
+ fallenBehindMonitor,
intakeEventCounter,
selfId,
lag -> syncLagHandler.accept(lag));
@@ -239,7 +239,7 @@ public ReconnectStateSyncProtocol createStateSyncProtocol(
fallenBehindMonitor,
platformStateFacade,
reservedSignedStateResultPromise,
- swirldStateManager,
+ stateLifecycleManager,
createStateFromVirtualMap);
}
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/network/protocol/ReconnectStateSyncProtocol.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/network/protocol/ReconnectStateSyncProtocol.java
index ec3e15c9e7ea..1e26ab142232 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/network/protocol/ReconnectStateSyncProtocol.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/network/protocol/ReconnectStateSyncProtocol.java
@@ -8,10 +8,10 @@
import com.swirlds.platform.reconnect.FallenBehindMonitor;
import com.swirlds.platform.reconnect.ReconnectStatePeerProtocol;
import com.swirlds.platform.reconnect.ReconnectStateTeacherThrottle;
-import com.swirlds.platform.state.SwirldStateManager;
import com.swirlds.platform.state.service.PlatformStateFacade;
import com.swirlds.platform.state.signed.ReservedSignedState;
import com.swirlds.state.MerkleNodeState;
+import com.swirlds.state.StateLifecycleManager;
import com.swirlds.virtualmap.VirtualMap;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.time.Duration;
@@ -39,7 +39,7 @@ public class ReconnectStateSyncProtocol implements Protocol {
private final PlatformContext platformContext;
private final AtomicReference platformStatus = new AtomicReference<>(PlatformStatus.STARTING_UP);
private final ReservedSignedStateResultPromise reservedSignedStateResultPromise;
- private final SwirldStateManager swirldStateManager;
+ private final StateLifecycleManager stateLifecycleManager;
private final Function createStateFromVirtualMap;
public ReconnectStateSyncProtocol(
@@ -52,7 +52,7 @@ public ReconnectStateSyncProtocol(
@NonNull final FallenBehindMonitor fallenBehindManager,
@NonNull final PlatformStateFacade platformStateFacade,
@NonNull final ReservedSignedStateResultPromise reservedSignedStateResultPromise,
- @NonNull final SwirldStateManager swirldStateManager,
+ @NonNull final StateLifecycleManager stateLifecycleManager,
@NonNull final Function createStateFromVirtualMap) {
this.platformContext = Objects.requireNonNull(platformContext);
@@ -65,7 +65,7 @@ public ReconnectStateSyncProtocol(
this.platformStateFacade = platformStateFacade;
this.time = Objects.requireNonNull(platformContext.getTime());
this.reservedSignedStateResultPromise = Objects.requireNonNull(reservedSignedStateResultPromise);
- this.swirldStateManager = Objects.requireNonNull(swirldStateManager);
+ this.stateLifecycleManager = Objects.requireNonNull(stateLifecycleManager);
this.createStateFromVirtualMap = Objects.requireNonNull(createStateFromVirtualMap);
}
@@ -88,7 +88,7 @@ public ReconnectStatePeerProtocol createPeerInstance(@NonNull final NodeId peerI
time,
platformStateFacade,
reservedSignedStateResultPromise,
- swirldStateManager,
+ stateLifecycleManager,
createStateFromVirtualMap);
}
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectController.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectController.java
index 874223763e03..a72262f9e143 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectController.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectController.java
@@ -18,7 +18,6 @@
import com.swirlds.platform.network.protocol.ReservedSignedStateResultPromise;
import com.swirlds.platform.network.protocol.ReservedSignedStateResultPromise.ReservedSignedStateResult;
import com.swirlds.platform.state.ConsensusStateEventHandler;
-import com.swirlds.platform.state.SwirldStateManager;
import com.swirlds.platform.state.service.PlatformStateFacade;
import com.swirlds.platform.state.signed.SignedState;
import com.swirlds.platform.state.signed.SignedStateValidationData;
@@ -32,6 +31,7 @@
import com.swirlds.platform.system.status.actions.ReconnectCompleteAction;
import com.swirlds.platform.wiring.PlatformCoordinator;
import com.swirlds.state.MerkleNodeState;
+import com.swirlds.state.StateLifecycleManager;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.time.Duration;
@@ -72,7 +72,7 @@ public class ReconnectController implements Runnable {
private final Platform platform;
private final PlatformContext platformContext;
private final PlatformCoordinator platformCoordinator;
- private final SwirldStateManager swirldStateManager;
+ private final StateLifecycleManager stateLifecycleManager;
private final SavedStateController savedStateController;
private final ConsensusStateEventHandler consensusStateEventHandler;
private final ReservedSignedStateResultPromise peerReservedSignedStateResultPromise;
@@ -90,7 +90,7 @@ public ReconnectController(
@NonNull final Platform platform,
@NonNull final PlatformContext platformContext,
@NonNull final PlatformCoordinator platformCoordinator,
- @NonNull final SwirldStateManager swirldStateManager,
+ @NonNull final StateLifecycleManager stateLifecycleManager,
@NonNull final SavedStateController savedStateController,
@NonNull final ConsensusStateEventHandler consensusStateEventHandler,
@NonNull final ReservedSignedStateResultPromise peerReservedSignedStateResultPromise,
@@ -107,7 +107,7 @@ public ReconnectController(
this.reconnectConfig = platformContext.getConfiguration().getConfigData(ReconnectConfig.class);
this.platform = requireNonNull(platform);
this.platformContext = requireNonNull(platformContext);
- this.swirldStateManager = requireNonNull(swirldStateManager);
+ this.stateLifecycleManager = requireNonNull(stateLifecycleManager);
this.savedStateController = requireNonNull(savedStateController);
this.consensusStateEventHandler = requireNonNull(consensusStateEventHandler);
this.time = platformContext.getTime();
@@ -161,7 +161,7 @@ public void run() {
platformCoordinator.clear();
logger.info(RECONNECT.getMarker(), "Queues have been cleared");
- final MerkleNodeState currentState = swirldStateManager.getConsensusState();
+ final MerkleNodeState currentState = stateLifecycleManager.getMutableState();
hashStateForReconnect(merkleCryptography, currentState);
int failedReconnectsInARow = 0;
do {
@@ -261,7 +261,7 @@ private void loadState(@NonNull final SignedState signedState) {
+ Roster.JSON.toJSON(stateRoster) + ")");
}
- swirldStateManager.setState(signedState.getState(), false);
+ stateLifecycleManager.initState(signedState.getState(), false);
// kick off transition to RECONNECT_COMPLETE before beginning to save the reconnect state to disk
// this guarantees that the platform status will be RECONNECT_COMPLETE before the state is saved
platformCoordinator.submitStatusAction(new ReconnectCompleteAction(signedState.getRound()));
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectStatePeerProtocol.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectStatePeerProtocol.java
index d5ad50544e81..2e4e282db531 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectStatePeerProtocol.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectStatePeerProtocol.java
@@ -21,10 +21,10 @@
import com.swirlds.platform.network.NetworkProtocolException;
import com.swirlds.platform.network.protocol.PeerProtocol;
import com.swirlds.platform.network.protocol.ReservedSignedStateResultPromise;
-import com.swirlds.platform.state.SwirldStateManager;
import com.swirlds.platform.state.service.PlatformStateFacade;
import com.swirlds.platform.state.signed.ReservedSignedState;
import com.swirlds.state.MerkleNodeState;
+import com.swirlds.state.StateLifecycleManager;
import com.swirlds.virtualmap.VirtualMap;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.IOException;
@@ -82,7 +82,7 @@ public class ReconnectStatePeerProtocol implements PeerProtocol {
private final Time time;
private final PlatformContext platformContext;
private final ReservedSignedStateResultPromise reservedSignedStateResultPromise;
- private final SwirldStateManager swirldStateManager;
+ private final StateLifecycleManager stateLifecycleManager;
private final Function createStateFromVirtualMap;
/**
@@ -112,7 +112,7 @@ public ReconnectStatePeerProtocol(
@NonNull final Time time,
@NonNull final PlatformStateFacade platformStateFacade,
@NonNull final ReservedSignedStateResultPromise reservedSignedStateResultPromise,
- @NonNull final SwirldStateManager swirldStateManager,
+ @NonNull final StateLifecycleManager stateLifecycleManager,
@NonNull final Function createStateFromVirtualMap) {
this.platformContext = Objects.requireNonNull(platformContext);
@@ -126,7 +126,7 @@ public ReconnectStatePeerProtocol(
this.platformStatusSupplier = Objects.requireNonNull(platformStatusSupplier);
this.platformStateFacade = Objects.requireNonNull(platformStateFacade);
this.reservedSignedStateResultPromise = Objects.requireNonNull(reservedSignedStateResultPromise);
- this.swirldStateManager = Objects.requireNonNull(swirldStateManager);
+ this.stateLifecycleManager = Objects.requireNonNull(stateLifecycleManager);
this.createStateFromVirtualMap = Objects.requireNonNull(createStateFromVirtualMap);
Objects.requireNonNull(time);
@@ -311,7 +311,7 @@ public void runProtocol(final Connection connection)
private void learner(final Connection connection) {
try {
- final MerkleNodeState consensusState = swirldStateManager.getConsensusState();
+ final MerkleNodeState consensusState = stateLifecycleManager.getMutableState();
final ReconnectStateLearner learner = new ReconnectStateLearner(
platformContext,
threadManager,
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/recovery/EventRecoveryWorkflow.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/recovery/EventRecoveryWorkflow.java
index 09c9f5e0b64b..05527497d16b 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/recovery/EventRecoveryWorkflow.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/recovery/EventRecoveryWorkflow.java
@@ -48,6 +48,8 @@
import com.swirlds.platform.system.state.notifications.NewRecoveredStateNotification;
import com.swirlds.state.MerkleNodeState;
import com.swirlds.state.State;
+import com.swirlds.state.StateLifecycleManager;
+import com.swirlds.state.merkle.StateLifecycleManagerImpl;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.IOException;
import java.nio.file.Files;
@@ -155,7 +157,12 @@ public static void recoverState(
.apply(v),
platformStateFacade,
platformContext);
+ final StateLifecycleManager stateLifecycleManager = new StateLifecycleManagerImpl<>(
+ platformContext.getMetrics(),
+ platformContext.getTime(),
+ hederaApp.stateRootFromVirtualMap(platformContext.getMetrics(), platformContext.getTime()));
try (final ReservedSignedState initialState = deserializedSignedState.reservedSignedState()) {
+ stateLifecycleManager.setSnapshotSource(initialState.get());
HederaUtils.updateStateHash(hederaApp, deserializedSignedState);
logger.info(
@@ -192,11 +199,7 @@ public static void recoverState(
recoveredState.state().get().getState().copy();
SignedStateFileWriter.writeSignedStateFilesToDirectory(
- platformContext,
- selfId,
- resultingStateDirectory,
- recoveredState.state().get(),
- platformStateFacade);
+ platformContext, selfId, resultingStateDirectory, platformStateFacade, stateLifecycleManager);
final StateConfig stateConfig = platformContext.getConfiguration().getConfigData(StateConfig.class);
updateEmergencyRecoveryFile(
stateConfig, resultingStateDirectory, initialState.get().getConsensusTimestamp());
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/SwirldStateManager.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/SwirldStateManager.java
deleted file mode 100644
index 522814f5f625..000000000000
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/SwirldStateManager.java
+++ /dev/null
@@ -1,131 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-package com.swirlds.platform.state;
-
-import static com.swirlds.base.units.UnitConstants.NANOSECONDS_TO_MICROSECONDS;
-import static java.util.Objects.requireNonNull;
-
-import com.hedera.hapi.node.state.roster.Roster;
-import com.swirlds.common.context.PlatformContext;
-import com.swirlds.state.MerkleNodeState;
-import com.swirlds.state.State;
-import com.swirlds.state.merkle.StateMetrics;
-import edu.umd.cs.findbugs.annotations.NonNull;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * This class is responsible for maintaining references to the mutable state and the latest immutable state.
- * It also updates these references upon state signing.
- */
-public class SwirldStateManager {
-
- /**
- * Metrics for the state object
- */
- private final StateMetrics stateMetrics;
-
- /**
- * reference to the state that reflects all known consensus transactions
- */
- private final AtomicReference stateRef = new AtomicReference<>();
-
- /**
- * The most recent immutable state. No value until the first fast copy is created.
- */
- private final AtomicReference latestImmutableState = new AtomicReference<>();
-
- /**
- * Constructor.
- *
- * @param platformContext the platform context
- * @param roster the current roster
- */
- public SwirldStateManager(@NonNull final PlatformContext platformContext, @NonNull final Roster roster) {
- requireNonNull(platformContext);
- requireNonNull(roster);
- this.stateMetrics = new StateMetrics(platformContext.getMetrics());
- }
-
- /**
- * Set the initial State for the platform. This method should only be called once.
- *
- * @param state the initial state
- */
- public void setState(@NonNull final MerkleNodeState state, boolean onInit) {
- requireNonNull(state);
-
- state.throwIfDestroyed("state must not be destroyed");
- state.throwIfImmutable("state must be mutable");
-
- if (onInit && stateRef.get() != null) {
- throw new IllegalStateException("Attempt to set initial state when there is already a state reference.");
- }
-
- updateStateRefs(state);
- }
-
- /**
- * Returns the consensus state. The consensus state could become immutable at any time. Modifications must not be
- * made to the returned state.
- */
- public MerkleNodeState getConsensusState() {
- return stateRef.get();
- }
-
- private void updateStateRefs(MerkleNodeState state) {
- // Create a fast copy so there is always an immutable state to
- // invoke handleTransaction on for pre-consensus transactions
- final long copyStart = System.nanoTime();
- // Create a fast copy
- final MerkleNodeState copy = state.copy();
- // Increment the reference count because this reference becomes the new value
- copy.getRoot().reserve();
- final long copyEnd = System.nanoTime();
- stateMetrics.stateCopyMicros((copyEnd - copyStart) * NANOSECONDS_TO_MICROSECONDS);
- // Set latest immutable first to prevent the newly immutable stateRoot from being deleted between setting the
- // stateRef and the latestImmutableState
- setLatestImmutableState(state);
- updateStateRef(copy);
- }
-
- /**
- * Sets the consensus state to the state provided. Must be mutable and have a reference count of at least 1.
- *
- * @param state a new mutable state
- */
- private void updateStateRef(final MerkleNodeState state) {
- final var currVal = stateRef.get();
- if (currVal != null) {
- currVal.release();
- }
- // Do not increment the reference count because the state provided already has a reference count of at least
- // one to represent this reference and to prevent it from being deleted before this reference is set.
- stateRef.set(state);
- }
-
- private void setLatestImmutableState(final MerkleNodeState immutableState) {
- final State currVal = latestImmutableState.get();
- if (currVal != null) {
- currVal.release();
- }
- immutableState.getRoot().reserve();
- latestImmutableState.set(immutableState);
- }
-
- /**
- * Updates the state to a fast copy of itself and returns a reference to the previous state to be used for
- * signing. The reference count of the previous state returned by this is incremented to prevent it from being
- * garbage collected until it is put in a signed state, so callers are responsible for decrementing the reference
- * count when it is no longer needed.
- *
- * Consensus event handling will block until this method returns. Pre-consensus
- * event handling may or may not be blocked depending on the implementation.
- *
- * @return a copy of the state to use for the next signed state
- * @see State#copy()
- */
- public MerkleNodeState getStateForSigning() {
- final MerkleNodeState state = stateRef.get();
- updateStateRefs(state);
- return latestImmutableState.get();
- }
-}
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/editor/StateEditorSave.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/editor/StateEditorSave.java
index ac5045ec75ac..efc3df669f01 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/editor/StateEditorSave.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/editor/StateEditorSave.java
@@ -14,6 +14,9 @@
import com.swirlds.logging.legacy.LogMarker;
import com.swirlds.platform.config.DefaultConfiguration;
import com.swirlds.platform.state.signed.ReservedSignedState;
+import com.swirlds.platform.state.signed.SignedState;
+import com.swirlds.state.StateLifecycleManager;
+import com.swirlds.state.merkle.StateLifecycleManagerImpl;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
@@ -46,6 +49,12 @@ public void run() {
final PlatformContext platformContext = PlatformContext.create(configuration);
logger.info(LogMarker.CLI.getMarker(), "Hashing state");
+ final StateLifecycleManager stateLifecycleManager = new StateLifecycleManagerImpl<>(
+ platformContext.getMetrics(), platformContext.getTime(), (virtualMap) -> {
+ // FUTURE WORK: https://github.com/hiero-ledger/hiero-consensus-node/issues/19003
+ throw new UnsupportedOperationException();
+ });
+
platformContext
.getMerkleCryptography()
.digestTreeAsync(reservedSignedState.get().getState().getRoot())
@@ -60,8 +69,9 @@ public void run() {
}
try (final ReservedSignedState signedState = getStateEditor().getSignedStateCopy()) {
+ stateLifecycleManager.setSnapshotSource(signedState.get());
writeSignedStateFilesToDirectory(
- platformContext, NO_NODE_ID, directory, signedState.get(), DEFAULT_PLATFORM_STATE_FACADE);
+ platformContext, NO_NODE_ID, directory, DEFAULT_PLATFORM_STATE_FACADE, stateLifecycleManager);
}
} catch (final IOException e) {
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/signed/SignedState.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/signed/SignedState.java
index 0a8e3bca82e8..40b7dd9fb871 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/signed/SignedState.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/signed/SignedState.java
@@ -23,6 +23,7 @@
import com.swirlds.platform.state.signed.SignedStateHistory.SignedStateAction;
import com.swirlds.platform.state.snapshot.StateToDiskReason;
import com.swirlds.state.MerkleNodeState;
+import com.swirlds.state.MerkleNodeStateAware;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.security.cert.X509Certificate;
@@ -62,7 +63,7 @@
* rejoining after a long absence.
*
*/
-public class SignedState {
+public class SignedState implements MerkleNodeStateAware {
private static final Logger logger = LogManager.getLogger(SignedState.class);
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/DefaultStateSnapshotManager.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/DefaultStateSnapshotManager.java
index d8f6f5f1ed5d..88e4d680ac98 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/DefaultStateSnapshotManager.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/DefaultStateSnapshotManager.java
@@ -16,6 +16,7 @@
import com.swirlds.platform.state.service.PlatformStateFacade;
import com.swirlds.platform.state.signed.ReservedSignedState;
import com.swirlds.platform.state.signed.SignedState;
+import com.swirlds.state.StateLifecycleManager;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.io.IOException;
@@ -80,6 +81,11 @@ public class DefaultStateSnapshotManager implements StateSnapshotManager {
*/
private final SignedStateFilePath signedStateFilePath;
+ /**
+ * Provides access to the state
+ */
+ private final StateLifecycleManager stateLifecycleManager;
+
/**
* Creates a new instance.
*
@@ -88,13 +94,15 @@ public class DefaultStateSnapshotManager implements StateSnapshotManager {
* @param selfId the ID of this node
* @param swirldName the name of the swirld
* @param platformStateFacade the facade to access the platform state
+ * @param stateLifecycleManager the state lifecycle manager
*/
public DefaultStateSnapshotManager(
@NonNull final PlatformContext platformContext,
@NonNull final String mainClassName,
@NonNull final NodeId selfId,
@NonNull final String swirldName,
- @NonNull final PlatformStateFacade platformStateFacade) {
+ @NonNull final PlatformStateFacade platformStateFacade,
+ @NonNull StateLifecycleManager stateLifecycleManager) {
this.platformContext = Objects.requireNonNull(platformContext);
this.time = platformContext.getTime();
@@ -103,6 +111,7 @@ public DefaultStateSnapshotManager(
this.swirldName = Objects.requireNonNull(swirldName);
configuration = platformContext.getConfiguration();
this.platformStateFacade = platformStateFacade;
+ this.stateLifecycleManager = stateLifecycleManager;
signedStateFilePath = new SignedStateFilePath(configuration.getConfigData(StateCommonConfig.class));
metrics = new StateSnapshotManagerMetrics(platformContext);
}
@@ -171,8 +180,9 @@ private static StateToDiskReason getReason(@NonNull final SignedState state) {
private boolean saveStateTask(@NonNull final SignedState state, @NonNull final Path directory) {
try {
+ stateLifecycleManager.setSnapshotSource(state);
SignedStateFileWriter.writeSignedStateToDisk(
- platformContext, selfId, directory, state, getReason(state), platformStateFacade);
+ platformContext, selfId, directory, getReason(state), platformStateFacade, stateLifecycleManager);
return true;
} catch (final Throwable e) {
logger.error(
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/SignedStateFileWriter.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/SignedStateFileWriter.java
index 0b32549a6c82..09c5e5b79b2e 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/SignedStateFileWriter.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/SignedStateFileWriter.java
@@ -11,6 +11,7 @@
import static com.swirlds.platform.state.snapshot.SignedStateFileUtils.HASH_INFO_FILE_NAME;
import static com.swirlds.platform.state.snapshot.SignedStateFileUtils.INIT_SIG_SET_FILE_VERSION;
import static com.swirlds.platform.state.snapshot.SignedStateFileUtils.SIGNATURE_SET_FILE_NAME;
+import static java.util.Objects.requireNonNull;
import com.hedera.hapi.node.state.roster.Roster;
import com.swirlds.common.context.PlatformContext;
@@ -23,7 +24,7 @@
import com.swirlds.platform.state.signed.SigSet;
import com.swirlds.platform.state.signed.SignedState;
import com.swirlds.state.MerkleNodeState;
-import com.swirlds.state.State;
+import com.swirlds.state.StateLifecycleManager;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.io.BufferedWriter;
@@ -31,7 +32,6 @@
import java.io.IOException;
import java.nio.file.Path;
import java.time.Instant;
-import java.util.Objects;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hiero.consensus.model.node.NodeId;
@@ -87,14 +87,14 @@ public static void writeHashInfoFile(
* @param directory the directory to write to
* @param signedState the signed state being written
*/
- public static void writeMetadataFile(
+ private static void writeMetadataFile(
@Nullable final NodeId selfId,
@NonNull final Path directory,
@NonNull final SignedState signedState,
@NonNull final PlatformStateFacade platformStateFacade)
throws IOException {
- Objects.requireNonNull(directory, "directory must not be null");
- Objects.requireNonNull(signedState, "signedState must not be null");
+ requireNonNull(directory, "directory must not be null");
+ requireNonNull(signedState, "signedState must not be null");
final Path metadataFile = directory.resolve(SavedStateMetadata.FILE_NAME);
@@ -131,27 +131,25 @@ public static void writeSignatureSetFile(final @NonNull Path directory, final @N
* @param platformContext the platform context
* @param selfId the id of the platform
* @param directory the directory where all files should be placed
- * @param signedState the signed state being written to disk
*/
public static void writeSignedStateFilesToDirectory(
@Nullable final PlatformContext platformContext,
@Nullable final NodeId selfId,
@NonNull final Path directory,
- @NonNull final SignedState signedState,
- @NonNull final PlatformStateFacade platformStateFacade)
+ @NonNull final PlatformStateFacade platformStateFacade,
+ @NonNull final StateLifecycleManager stateLifecycleManager)
throws IOException {
- Objects.requireNonNull(platformContext);
- Objects.requireNonNull(directory);
- Objects.requireNonNull(signedState);
-
- final State state = signedState.getState();
-
- state.createSnapshot(directory);
- writeSignatureSetFile(directory, signedState);
- writeHashInfoFile(platformContext, directory, signedState.getState(), platformStateFacade);
- writeMetadataFile(selfId, directory, signedState, platformStateFacade);
- writeEmergencyRecoveryFile(directory, signedState);
- final Roster currentRoster = signedState.getRoster();
+ requireNonNull(platformContext);
+ requireNonNull(directory);
+ requireNonNull(stateLifecycleManager.getSnapshotSource());
+
+ SignedState snapshotSource = stateLifecycleManager.getSnapshotSource();
+ stateLifecycleManager.createSnapshot(directory);
+ writeSignatureSetFile(directory, snapshotSource);
+ writeHashInfoFile(platformContext, directory, snapshotSource.getState(), platformStateFacade);
+ writeMetadataFile(selfId, directory, snapshotSource, platformStateFacade);
+ writeEmergencyRecoveryFile(directory, snapshotSource);
+ final Roster currentRoster = snapshotSource.getRoster();
if (currentRoster != null) {
writeRosterFile(directory, currentRoster);
}
@@ -162,8 +160,8 @@ public static void writeSignedStateFilesToDirectory(
platformContext,
selfId,
directory,
- platformStateFacade.ancientThresholdOf(signedState.getState()),
- signedState.getRound());
+ platformStateFacade.ancientThresholdOf(snapshotSource.getState()),
+ snapshotSource.getRound());
}
}
@@ -189,39 +187,42 @@ private static void writeRosterFile(@NonNull final Path directory, @NonNull fina
* @param platformContext the platform context
* @param selfId the id of the platform
* @param savedStateDirectory the directory where the state will be stored
- * @param signedState the object to be written
* @param stateToDiskReason the reason the state is being written to disk
+ * @param platformStateFacade the facade to access the platform state
+ * @param stateLifecycleManager the state lifecycle manager
*/
public static void writeSignedStateToDisk(
@NonNull final PlatformContext platformContext,
@Nullable final NodeId selfId,
@NonNull final Path savedStateDirectory,
- @NonNull final SignedState signedState,
@Nullable final StateToDiskReason stateToDiskReason,
- @NonNull final PlatformStateFacade platformStateFacade)
+ @NonNull final PlatformStateFacade platformStateFacade,
+ @NonNull final StateLifecycleManager stateLifecycleManager)
throws IOException {
- Objects.requireNonNull(platformContext);
- Objects.requireNonNull(savedStateDirectory);
- Objects.requireNonNull(signedState);
+ requireNonNull(platformContext);
+ requireNonNull(savedStateDirectory);
+ requireNonNull(stateLifecycleManager);
+ final SignedState snapshotSource = stateLifecycleManager.getSnapshotSource();
+ requireNonNull(snapshotSource);
try {
logger.info(
STATE_TO_DISK.getMarker(),
"Started writing round {} state to disk. Reason: {}, directory: {}",
- signedState.getRound(),
+ snapshotSource.getRound(),
stateToDiskReason == null ? "UNKNOWN" : stateToDiskReason,
savedStateDirectory);
executeAndRename(
savedStateDirectory,
directory -> writeSignedStateFilesToDirectory(
- platformContext, selfId, directory, signedState, platformStateFacade),
+ platformContext, selfId, directory, platformStateFacade, stateLifecycleManager),
platformContext.getConfiguration());
logger.info(STATE_TO_DISK.getMarker(), () -> new StateSavedToDiskPayload(
- signedState.getRound(),
- signedState.isFreezeState(),
+ snapshotSource.getRound(),
+ snapshotSource.isFreezeState(),
stateToDiskReason == null ? "UNKNOWN" : stateToDiskReason.toString(),
savedStateDirectory)
.toString());
@@ -229,7 +230,7 @@ public static void writeSignedStateToDisk(
logger.error(
EXCEPTION.getMarker(),
"Exception when writing the signed state for round {} to disk:",
- signedState.getRound(),
+ snapshotSource.getRound(),
e);
throw e;
}
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/SignedStateFileReadWriteTest.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/SignedStateFileReadWriteTest.java
index ae6d32452a09..8a63496d9e2d 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/SignedStateFileReadWriteTest.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/SignedStateFileReadWriteTest.java
@@ -4,7 +4,6 @@
import static com.swirlds.common.io.utility.FileUtils.throwIfFileExists;
import static com.swirlds.common.merkle.utility.MerkleTreeSnapshotReader.SIGNED_STATE_FILE_NAME;
import static com.swirlds.platform.StateFileManagerTests.hashState;
-import static com.swirlds.platform.StateFileManagerTests.makeImmutable;
import static com.swirlds.platform.state.snapshot.SignedStateFileReader.readStateFile;
import static com.swirlds.platform.state.snapshot.SignedStateFileUtils.CURRENT_ROSTER_FILE_NAME;
import static com.swirlds.platform.state.snapshot.SignedStateFileUtils.HASH_INFO_FILE_NAME;
@@ -23,10 +22,12 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.hedera.hapi.node.base.SemanticVersion;
+import com.swirlds.base.test.fixtures.time.FakeTime;
import com.swirlds.common.config.StateCommonConfig_;
import com.swirlds.common.context.PlatformContext;
import com.swirlds.common.io.utility.LegacyTemporaryFileBuilder;
import com.swirlds.common.merkle.utility.MerkleTreeVisualizer;
+import com.swirlds.common.metrics.noop.NoOpMetrics;
import com.swirlds.common.test.fixtures.platform.TestPlatformContextBuilder;
import com.swirlds.config.api.Configuration;
import com.swirlds.config.extensions.test.fixtures.TestConfigBuilder;
@@ -38,7 +39,8 @@
import com.swirlds.platform.state.snapshot.StateToDiskReason;
import com.swirlds.platform.test.fixtures.state.RandomSignedStateGenerator;
import com.swirlds.state.MerkleNodeState;
-import com.swirlds.state.State;
+import com.swirlds.state.StateLifecycleManager;
+import com.swirlds.state.merkle.StateLifecycleManagerImpl;
import com.swirlds.state.test.fixtures.merkle.TestVirtualMapState;
import java.io.BufferedReader;
import java.io.FileReader;
@@ -60,6 +62,7 @@ class SignedStateFileReadWriteTest {
private static SemanticVersion platformVersion;
private static PlatformStateFacade stateFacade;
+ private StateLifecycleManager stateLifecycleManager;
@BeforeAll
static void beforeAll() throws ConstructableRegistryException {
@@ -78,6 +81,8 @@ static void beforeAll() throws ConstructableRegistryException {
@BeforeEach
void beforeEach() throws IOException {
testDirectory = LegacyTemporaryFileBuilder.buildTemporaryFile("SignedStateFileReadWriteTest", CONFIGURATION);
+ stateLifecycleManager =
+ new StateLifecycleManagerImpl<>(new NoOpMetrics(), new FakeTime(), TestVirtualMapState::new);
LegacyTemporaryFileBuilder.overrideTemporaryFileLocation(testDirectory.resolve("tmp"));
}
@@ -126,10 +131,11 @@ void writeThenReadStateFileTest() throws IOException {
assertFalse(exists(stateFile), "signed state file should not yet exist");
assertFalse(exists(signatureSetFile), "signature set file should not yet exist");
- State state = signedState.getState();
+ MerkleNodeState state = signedState.getState();
state.copy().release();
hashState(signedState);
- state.createSnapshot(testDirectory);
+ stateLifecycleManager.setSnapshotSource(signedState);
+ stateLifecycleManager.createSnapshot(testDirectory);
writeSignatureSetFile(testDirectory, signedState);
assertTrue(exists(stateFile), "signed state file should be present");
@@ -147,8 +153,8 @@ void writeThenReadStateFileTest() throws IOException {
signedState.getState().getHash(),
deserializedSignedState.reservedSignedState().get().getState().getHash(),
"hash should match");
- assertNotSame(signedState, deserializedSignedState.reservedSignedState(), "state should be a different object");
- signedState.getState().release();
+ assertNotSame(
+ signedState, deserializedSignedState.reservedSignedState().get(), "state should be a different object");
deserializedSignedState.reservedSignedState().get().getState().release();
}
@@ -159,6 +165,7 @@ void writeSavedStateToDiskTest() throws IOException {
.setSoftwareVersion(platformVersion)
.build();
final Path directory = testDirectory.resolve("state");
+ stateLifecycleManager.initState(signedState.getState(), true);
final Path stateFile = directory.resolve(SIGNED_STATE_FILE_NAME);
final Path hashInfoFile = directory.resolve(HASH_INFO_FILE_NAME);
@@ -173,16 +180,17 @@ void writeSavedStateToDiskTest() throws IOException {
.withConfiguration(configuration)
.build();
- makeImmutable(signedState);
+ stateLifecycleManager.getMutableState().release();
hashState(signedState);
+ stateLifecycleManager.setSnapshotSource(signedState);
writeSignedStateToDisk(
platformContext,
NodeId.of(0),
directory,
- signedState,
StateToDiskReason.PERIODIC_SNAPSHOT,
- stateFacade);
+ stateFacade,
+ stateLifecycleManager);
assertTrue(exists(stateFile), "state file should exist");
assertTrue(exists(hashInfoFile), "hash info file should exist");
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/StateFileManagerTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/StateFileManagerTests.java
index f814e34c2f2f..c8831755e6ee 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/StateFileManagerTests.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/StateFileManagerTests.java
@@ -41,6 +41,8 @@
import com.swirlds.platform.state.snapshot.StateDumpRequest;
import com.swirlds.platform.state.snapshot.StateSnapshotManager;
import com.swirlds.platform.test.fixtures.state.RandomSignedStateGenerator;
+import com.swirlds.state.StateLifecycleManager;
+import com.swirlds.state.merkle.StateLifecycleManagerImpl;
import com.swirlds.state.test.fixtures.merkle.TestVirtualMapState;
import java.io.IOException;
import java.nio.file.Files;
@@ -75,6 +77,7 @@ class StateFileManagerTests {
private SignedStateFilePath signedStateFilePath;
Path testDirectory;
+ private StateLifecycleManager stateLifecycleManager;
@BeforeAll
static void beforeAll() throws ConstructableRegistryException {
@@ -97,6 +100,8 @@ void beforeEach() throws IOException {
.build();
signedStateFilePath =
new SignedStateFilePath(context.getConfiguration().getConfigData(StateCommonConfig.class));
+ stateLifecycleManager =
+ new StateLifecycleManagerImpl<>(context.getMetrics(), context.getTime(), TestVirtualMapState::new);
}
@AfterEach
@@ -154,7 +159,7 @@ private void validateSavingOfState(final SignedState originalState, final Path s
@DisplayName("Standard Operation Test")
void standardOperationTest(final boolean successExpected) throws IOException {
final SignedState signedState = new RandomSignedStateGenerator().build();
- makeImmutable(signedState);
+ initLifecycleManagerAndMakeStateImmutable(signedState);
hashState(signedState);
if (!successExpected) {
@@ -166,7 +171,7 @@ void standardOperationTest(final boolean successExpected) throws IOException {
}
final StateSnapshotManager manager = new DefaultStateSnapshotManager(
- context, MAIN_CLASS_NAME, SELF_ID, SWIRLD_NAME, TEST_PLATFORM_STATE_FACADE);
+ context, MAIN_CLASS_NAME, SELF_ID, SWIRLD_NAME, TEST_PLATFORM_STATE_FACADE, stateLifecycleManager);
final StateSavingResult stateSavingResult = manager.saveStateTask(signedState.reserve("test"));
@@ -184,9 +189,9 @@ void saveISSignedState() throws IOException {
final SignedState signedState = new RandomSignedStateGenerator().build();
final StateSnapshotManager manager = new DefaultStateSnapshotManager(
- context, MAIN_CLASS_NAME, SELF_ID, SWIRLD_NAME, TEST_PLATFORM_STATE_FACADE);
+ context, MAIN_CLASS_NAME, SELF_ID, SWIRLD_NAME, TEST_PLATFORM_STATE_FACADE, stateLifecycleManager);
signedState.markAsStateToSave(ISS);
- makeImmutable(signedState);
+ initLifecycleManagerAndMakeStateImmutable(signedState);
hashState(signedState);
manager.dumpStateTask(StateDumpRequest.create(signedState.reserve("test")));
@@ -226,8 +231,8 @@ void sequenceOfStatesTest(final boolean startAtGenesis) throws IOException {
final double standardDeviationTimeBetweenStates = 0.5;
final StateSnapshotManager manager = new DefaultStateSnapshotManager(
- context, MAIN_CLASS_NAME, SELF_ID, SWIRLD_NAME, TEST_PLATFORM_STATE_FACADE);
- final SavedStateController controller = new DefaultSavedStateController(context);
+ context, MAIN_CLASS_NAME, SELF_ID, SWIRLD_NAME, TEST_PLATFORM_STATE_FACADE, stateLifecycleManager);
+ final SavedStateController controller = new DefaultSavedStateController(context, stateLifecycleManager);
Instant timestamp;
final long firstRound;
@@ -266,8 +271,8 @@ void sequenceOfStatesTest(final boolean startAtGenesis) throws IOException {
.build();
final ReservedSignedState reservedSignedState = signedState.reserve("initialTestReservation");
+ initLifecycleManagerAndMakeStateImmutable(reservedSignedState.get());
controller.markSavedState(new StateWithHashComplexity(reservedSignedState, 1));
- makeImmutable(reservedSignedState.get());
hashState(signedState);
if (signedState.isStateToSave()) {
@@ -350,7 +355,7 @@ void stateDeletionTest() throws IOException {
final int count = 10;
final StateSnapshotManager manager = new DefaultStateSnapshotManager(
- context, MAIN_CLASS_NAME, SELF_ID, SWIRLD_NAME, TEST_PLATFORM_STATE_FACADE);
+ context, MAIN_CLASS_NAME, SELF_ID, SWIRLD_NAME, TEST_PLATFORM_STATE_FACADE, stateLifecycleManager);
final Path statesDirectory =
signedStateFilePath.getSignedStatesDirectoryForSwirld(MAIN_CLASS_NAME, SELF_ID, SWIRLD_NAME);
@@ -363,7 +368,7 @@ void stateDeletionTest() throws IOException {
.resolve("node" + SELF_ID + "_round" + issRound);
final SignedState issState =
new RandomSignedStateGenerator(random).setRound(issRound).build();
- makeImmutable(issState);
+ initLifecycleManagerAndMakeStateImmutable(issState);
issState.markAsStateToSave(ISS);
hashState(issState);
manager.dumpStateTask(StateDumpRequest.create(issState.reserve("test")));
@@ -377,7 +382,7 @@ void stateDeletionTest() throws IOException {
.resolve("node" + SELF_ID + "_round" + fatalRound);
final SignedState fatalState =
new RandomSignedStateGenerator(random).setRound(fatalRound).build();
- makeImmutable(fatalState);
+ initLifecycleManagerAndMakeStateImmutable(fatalState);
hashState(fatalState);
fatalState.markAsStateToSave(FATAL_ERROR);
manager.dumpStateTask(StateDumpRequest.create(fatalState.reserve("test")));
@@ -390,7 +395,7 @@ void stateDeletionTest() throws IOException {
new RandomSignedStateGenerator(random).setRound(round).build();
issState.markAsStateToSave(PERIODIC_SNAPSHOT);
states.add(signedState);
- makeImmutable(signedState);
+ initLifecycleManagerAndMakeStateImmutable(signedState);
hashState(signedState);
manager.saveStateTask(signedState.reserve("test"));
@@ -424,7 +429,8 @@ static void hashState(SignedState signedState) {
signedState.getState().getRoot().getHash();
}
- static void makeImmutable(SignedState signedState) {
- signedState.getState().copy().release();
+ void initLifecycleManagerAndMakeStateImmutable(SignedState state) {
+ stateLifecycleManager.initState(state.getState(), false);
+ stateLifecycleManager.getMutableState().release();
}
}
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/DefaultTransactionHandlerTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/DefaultTransactionHandlerTests.java
index 1141a4b3b387..90394fd382a1 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/DefaultTransactionHandlerTests.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/DefaultTransactionHandlerTests.java
@@ -89,7 +89,7 @@ private ConsensusRound newConsensusRound(final boolean pcesRound) {
@ParameterizedTest
@CsvSource({"false", "true"})
void normalOperation(final boolean pcesRound) throws InterruptedException {
- final TransactionHandlerTester tester = new TransactionHandlerTester(roster);
+ final TransactionHandlerTester tester = new TransactionHandlerTester();
final ConsensusRound consensusRound = newConsensusRound(pcesRound);
final TransactionHandlerResult handlerOutput =
@@ -146,13 +146,13 @@ void normalOperation(final boolean pcesRound) throws InterruptedException {
"the state should match the PCES boolean");
verify(tester.getStateEventHandler())
.onSealConsensusRound(
- consensusRound, tester.getSwirldStateManager().getConsensusState());
+ consensusRound, tester.getStateLifecycleManager().getMutableState());
}
@Test
@DisplayName("Round in freeze period")
void freezeHandling() throws InterruptedException {
- final TransactionHandlerTester tester = new TransactionHandlerTester(roster);
+ final TransactionHandlerTester tester = new TransactionHandlerTester();
final ConsensusRound consensusRound = newConsensusRound(false);
when(tester.getPlatformStateFacade().isInFreezePeriod(consensusRound.getConsensusTimestamp(), (MerkleNodeState)
tester.getConsensusState()))
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/TransactionHandlerTester.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/TransactionHandlerTester.java
index 6f237c8a17f9..34465087a29a 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/TransactionHandlerTester.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/TransactionHandlerTester.java
@@ -14,7 +14,6 @@
import com.swirlds.common.test.fixtures.platform.TestPlatformContextBuilder;
import com.swirlds.platform.state.ConsensusStateEventHandler;
import com.swirlds.platform.state.PlatformStateModifier;
-import com.swirlds.platform.state.SwirldStateManager;
import com.swirlds.platform.state.service.PlatformStateFacade;
import com.swirlds.platform.state.service.PlatformStateValueAccumulator;
import com.swirlds.platform.system.status.StatusActionSubmitter;
@@ -22,6 +21,8 @@
import com.swirlds.platform.test.fixtures.state.TestPlatformStateFacade;
import com.swirlds.state.MerkleNodeState;
import com.swirlds.state.State;
+import com.swirlds.state.StateLifecycleManager;
+import com.swirlds.state.merkle.StateLifecycleManagerImpl;
import java.util.ArrayList;
import java.util.List;
import org.hiero.consensus.model.hashgraph.Round;
@@ -32,7 +33,7 @@
*/
public class TransactionHandlerTester {
private final PlatformStateModifier platformState;
- private final SwirldStateManager swirldStateManager;
+ private final StateLifecycleManager stateLifecycleManager;
private final DefaultTransactionHandler defaultTransactionHandler;
private final List submittedActions = new ArrayList<>();
private final List handledRounds = new ArrayList<>();
@@ -43,9 +44,8 @@ public class TransactionHandlerTester {
/**
* Constructs a new {@link TransactionHandlerTester} with the given {@link Roster}.
*
- * @param roster the {@link Roster} to use
*/
- public TransactionHandlerTester(final Roster roster) {
+ public TransactionHandlerTester() {
final PlatformContext platformContext =
TestPlatformContextBuilder.create().build();
platformState = new PlatformStateValueAccumulator();
@@ -66,11 +66,12 @@ public TransactionHandlerTester(final Roster roster) {
.when(consensusStateEventHandler)
.onHandleConsensusRound(any(), same(consensusState), any());
final StatusActionSubmitter statusActionSubmitter = submittedActions::add;
- swirldStateManager = new SwirldStateManager(platformContext, roster);
- swirldStateManager.setState(consensusState, true);
+ stateLifecycleManager = new StateLifecycleManagerImpl(
+ platformContext.getMetrics(), platformContext.getTime(), vm -> consensusState);
+ stateLifecycleManager.initState(consensusState, true);
defaultTransactionHandler = new DefaultTransactionHandler(
platformContext,
- swirldStateManager,
+ stateLifecycleManager,
statusActionSubmitter,
mock(SemanticVersion.class),
platformStateFacade,
@@ -107,10 +108,10 @@ public List getHandledRounds() {
}
/**
- * @return the {@link SwirldStateManager} used by this tester
+ * @return the {@link StateLifecycleManager} used by this tester
*/
- public SwirldStateManager getSwirldStateManager() {
- return swirldStateManager;
+ public StateLifecycleManager getStateLifecycleManager() {
+ return stateLifecycleManager;
}
/**
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectControllerTest.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectControllerTest.java
index 5dcca8d9a16c..ab7b976449b9 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectControllerTest.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectControllerTest.java
@@ -29,7 +29,6 @@
import com.swirlds.platform.components.SavedStateController;
import com.swirlds.platform.network.protocol.ReservedSignedStateResultPromise;
import com.swirlds.platform.state.ConsensusStateEventHandler;
-import com.swirlds.platform.state.SwirldStateManager;
import com.swirlds.platform.state.service.PlatformStateFacade;
import com.swirlds.platform.state.signed.ReservedSignedState;
import com.swirlds.platform.state.signed.SigSet;
@@ -46,6 +45,7 @@
import com.swirlds.platform.test.fixtures.state.RandomSignedStateGenerator;
import com.swirlds.platform.wiring.PlatformCoordinator;
import com.swirlds.state.MerkleNodeState;
+import com.swirlds.state.StateLifecycleManager;
import com.swirlds.state.test.fixtures.merkle.TestVirtualMapState;
import java.time.Duration;
import java.util.Random;
@@ -84,7 +84,7 @@ class ReconnectControllerTest {
private MerkleCryptography merkleCryptography;
private Platform platform;
private PlatformCoordinator platformCoordinator;
- private SwirldStateManager swirldStateManager;
+ private StateLifecycleManager stateLifecycleManager;
private SavedStateController savedStateController;
private ConsensusStateEventHandler consensusStateEventHandler;
private ReservedSignedStateResultPromise peerReservedSignedStateResultPromise;
@@ -166,8 +166,8 @@ void setUp() {
platformCoordinator = mock(PlatformCoordinator.class);
// Mock SwirldStateManager
- swirldStateManager = mock(SwirldStateManager.class);
- when(swirldStateManager.getConsensusState()).thenReturn(testWorkingState);
+ stateLifecycleManager = mock(StateLifecycleManager.class);
+ when(stateLifecycleManager.getMutableState()).thenReturn(testWorkingState);
// Mock SavedStateController
savedStateController = mock(SavedStateController.class);
@@ -209,7 +209,7 @@ private ReconnectController createController() {
platform,
platformContext,
platformCoordinator,
- swirldStateManager,
+ stateLifecycleManager,
savedStateController,
consensusStateEventHandler,
peerReservedSignedStateResultPromise,
@@ -809,7 +809,7 @@ void testSystemExitOnReconnectWindowTimeout() throws Exception {
platform,
shortWindowContext,
platformCoordinator,
- swirldStateManager,
+ stateLifecycleManager,
savedStateController,
consensusStateEventHandler,
peerReservedSignedStateResultPromise,
@@ -886,7 +886,7 @@ void testSystemExitWhenReconnectDisabled() throws Exception {
platform,
disabledContext,
platformCoordinator,
- swirldStateManager,
+ stateLifecycleManager,
savedStateController,
consensusStateEventHandler,
peerReservedSignedStateResultPromise,
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectStatePeerProtocolTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectStatePeerProtocolTests.java
index ba9be70862b7..591f389738af 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectStatePeerProtocolTests.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectStatePeerProtocolTests.java
@@ -33,11 +33,11 @@
import com.swirlds.platform.network.protocol.Protocol;
import com.swirlds.platform.network.protocol.ReconnectStateSyncProtocol;
import com.swirlds.platform.network.protocol.ReservedSignedStateResultPromise;
-import com.swirlds.platform.state.SwirldStateManager;
import com.swirlds.platform.state.signed.ReservedSignedState;
import com.swirlds.platform.state.signed.SignedState;
import com.swirlds.platform.test.fixtures.state.RandomSignedStateGenerator;
import com.swirlds.state.MerkleNodeState;
+import com.swirlds.state.StateLifecycleManager;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
@@ -162,7 +162,7 @@ void shouldInitiateTest(final InitiateParams params) {
fallenBehindManager,
TEST_PLATFORM_STATE_FACADE,
reservedSignedStateResultPromise,
- mock(SwirldStateManager.class),
+ mock(StateLifecycleManager.class),
a -> null);
reconnectProtocol.updatePlatformStatus(ACTIVE);
@@ -206,7 +206,7 @@ void testShouldAccept(final AcceptParams params) {
fallenBehindManager,
TEST_PLATFORM_STATE_FACADE,
reservedSignedStateResultPromise,
- mock(SwirldStateManager.class),
+ mock(StateLifecycleManager.class),
a -> null);
reconnectProtocol.updatePlatformStatus(ACTIVE);
assertEquals(
@@ -245,7 +245,7 @@ void testTeacherThrottleReleased() {
Time.getCurrent(),
TEST_PLATFORM_STATE_FACADE,
reservedSignedStateResultPromise,
- mock(SwirldStateManager.class),
+ mock(StateLifecycleManager.class),
a -> null);
final SignedState signedState = spy(new RandomSignedStateGenerator().build());
when(signedState.isComplete()).thenReturn(true);
@@ -267,7 +267,7 @@ void testTeacherThrottleReleased() {
Time.getCurrent(),
TEST_PLATFORM_STATE_FACADE,
reservedSignedStateResultPromise,
- mock(SwirldStateManager.class),
+ mock(StateLifecycleManager.class),
a -> null);
// pretend we have fallen behind
@@ -302,7 +302,7 @@ void testPermitReleased() {
fallenBehindManager,
TEST_PLATFORM_STATE_FACADE,
reservedSignedStateResultPromise,
- mock(SwirldStateManager.class),
+ mock(StateLifecycleManager.class),
a -> null);
reconnectProtocol.updatePlatformStatus(ACTIVE);
assertFalse(
@@ -362,7 +362,7 @@ void abortedLearner() {
fallenBehindManager,
TEST_PLATFORM_STATE_FACADE,
reservedSignedStateResultPromise,
- mock(SwirldStateManager.class),
+ mock(StateLifecycleManager.class),
a -> null);
reconnectProtocol.updatePlatformStatus(ACTIVE);
final PeerProtocol peerProtocol = reconnectProtocol.createPeerInstance(NodeId.of(0));
@@ -407,7 +407,7 @@ void abortedTeacher() {
fallenBehindManager,
TEST_PLATFORM_STATE_FACADE,
reservedSignedStateResultPromise,
- mock(SwirldStateManager.class),
+ mock(StateLifecycleManager.class),
a -> null);
reconnectProtocol.updatePlatformStatus(ACTIVE);
final PeerProtocol peerProtocol = reconnectProtocol.createPeerInstance(NodeId.of(0));
@@ -445,7 +445,7 @@ void teacherHasNoSignedState() {
fallenBehindManager,
TEST_PLATFORM_STATE_FACADE,
reservedSignedStateResultPromise,
- mock(SwirldStateManager.class),
+ mock(StateLifecycleManager.class),
a -> null);
reconnectProtocol.updatePlatformStatus(ACTIVE);
final PeerProtocol peerProtocol = reconnectProtocol.createPeerInstance(NodeId.of(0));
@@ -476,7 +476,7 @@ void teacherNotActive() {
fallenBehindManager,
TEST_PLATFORM_STATE_FACADE,
reservedSignedStateResultPromise,
- mock(SwirldStateManager.class),
+ mock(StateLifecycleManager.class),
a -> null);
reconnectProtocol.updatePlatformStatus(CHECKING);
final PeerProtocol peerProtocol = reconnectProtocol.createPeerInstance(NodeId.of(0));
@@ -503,7 +503,7 @@ void teacherHoldsLearnerPermit() {
mock(FallenBehindMonitor.class),
TEST_PLATFORM_STATE_FACADE,
reservedSignedStateResultPromise,
- mock(SwirldStateManager.class),
+ mock(StateLifecycleManager.class),
a -> null);
reconnectProtocol.updatePlatformStatus(ACTIVE);
final PeerProtocol peerProtocol = reconnectProtocol.createPeerInstance(NodeId.of(0));
@@ -550,7 +550,7 @@ void teacherCantAcquireLearnerPermit() {
mock(FallenBehindMonitor.class),
TEST_PLATFORM_STATE_FACADE,
reservedSignedStateResultPromise,
- mock(SwirldStateManager.class),
+ mock(StateLifecycleManager.class),
a -> null);
reconnectProtocol.updatePlatformStatus(ACTIVE);
final PeerProtocol peerProtocol = reconnectProtocol.createPeerInstance(NodeId.of(0));
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/SwirldsStateManagerTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateLifecycleManagerTests.java
similarity index 81%
rename from platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/SwirldsStateManagerTests.java
rename to platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateLifecycleManagerTests.java
index d928a135ebdd..4b3d348ec65f 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/SwirldsStateManagerTests.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateLifecycleManagerTests.java
@@ -20,15 +20,17 @@
import com.swirlds.platform.test.fixtures.state.RandomSignedStateGenerator;
import com.swirlds.platform.test.fixtures.state.TestingAppStateInitializer;
import com.swirlds.state.MerkleNodeState;
+import com.swirlds.state.StateLifecycleManager;
+import com.swirlds.state.merkle.StateLifecycleManagerImpl;
import com.swirlds.state.test.fixtures.merkle.TestVirtualMapState;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
-class SwirldsStateManagerTests {
+class StateLifecycleManagerTests {
- private SwirldStateManager swirldStateManager;
+ private StateLifecycleManager stateLifecycleManager;
private MerkleNodeState initialState;
@BeforeEach
@@ -41,8 +43,9 @@ void setup() {
final PlatformContext platformContext =
TestPlatformContextBuilder.create().build();
- swirldStateManager = new SwirldStateManager(platformContext, roster);
- swirldStateManager.setState(initialState, true);
+ stateLifecycleManager = new StateLifecycleManagerImpl<>(
+ platformContext.getMetrics(), platformContext.getTime(), TestVirtualMapState::new);
+ stateLifecycleManager.initState(initialState, true);
}
@AfterEach
@@ -50,8 +53,8 @@ void tearDown() {
if (!initialState.isDestroyed()) {
initialState.release();
}
- if (!swirldStateManager.getConsensusState().isDestroyed()) {
- swirldStateManager.getConsensusState().release();
+ if (!stateLifecycleManager.getMutableState().isDestroyed()) {
+ stateLifecycleManager.getMutableState().release();
}
MerkleDbTestUtils.assertAllDatabasesClosed();
}
@@ -64,39 +67,39 @@ void initialStateReferenceCount() {
initialState.getRoot().getReservationCount(),
"The initial state is copied and should be referenced once as the previous immutable state.");
Reservable consensusStateAsReservable =
- swirldStateManager.getConsensusState().getRoot();
+ stateLifecycleManager.getMutableState().getRoot();
assertEquals(
1, consensusStateAsReservable.getReservationCount(), "The consensus state should have one reference.");
}
@Test
@DisplayName("Load From Signed State - state reference counts")
- void setStateRefCount() {
+ void initStateRefCount() {
final SignedState ss1 = newSignedState();
final Reservable state1 = ss1.getState().getRoot();
- swirldStateManager.setState(ss1.getState(), false);
+ stateLifecycleManager.initState(ss1.getState(), false);
assertEquals(
2,
state1.getReservationCount(),
"Loading from signed state should increment the reference count, because it is now referenced by the "
- + "signed state and the previous immutable state in SwirldStateManager.");
- final MerkleNodeState consensusState1 = swirldStateManager.getConsensusState();
+ + "signed state and the previous immutable state in StateLifecycleManagerImpl.");
+ final MerkleNodeState consensusState1 = stateLifecycleManager.getMutableState();
assertEquals(
1,
consensusState1.getRoot().getReservationCount(),
"The current consensus state should have a single reference count.");
final SignedState ss2 = newSignedState();
- swirldStateManager.setState(ss2.getState(), false);
- final MerkleNodeState consensusState2 = swirldStateManager.getConsensusState();
+ stateLifecycleManager.initState(ss2.getState(), false);
+ final MerkleNodeState consensusState2 = stateLifecycleManager.getMutableState();
Reservable state2 = ss2.getState().getRoot();
assertEquals(
2,
state2.getReservationCount(),
"Loading from signed state should increment the reference count, because it is now referenced by the "
- + "signed state and the previous immutable state in SwirldStateManager.");
+ + "signed state and the previous immutable state in StateLifecycleManagerImpl.");
assertEquals(
1,
consensusState2.getRoot().getReservationCount(),
@@ -114,7 +117,7 @@ void setStateRefCount() {
private static MerkleNodeState newState(PlatformStateFacade platformStateFacade) {
final String virtualMapLabel =
- SwirldsStateManagerTests.class.getSimpleName() + "-" + java.util.UUID.randomUUID();
+ StateLifecycleManagerTests.class.getSimpleName() + "-" + java.util.UUID.randomUUID();
final MerkleNodeState state = TestVirtualMapState.createInstanceWithVirtualMapLabel(virtualMapLabel);
TestingAppStateInitializer.initPlatformState(state);
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateSignatureCollectorTester.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateSignatureCollectorTester.java
index 2389a9b2c765..50691fa3078d 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateSignatureCollectorTester.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateSignatureCollectorTester.java
@@ -106,7 +106,7 @@ private List processStates(@Nullable final List stateLifecycleManager;
@BeforeEach
void beforeEach() throws IOException {
@@ -126,22 +130,24 @@ private SignedState writeState(
final SignedState signedState =
new RandomSignedStateGenerator(random).setRound(round).build();
- // make the state immutable
- signedState.getState().copy().release();
+ stateLifecycleManager =
+ new StateLifecycleManagerImpl<>(new NoOpMetrics(), new FakeTime(), TestVirtualMapState::new);
+ stateLifecycleManager.initState(signedState.getState(), true);
+ stateLifecycleManager.getMutableState().release();
// FUTURE WORK: https://github.com/hiero-ledger/hiero-consensus-node/issues/19905
TestMerkleCryptoFactory.getInstance()
.digestTreeSync(signedState.getState().getRoot());
final Path savedStateDirectory =
signedStateFilePath.getSignedStateDirectory(mainClassName, selfId, swirldName, round);
-
+ stateLifecycleManager.setSnapshotSource(signedState);
writeSignedStateToDisk(
platformContext,
selfId,
savedStateDirectory,
- signedState,
StateToDiskReason.PERIODIC_SNAPSHOT,
- platformStateFacade);
+ platformStateFacade,
+ stateLifecycleManager);
if (corrupted) {
final Path stateFilePath = savedStateDirectory.resolve("SignedState.swh");
diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeState.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeState.java
index 78dd3d28a7aa..ac7097fe2b3a 100644
--- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeState.java
+++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeState.java
@@ -6,8 +6,6 @@
import com.swirlds.common.merkle.MerkleNode;
import com.swirlds.state.lifecycle.StateMetadata;
import edu.umd.cs.findbugs.annotations.NonNull;
-import java.io.IOException;
-import java.nio.file.Path;
import org.hiero.base.crypto.Hash;
/**
@@ -74,12 +72,6 @@ default MerkleNode getRoot() {
*/
void removeServiceState(@NonNull String serviceName, int stateId);
- /**
- * Loads a snapshot of a state.
- * @param targetPath The path to load the snapshot from.
- */
- MerkleNodeState loadSnapshot(@NonNull Path targetPath) throws IOException;
-
/**
* Get the merkle path of the singleton state by its ID.
* @param stateId The state ID of the singleton state.
diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeStateAware.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeStateAware.java
new file mode 100644
index 000000000000..69806a468c37
--- /dev/null
+++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeStateAware.java
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: Apache-2.0
+package com.swirlds.state;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+
+/**
+ * Implementations of this interface can provide access to their {@link MerkleNodeState}.
+ */
+public interface MerkleNodeStateAware {
+ /**
+ * An instance of {@link MerkleNodeState} associated with this object.
+ * @return an instance of {@link MerkleNodeState} associated with this object.
+ */
+ @NonNull
+ MerkleNodeState getState();
+}
diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/State.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/State.java
index 2a1cb3e4ce09..5063f4ca0d34 100644
--- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/State.java
+++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/State.java
@@ -9,8 +9,6 @@
import com.swirlds.state.spi.WritableStates;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
-import java.io.IOException;
-import java.nio.file.Path;
import org.hiero.base.crypto.Hash;
import org.hiero.base.crypto.Hashable;
@@ -94,22 +92,6 @@ default void computeHash() {
throw new UnsupportedOperationException();
}
- /**
- * Creates a snapshot for the state. The state has to be hashed and immutable before calling this method.
- * @param targetPath The path to save the snapshot.
- */
- default void createSnapshot(final @NonNull Path targetPath) {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Loads a snapshot of a state.
- * @param targetPath The path to load the snapshot from.
- */
- default State loadSnapshot(final @NonNull Path targetPath) throws IOException {
- throw new UnsupportedOperationException();
- }
-
/**
* Used to track the status of the Platform.
* @return {@code true} if Platform status is not {@code PlatformStatus.ACTIVE}.
diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
new file mode 100644
index 000000000000..d7a29fcac6a3
--- /dev/null
+++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: Apache-2.0
+package com.swirlds.state;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import java.nio.file.Path;
+
+/**
+ * An implementaion of this class is responsible
+ * @param a type of the snapshot source, should implement {@link MerkleNodeStateAware}
+ */
+public interface StateLifecycleManager {
+
+ /**
+ * Set the initial State. This method should only be on a startup of after a reconnect.
+ *
+ * @param state the initial state
+ */
+ void initState(@NonNull final MerkleNodeState state, boolean onStartup);
+
+ /**
+ * Get the mutable state.
+ */
+ MerkleNodeState getMutableState();
+
+ /**
+ * Get the latest immutable state.
+ * @return the latest immutable state.
+ */
+ MerkleNodeState getLatestImmutableState();
+
+ /**
+ * Sets the state to create a snapshot from.
+ * @param source the state to create a snapshot from.
+ */
+ void setSnapshotSource(@NonNull T source);
+
+ T getSnapshotSource();
+
+ /**
+ * Creates a snapshot for the state that was previously set with {@link #setSnapshotSource(MerkleNodeStateAware)}.
+ * The state has to be hashed before calling this method. Once the snapshot is created, the manager releases the source
+ * state of the snapshot and clears the reference to it.
+ *
+ * @param targetPath The path to save the snapshot.
+ */
+ void createSnapshot(@NonNull Path targetPath);
+
+ /**
+ * Loads a snapshot of a state and uses it as a new mutable state.
+ *
+ * @param targetPath The path to load the snapshot from.
+ * @return mutable copy of the loaded state
+ */
+ MerkleNodeState loadSnapshot(@NonNull Path targetPath);
+
+ /**
+ * Creates a mutable copy of the state. The previous mutable state becomes immutable,
+ * replacing the latest immutable state.
+ *
+ * @return a mutable copy of the current mutable state which became the latest immutable state.
+ */
+ MerkleNodeState copyMutableState();
+}
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
new file mode 100644
index 000000000000..1ee25591cd34
--- /dev/null
+++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: Apache-2.0
+package com.swirlds.state.merkle;
+
+import static com.swirlds.base.units.UnitConstants.NANOSECONDS_TO_MICROSECONDS;
+import static java.util.Objects.requireNonNull;
+
+import com.swirlds.base.time.Time;
+import com.swirlds.common.merkle.MerkleNode;
+import com.swirlds.common.merkle.utility.MerkleTreeSnapshotReader;
+import com.swirlds.common.merkle.utility.MerkleTreeSnapshotWriter;
+import com.swirlds.metrics.api.Metrics;
+import com.swirlds.state.MerkleNodeState;
+import com.swirlds.state.MerkleNodeStateAware;
+import com.swirlds.state.State;
+import com.swirlds.state.StateLifecycleManager;
+import com.swirlds.virtualmap.VirtualMap;
+import edu.umd.cs.findbugs.annotations.NonNull;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Path;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
+
+/**
+ * This class is responsible for maintaining references to the mutable state and the latest immutable state.
+ * It also updates these references upon state signing.
+ */
+public class StateLifecycleManagerImpl implements StateLifecycleManager {
+
+ /**
+ * Metrics for the state object
+ */
+ private final StateMetrics stateMetrics;
+
+ /**
+ * Metrics for the snapshot creation process
+ */
+ private final MerkleRootSnapshotMetrics snapshotMetrics;
+
+ private final Time time;
+
+ private final Metrics metrics;
+ private final Function stateSupplier;
+
+ /**
+ * reference to the state that reflects all known consensus transactions
+ */
+ private final AtomicReference stateRef = new AtomicReference<>();
+
+ /**
+ * The most recent immutable state. No value until the first fast copy is created.
+ */
+ private final AtomicReference latestImmutableStateRef = new AtomicReference<>();
+
+ /**
+ * The most recent immutable state. No value until the first fast copy is created.
+ */
+ private final AtomicReference snapshotSource = new AtomicReference<>();
+
+ /**
+ * Constructor.
+ *
+ * @param metrics the metrics object to gather state metrics
+ * @param time the time object
+ */
+ public StateLifecycleManagerImpl(
+ @NonNull final Metrics metrics,
+ @NonNull Time time,
+ Function stateSupplier) {
+ requireNonNull(metrics);
+ this.stateSupplier = stateSupplier;
+ this.metrics = metrics;
+ this.stateMetrics = new StateMetrics(metrics);
+ this.snapshotMetrics = new MerkleRootSnapshotMetrics(metrics);
+ this.time = time;
+ }
+
+ /**
+ * Set the initial State. This method should only be on a startup of after a reconnect.
+ *
+ * @param state the initial state
+ */
+ public void initState(@NonNull final MerkleNodeState state, boolean onStartup) {
+ requireNonNull(state);
+
+ state.throwIfDestroyed("state must not be destroyed");
+ state.throwIfImmutable("state must be mutable");
+
+ if (onStartup && stateRef.get() != null) {
+ throw new IllegalStateException("Attempt to set initial state when there is already a state reference.");
+ }
+
+ updateStateRefs(state);
+ }
+
+ @Override
+ public void setSnapshotSource(@NonNull T source) {
+ requireNonNull(source);
+ MerkleNodeState state = source.getState();
+ state.throwIfDestroyed("state must not be destroyed");
+ state.throwIfMutable("state must be immutable");
+ boolean result = snapshotSource.compareAndSet(null, source);
+ assert result : "Snapshot source was already set";
+ }
+
+ @Override
+ public T getSnapshotSource() {
+ return snapshotSource.get();
+ }
+
+ @Override
+ public MerkleNodeState getMutableState() {
+ return stateRef.get();
+ }
+
+ @Override
+ public MerkleNodeState getLatestImmutableState() {
+ return latestImmutableStateRef.get();
+ }
+
+ @Override
+ public MerkleNodeState copyMutableState() {
+ final MerkleNodeState state = stateRef.get();
+ updateStateRefs(state);
+ return stateRef.get();
+ }
+
+ private void updateStateRefs(MerkleNodeState state) {
+ // Create a fast copy so there is always an immutable state to
+ // invoke handleTransaction on for pre-consensus transactions
+ final long copyStart = System.nanoTime();
+ // Create a fast copy
+ final MerkleNodeState copy = state.copy();
+ // Increment the reference count because this reference becomes the new value
+ copy.getRoot().reserve();
+ final long copyEnd = System.nanoTime();
+ stateMetrics.stateCopyMicros((copyEnd - copyStart) * NANOSECONDS_TO_MICROSECONDS);
+ // Set latest immutable first to prevent the newly immutable stateRoot from being deleted between setting the
+ // stateRef and the latestImmutableState
+ setLatestImmutableState(state);
+ updateStateRef(copy);
+ }
+
+ /**
+ * Sets the consensus state to the state provided. Must be mutable and have a reference count of at least 1.
+ *
+ * @param state a new mutable state
+ */
+ private void updateStateRef(final MerkleNodeState state) {
+ final var currVal = stateRef.get();
+ if (currVal != null && !currVal.isDestroyed()) {
+ currVal.release();
+ }
+ // Do not increment the reference count because the state provided already has a reference count of at least
+ // one to represent this reference and to prevent it from being deleted before this reference is set.
+ stateRef.set(state);
+ }
+
+ private void setLatestImmutableState(final MerkleNodeState latestImmutableState) {
+ final State currVal = latestImmutableStateRef.get();
+ if (currVal != null && !currVal.isDestroyed()) {
+ currVal.release();
+ }
+ latestImmutableState.getRoot().reserve();
+ latestImmutableStateRef.set(latestImmutableState);
+ }
+
+ /**
+ * {@inheritDoc}}
+ */
+ public void createSnapshot(final @NonNull Path targetPath) {
+ requireNonNull(time);
+ requireNonNull(snapshotMetrics);
+ VirtualMapState> state = (VirtualMapState>) snapshotSource.get().getState();
+ state.throwIfMutable();
+ state.throwIfDestroyed();
+ final long startTime = time.currentTimeMillis();
+ MerkleTreeSnapshotWriter.createSnapshot(state.virtualMap, targetPath, state.getRound());
+ snapshotMetrics.updateWriteStateToDiskTimeMetric(time.currentTimeMillis() - startTime);
+ snapshotSource.set(null);
+ }
+
+ @Override
+ public MerkleNodeState loadSnapshot(@NonNull Path targetPath) {
+ final MerkleNode root;
+ try {
+ root = MerkleTreeSnapshotReader.readStateFileData(targetPath).stateRoot();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ if (!(root instanceof VirtualMap readVirtualMap)) {
+ throw new IllegalStateException(
+ "Root should be a VirtualMap, but it is " + root.getClass().getSimpleName() + " instead");
+ }
+
+ final var mutableCopy = readVirtualMap.copy();
+ mutableCopy.registerMetrics(metrics);
+ readVirtualMap.release();
+ readVirtualMap = mutableCopy;
+
+ return stateSupplier.apply(readVirtualMap);
+ }
+}
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java
index ce9d2fb32687..4e423cd76613 100644
--- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java
+++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java
@@ -19,8 +19,6 @@
import com.swirlds.base.time.Time;
import com.swirlds.common.Reservable;
import com.swirlds.common.merkle.MerkleNode;
-import com.swirlds.common.merkle.utility.MerkleTreeSnapshotReader;
-import com.swirlds.common.merkle.utility.MerkleTreeSnapshotWriter;
import com.swirlds.config.api.Configuration;
import com.swirlds.merkledb.MerkleDbDataSourceBuilder;
import com.swirlds.merkledb.config.MerkleDbConfig;
@@ -58,7 +56,6 @@
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.io.IOException;
-import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -80,11 +77,6 @@ public abstract class VirtualMapState> implements M
private static final Logger logger = LogManager.getLogger(VirtualMapState.class);
- /**
- * Metrics for the snapshot creation process
- */
- private final MerkleRootSnapshotMetrics snapshotMetrics;
-
/**
* Maintains information about all services known by this instance. Map keys are
* service names, values are service states by service ID.
@@ -108,8 +100,6 @@ public abstract class VirtualMapState> implements M
private final Metrics metrics;
- private final Time time;
-
protected VirtualMap virtualMap;
/**
@@ -129,7 +119,6 @@ public VirtualMapState(
@NonNull final Configuration configuration, @NonNull final Metrics metrics, @NonNull final Time time) {
requireNonNull(configuration);
this.metrics = requireNonNull(metrics);
- this.time = requireNonNull(time);
final MerkleDbDataSourceBuilder dsBuilder;
final MerkleDbConfig merkleDbConfig = configuration.getConfigData(MerkleDbConfig.class);
dsBuilder = new MerkleDbDataSourceBuilder(
@@ -137,7 +126,6 @@ public VirtualMapState(
this.virtualMap = new VirtualMap(VM_LABEL, dsBuilder, configuration);
this.virtualMap.registerMetrics(metrics);
- this.snapshotMetrics = new MerkleRootSnapshotMetrics(metrics);
}
/**
@@ -151,8 +139,6 @@ public VirtualMapState(
@NonNull final VirtualMap virtualMap, @NonNull final Metrics metrics, @NonNull final Time time) {
this.virtualMap = requireNonNull(virtualMap);
this.metrics = requireNonNull(metrics);
- this.time = requireNonNull(time);
- this.snapshotMetrics = new MerkleRootSnapshotMetrics(metrics);
}
/**
@@ -163,9 +149,7 @@ public VirtualMapState(
protected VirtualMapState(@NonNull final VirtualMapState from) {
this.virtualMap = from.virtualMap.copy();
this.metrics = from.metrics;
- this.time = from.time;
this.startupMode = from.startupMode;
- this.snapshotMetrics = new MerkleRootSnapshotMetrics(from.metrics);
this.listeners.addAll(from.listeners);
// Copy over the metadata
@@ -181,16 +165,6 @@ protected VirtualMapState(@NonNull final VirtualMapState from) {
*/
protected abstract T copyingConstructor();
- /**
- * Creates a new instance.
- *
- * @param virtualMap should have already registered metrics
- * @param metrics the platform metric instance to use when creating the new instance of state
- * @param time the time instance to use when creating the new instance of state
- */
- protected abstract T newInstance(
- @NonNull final VirtualMap virtualMap, @NonNull final Metrics metrics, @NonNull final Time time);
-
// State interface implementation
/**
@@ -251,40 +225,6 @@ public void computeHash() {
virtualMap.getHash();
}
- /**
- * {@inheritDoc}
- */
- @Override
- public void createSnapshot(@NonNull final Path targetPath) {
- requireNonNull(time);
- requireNonNull(snapshotMetrics);
- virtualMap.throwIfMutable();
- virtualMap.throwIfDestroyed();
- final long startTime = time.currentTimeMillis();
- MerkleTreeSnapshotWriter.createSnapshot(virtualMap, targetPath, getRound());
- snapshotMetrics.updateWriteStateToDiskTimeMetric(time.currentTimeMillis() - startTime);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public T loadSnapshot(@NonNull Path targetPath) throws IOException {
- final MerkleNode root =
- MerkleTreeSnapshotReader.readStateFileData(targetPath).stateRoot();
- if (!(root instanceof VirtualMap readVirtualMap)) {
- throw new IllegalStateException(
- "Root should be a VirtualMap, but it is " + root.getClass().getSimpleName() + " instead");
- }
-
- final var mutableCopy = readVirtualMap.copy();
- mutableCopy.registerMetrics(metrics);
- readVirtualMap.release();
- readVirtualMap = mutableCopy;
-
- return newInstance(readVirtualMap, metrics, time);
- }
-
/**
* Initializes the defined service state.
*
diff --git a/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/MerkleStateRoot.java b/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/MerkleStateRoot.java
index 3333a0d80692..8b635f2a5684 100644
--- a/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/MerkleStateRoot.java
+++ b/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/MerkleStateRoot.java
@@ -12,8 +12,6 @@
import com.swirlds.common.merkle.MerkleNode;
import com.swirlds.common.merkle.crypto.MerkleCryptography;
import com.swirlds.common.merkle.impl.PartialNaryMerkleInternal;
-import com.swirlds.common.merkle.utility.MerkleTreeSnapshotReader;
-import com.swirlds.common.merkle.utility.MerkleTreeSnapshotWriter;
import com.swirlds.common.utility.Labeled;
import com.swirlds.common.utility.RuntimeObjectRecord;
import com.swirlds.common.utility.RuntimeObjectRegistry;
@@ -48,7 +46,6 @@
import com.swirlds.virtualmap.VirtualMap;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.IOException;
-import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -848,27 +845,4 @@ public void computeHash() {
Thread.currentThread().interrupt();
}
}
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void createSnapshot(@NonNull final Path targetPath) {
- requireNonNull(time);
- requireNonNull(snapshotMetrics);
- throwIfMutable();
- throwIfDestroyed();
- final long startTime = time.currentTimeMillis();
- MerkleTreeSnapshotWriter.createSnapshot(this, targetPath, getRound());
- snapshotMetrics.updateWriteStateToDiskTimeMetric(time.currentTimeMillis() - startTime);
- }
-
- /**
- * {@inheritDoc}
- */
- @SuppressWarnings("unchecked")
- @Override
- public T loadSnapshot(@NonNull Path targetPath) throws IOException {
- return (T) MerkleTreeSnapshotReader.readStateFileData(targetPath).stateRoot();
- }
}
diff --git a/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/TestVirtualMapState.java b/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/TestVirtualMapState.java
index 152f7c7b57b8..cb658a311161 100644
--- a/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/TestVirtualMapState.java
+++ b/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/TestVirtualMapState.java
@@ -4,9 +4,7 @@
import static com.swirlds.state.test.fixtures.merkle.VirtualMapUtils.CONFIGURATION;
import com.swirlds.base.test.fixtures.time.FakeTime;
-import com.swirlds.base.time.Time;
import com.swirlds.common.metrics.noop.NoOpMetrics;
-import com.swirlds.metrics.api.Metrics;
import com.swirlds.state.MerkleNodeState;
import com.swirlds.state.State;
import com.swirlds.state.merkle.VirtualMapState;
@@ -38,12 +36,6 @@ protected TestVirtualMapState copyingConstructor() {
return new TestVirtualMapState(this);
}
- @Override
- protected TestVirtualMapState newInstance(
- @NonNull final VirtualMap virtualMap, @NonNull final Metrics metrics, @NonNull final Time time) {
- return new TestVirtualMapState(virtualMap);
- }
-
public static TestVirtualMapState createInstanceWithVirtualMapLabel(@NonNull final String virtualMapLabel) {
final var virtualMap = VirtualMapUtils.createVirtualMap(CONFIGURATION, virtualMapLabel);
return new TestVirtualMapState(virtualMap);
From c80769fe66625584453ccee190f24b2df8c651c5 Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Mon, 3 Nov 2025 21:43:13 -0500
Subject: [PATCH 02/23] Addressed a review comment - added missing generics
Signed-off-by: Ivan Malygin
---
.../com/hedera/node/app/state/merkle/SerializationTest.java | 4 ++--
.../src/main/java/com/swirlds/platform/SwirldsPlatform.java | 2 +-
.../java/com/swirlds/platform/builder/PlatformBuilder.java | 3 ++-
.../platform/eventhandling/TransactionHandlerTester.java | 6 +++---
.../swirlds/platform/reconnect/ReconnectControllerTest.java | 2 +-
5 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java
index 22739d3011c9..c34686dea046 100644
--- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java
+++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java
@@ -162,7 +162,7 @@ private void forceFlush(ReadableKVState, ?> state) {
@ValueSource(booleans = {true, false})
void simpleReadAndWrite(boolean forceFlush) throws IOException, ConstructableRegistryException {
final Schema schemaV1 = createV1Schema();
- final StateLifecycleManager stateLifecycleManager = createStateLifecycleManager(schemaV1);
+ final StateLifecycleManager> stateLifecycleManager = createStateLifecycleManager(schemaV1);
final MerkleNodeState originalTree = stateLifecycleManager.getMutableState();
// When we serialize it to bytes and deserialize it back into a tree
@@ -221,7 +221,7 @@ void snapshot() throws IOException {
@Test
void dualReadAndWrite() throws IOException, ConstructableRegistryException {
final Schema schemaV1 = createV1Schema();
- final StateLifecycleManager stateLifecycleManager = createStateLifecycleManager(schemaV1);
+ final StateLifecycleManager> stateLifecycleManager = createStateLifecycleManager(schemaV1);
final MerkleNodeState originalTree = stateLifecycleManager.getMutableState();
MerkleNodeState copy = stateLifecycleManager.copyMutableState(); // make a copy to make VM flushable
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
index b1b8a9e2203f..2fcaa7ac595f 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
@@ -217,7 +217,7 @@ public SwirldsPlatform(@NonNull final PlatformComponentBuilder builder) {
initializeState(this, platformContext, initialState, blocks.consensusStateEventHandler(), platformStateFacade);
// This object makes a copy of the state. After this point, initialState becomes immutable.
- final StateLifecycleManager stateLifecycleManager = blocks.stateLifecycleManager();
+ final StateLifecycleManager> stateLifecycleManager = blocks.stateLifecycleManager();
stateLifecycleManager.initState(initialState.getState(), true);
platformStateFacade.setCreationSoftwareVersionTo(stateLifecycleManager.getMutableState(), blocks.appVersion());
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuilder.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuilder.java
index a8b680d980b6..6a5e1c569fa5 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuilder.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuilder.java
@@ -37,6 +37,7 @@
import com.swirlds.platform.state.iss.IssScratchpad;
import com.swirlds.platform.state.service.PlatformStateFacade;
import com.swirlds.platform.state.signed.ReservedSignedState;
+import com.swirlds.platform.state.signed.SignedState;
import com.swirlds.platform.system.Platform;
import com.swirlds.platform.wiring.PlatformComponents;
import com.swirlds.platform.wiring.PlatformWiring;
@@ -434,7 +435,7 @@ public PlatformComponentBuilder buildComponentBuilder() {
final ApplicationCallbacks callbacks =
new ApplicationCallbacks(preconsensusEventConsumer, snapshotOverrideConsumer, staleEventConsumer);
- final StateLifecycleManager stateLifecycleManager = new StateLifecycleManagerImpl(
+ final StateLifecycleManager stateLifecycleManager = new StateLifecycleManagerImpl<>(
platformContext.getMetrics(), platformContext.getTime(), createStateFromVirtualMap);
if (model == null) {
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/TransactionHandlerTester.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/TransactionHandlerTester.java
index 34465087a29a..701fc3684259 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/TransactionHandlerTester.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/TransactionHandlerTester.java
@@ -33,7 +33,7 @@
*/
public class TransactionHandlerTester {
private final PlatformStateModifier platformState;
- private final StateLifecycleManager stateLifecycleManager;
+ private final StateLifecycleManager> stateLifecycleManager;
private final DefaultTransactionHandler defaultTransactionHandler;
private final List submittedActions = new ArrayList<>();
private final List handledRounds = new ArrayList<>();
@@ -66,7 +66,7 @@ public TransactionHandlerTester() {
.when(consensusStateEventHandler)
.onHandleConsensusRound(any(), same(consensusState), any());
final StatusActionSubmitter statusActionSubmitter = submittedActions::add;
- stateLifecycleManager = new StateLifecycleManagerImpl(
+ stateLifecycleManager = new StateLifecycleManagerImpl<>(
platformContext.getMetrics(), platformContext.getTime(), vm -> consensusState);
stateLifecycleManager.initState(consensusState, true);
defaultTransactionHandler = new DefaultTransactionHandler(
@@ -110,7 +110,7 @@ public List getHandledRounds() {
/**
* @return the {@link StateLifecycleManager} used by this tester
*/
- public StateLifecycleManager getStateLifecycleManager() {
+ public StateLifecycleManager> getStateLifecycleManager() {
return stateLifecycleManager;
}
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectControllerTest.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectControllerTest.java
index ab7b976449b9..bff45adc9777 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectControllerTest.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectControllerTest.java
@@ -84,7 +84,7 @@ class ReconnectControllerTest {
private MerkleCryptography merkleCryptography;
private Platform platform;
private PlatformCoordinator platformCoordinator;
- private StateLifecycleManager stateLifecycleManager;
+ private StateLifecycleManager> stateLifecycleManager;
private SavedStateController savedStateController;
private ConsensusStateEventHandler consensusStateEventHandler;
private ReservedSignedStateResultPromise peerReservedSignedStateResultPromise;
From d14a8af787abef83892f854e9e734bbc10ead348 Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Tue, 4 Nov 2025 09:49:43 -0500
Subject: [PATCH 03/23] Update
platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
Co-authored-by: Nikita Lebedev
Signed-off-by: Ivan Malygin
---
.../com/swirlds/state/merkle/StateLifecycleManagerImpl.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
index 1ee25591cd34..e2f44fa33e81 100644
--- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
+++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
@@ -65,8 +65,8 @@ public class StateLifecycleManagerImpl implement
*/
public StateLifecycleManagerImpl(
@NonNull final Metrics metrics,
- @NonNull Time time,
- Function stateSupplier) {
+ @NonNull final Time time,
+ @NonNull final Function stateSupplier) {
requireNonNull(metrics);
this.stateSupplier = stateSupplier;
this.metrics = metrics;
From 835d3411824d1e41b282e5f67f4e0bb4ee1fe88c Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Tue, 4 Nov 2025 09:49:52 -0500
Subject: [PATCH 04/23] Update
platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
Co-authored-by: Nikita Lebedev
Signed-off-by: Ivan Malygin
---
.../com/swirlds/state/merkle/StateLifecycleManagerImpl.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
index e2f44fa33e81..f820e0916439 100644
--- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
+++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
@@ -180,6 +180,9 @@ public void createSnapshot(final @NonNull Path targetPath) {
snapshotSource.set(null);
}
+ /**
+ * {@inheritDoc}
+ */
@Override
public MerkleNodeState loadSnapshot(@NonNull Path targetPath) {
final MerkleNode root;
From a1c4f94b2b9628cd8a0b42efcff0143b8cc44c5d Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Tue, 4 Nov 2025 09:50:01 -0500
Subject: [PATCH 05/23] Update
platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
Co-authored-by: Nikita Lebedev
Signed-off-by: Ivan Malygin
---
.../com/swirlds/state/merkle/StateLifecycleManagerImpl.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
index f820e0916439..10e615209566 100644
--- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
+++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
@@ -166,8 +166,9 @@ private void setLatestImmutableState(final MerkleNodeState latestImmutableState)
}
/**
- * {@inheritDoc}}
+ * {@inheritDoc}
*/
+ @Override
public void createSnapshot(final @NonNull Path targetPath) {
requireNonNull(time);
requireNonNull(snapshotMetrics);
From b0f959679a34300f56e8f5c5c8ec008443ac86c5 Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Tue, 4 Nov 2025 10:19:57 -0500
Subject: [PATCH 06/23] Update
platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
Co-authored-by: Nikita Lebedev
Signed-off-by: Ivan Malygin
---
.../com/swirlds/state/merkle/StateLifecycleManagerImpl.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
index 10e615209566..286523fbbf98 100644
--- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
+++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
@@ -125,7 +125,7 @@ public MerkleNodeState copyMutableState() {
return stateRef.get();
}
- private void updateStateRefs(MerkleNodeState state) {
+ private void copyAndUpdateStateRefs(MerkleNodeState state) {
// Create a fast copy so there is always an immutable state to
// invoke handleTransaction on for pre-consensus transactions
final long copyStart = System.nanoTime();
From 5b1b28c69992bb91c690e37635359dbc9c7f7b0c Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Tue, 4 Nov 2025 10:25:11 -0500
Subject: [PATCH 07/23] Removed unused fields, improved javadoc
Signed-off-by: Ivan Malygin
---
.../com/swirlds/state/StateLifecycleManager.java | 12 +++++++++---
.../state/merkle/StateLifecycleManagerImpl.java | 8 ++++++--
.../state/test/fixtures/merkle/MerkleStateRoot.java | 12 ------------
3 files changed, 15 insertions(+), 17 deletions(-)
diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
index d7a29fcac6a3..8db30f69175a 100644
--- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
+++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
@@ -5,8 +5,14 @@
import java.nio.file.Path;
/**
- * An implementaion of this class is responsible
- * @param a type of the snapshot source, should implement {@link MerkleNodeStateAware}
+ * Implementations of this interface are responsible for managing the state lifecycle:
+ *
+ * - Maintaining references to a mutable state and the latest immutable state.
+ * - Creating snapshots of the state.
+ * - Loading snapshots of the state.
+ * - Creating a mutable copy of the state, while making the current mutable state immutable.
+ *
+ * @param A type of the snapshot source, which should implement {@link MerkleNodeStateAware}.
*/
public interface StateLifecycleManager {
@@ -46,7 +52,7 @@ public interface StateLifecycleManager {
void createSnapshot(@NonNull Path targetPath);
/**
- * Loads a snapshot of a state and uses it as a new mutable state.
+ * Loads a snapshot of a state.
*
* @param targetPath The path to load the snapshot from.
* @return mutable copy of the loaded state
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
index 286523fbbf98..2db7f583c45c 100644
--- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
+++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
@@ -90,7 +90,7 @@ public void initState(@NonNull final MerkleNodeState state, boolean onStartup) {
throw new IllegalStateException("Attempt to set initial state when there is already a state reference.");
}
- updateStateRefs(state);
+ copyAndUpdateStateRefs(state);
}
@Override
@@ -121,10 +121,14 @@ public MerkleNodeState getLatestImmutableState() {
@Override
public MerkleNodeState copyMutableState() {
final MerkleNodeState state = stateRef.get();
- updateStateRefs(state);
+ copyAndUpdateStateRefs(state);
return stateRef.get();
}
+ /**
+ * Copies the provided state and updates both the latest immutable state and the mutable state reference.
+ * @param state the state to copy and update references for
+ */
private void copyAndUpdateStateRefs(MerkleNodeState state) {
// Create a fast copy so there is always an immutable state to
// invoke handleTransaction on for pre-consensus transactions
diff --git a/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/MerkleStateRoot.java b/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/MerkleStateRoot.java
index 8b635f2a5684..82b2654e1bdf 100644
--- a/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/MerkleStateRoot.java
+++ b/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/MerkleStateRoot.java
@@ -19,7 +19,6 @@
import com.swirlds.state.State;
import com.swirlds.state.StateChangeListener;
import com.swirlds.state.lifecycle.StateMetadata;
-import com.swirlds.state.merkle.MerkleRootSnapshotMetrics;
import com.swirlds.state.spi.CommittableWritableStates;
import com.swirlds.state.spi.EmptyReadableStates;
import com.swirlds.state.spi.KVChangeListener;
@@ -103,11 +102,6 @@ public abstract class MerkleStateRoot> extends Part
return services;
}
- /**
- * Metrics for the snapshot creation process
- */
- private final MerkleRootSnapshotMetrics snapshotMetrics;
-
/**
* Maintains information about all services known by this instance. Map keys are
* service names, values are service states by service ID.
@@ -135,8 +129,6 @@ public abstract class MerkleStateRoot> extends Part
private final Metrics metrics;
- private final Time time;
-
private final MerkleCryptography merkleCryptography;
/**
@@ -149,9 +141,7 @@ public MerkleStateRoot(
@NonNull final MerkleCryptography merkleCryptography) {
this.registryRecord = RuntimeObjectRegistry.createRecord(getClass());
this.metrics = requireNonNull(metrics);
- this.time = requireNonNull(time);
this.merkleCryptography = requireNonNull(merkleCryptography);
- this.snapshotMetrics = new MerkleRootSnapshotMetrics(metrics);
}
/**
@@ -164,9 +154,7 @@ protected MerkleStateRoot(@NonNull final MerkleStateRoot from) {
super(from);
this.registryRecord = RuntimeObjectRegistry.createRecord(getClass());
this.metrics = from.metrics;
- this.time = from.time;
this.merkleCryptography = from.merkleCryptography;
- this.snapshotMetrics = new MerkleRootSnapshotMetrics(from.metrics);
this.listeners.addAll(from.listeners);
// Copy over the metadata
From 4245de1896519fbdfbf9f828a286db584cbe8329 Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Tue, 4 Nov 2025 11:49:51 -0500
Subject: [PATCH 08/23] Added missing generics.
Signed-off-by: Ivan Malygin
---
.../com/hedera/node/app/state/merkle/SerializationTest.java | 2 +-
.../platform/eventhandling/DefaultTransactionHandler.java | 4 ++--
.../java/com/swirlds/platform/gossip/SyncGossipModular.java | 4 ++--
.../platform/network/protocol/ReconnectStateSyncProtocol.java | 4 ++--
.../com/swirlds/platform/reconnect/ReconnectController.java | 4 ++--
.../platform/reconnect/ReconnectStatePeerProtocol.java | 4 ++--
6 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java
index c34686dea046..4b4966c80b5e 100644
--- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java
+++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java
@@ -285,7 +285,7 @@ private void initServices(Schema schemaV1, MerkleNodeState load
loadedTree.getRoot().migrate(MINIMUM_SUPPORTED_VERSION);
}
- private StateLifecycleManager createStateLifecycleManager(Schema schemaV1) {
+ private StateLifecycleManager> createStateLifecycleManager(Schema schemaV1) {
final SignedState randomState =
new RandomSignedStateGenerator().setRound(1).build();
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/DefaultTransactionHandler.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/DefaultTransactionHandler.java
index 279360daa267..39a8821936f6 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/DefaultTransactionHandler.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/DefaultTransactionHandler.java
@@ -62,7 +62,7 @@ public class DefaultTransactionHandler implements TransactionHandler {
/**
* The class responsible for all interactions with the swirld state
*/
- private final StateLifecycleManager stateLifecycleManager;
+ private final StateLifecycleManager> stateLifecycleManager;
private final RoundHandlingMetrics handlerMetrics;
@@ -137,7 +137,7 @@ public class DefaultTransactionHandler implements TransactionHandler {
*/
public DefaultTransactionHandler(
@NonNull final PlatformContext platformContext,
- @NonNull final StateLifecycleManager stateLifecycleManager,
+ @NonNull final StateLifecycleManager> stateLifecycleManager,
@NonNull final StatusActionSubmitter statusActionSubmitter,
@NonNull final SemanticVersion softwareVersion,
@NonNull final PlatformStateFacade platformStateFacade,
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gossip/SyncGossipModular.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gossip/SyncGossipModular.java
index 6c2a408f1d7f..e097941e0fae 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gossip/SyncGossipModular.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gossip/SyncGossipModular.java
@@ -72,7 +72,7 @@ public class SyncGossipModular implements Gossip {
private final AbstractSyncProtocol> syncProtocol;
private final FallenBehindMonitor fallenBehindMonitor;
private final AbstractShadowgraphSynchronizer synchronizer;
- private final StateLifecycleManager stateLifecycleManager;
+ private final StateLifecycleManager> stateLifecycleManager;
private final Function createStateFromVirtualMap;
// this is not a nice dependency, should be removed as well as the sharedState
@@ -103,7 +103,7 @@ public SyncGossipModular(
@NonNull final Roster roster,
@NonNull final NodeId selfId,
@NonNull final SemanticVersion appVersion,
- @NonNull final StateLifecycleManager stateLifecycleManager,
+ @NonNull final StateLifecycleManager> stateLifecycleManager,
@NonNull final Supplier latestCompleteState,
@NonNull final IntakeEventCounter intakeEventCounter,
@NonNull final PlatformStateFacade platformStateFacade,
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/network/protocol/ReconnectStateSyncProtocol.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/network/protocol/ReconnectStateSyncProtocol.java
index 1e26ab142232..2e3c5845ac90 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/network/protocol/ReconnectStateSyncProtocol.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/network/protocol/ReconnectStateSyncProtocol.java
@@ -39,7 +39,7 @@ public class ReconnectStateSyncProtocol implements Protocol {
private final PlatformContext platformContext;
private final AtomicReference platformStatus = new AtomicReference<>(PlatformStatus.STARTING_UP);
private final ReservedSignedStateResultPromise reservedSignedStateResultPromise;
- private final StateLifecycleManager stateLifecycleManager;
+ private final StateLifecycleManager> stateLifecycleManager;
private final Function createStateFromVirtualMap;
public ReconnectStateSyncProtocol(
@@ -52,7 +52,7 @@ public ReconnectStateSyncProtocol(
@NonNull final FallenBehindMonitor fallenBehindManager,
@NonNull final PlatformStateFacade platformStateFacade,
@NonNull final ReservedSignedStateResultPromise reservedSignedStateResultPromise,
- @NonNull final StateLifecycleManager stateLifecycleManager,
+ @NonNull final StateLifecycleManager> stateLifecycleManager,
@NonNull final Function createStateFromVirtualMap) {
this.platformContext = Objects.requireNonNull(platformContext);
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectController.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectController.java
index a72262f9e143..6684cd559fbd 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectController.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectController.java
@@ -72,7 +72,7 @@ public class ReconnectController implements Runnable {
private final Platform platform;
private final PlatformContext platformContext;
private final PlatformCoordinator platformCoordinator;
- private final StateLifecycleManager stateLifecycleManager;
+ private final StateLifecycleManager> stateLifecycleManager;
private final SavedStateController savedStateController;
private final ConsensusStateEventHandler consensusStateEventHandler;
private final ReservedSignedStateResultPromise peerReservedSignedStateResultPromise;
@@ -90,7 +90,7 @@ public ReconnectController(
@NonNull final Platform platform,
@NonNull final PlatformContext platformContext,
@NonNull final PlatformCoordinator platformCoordinator,
- @NonNull final StateLifecycleManager stateLifecycleManager,
+ @NonNull final StateLifecycleManager> stateLifecycleManager,
@NonNull final SavedStateController savedStateController,
@NonNull final ConsensusStateEventHandler consensusStateEventHandler,
@NonNull final ReservedSignedStateResultPromise peerReservedSignedStateResultPromise,
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectStatePeerProtocol.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectStatePeerProtocol.java
index 2e4e282db531..07784d68e60f 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectStatePeerProtocol.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectStatePeerProtocol.java
@@ -82,7 +82,7 @@ public class ReconnectStatePeerProtocol implements PeerProtocol {
private final Time time;
private final PlatformContext platformContext;
private final ReservedSignedStateResultPromise reservedSignedStateResultPromise;
- private final StateLifecycleManager stateLifecycleManager;
+ private final StateLifecycleManager> stateLifecycleManager;
private final Function createStateFromVirtualMap;
/**
@@ -112,7 +112,7 @@ public ReconnectStatePeerProtocol(
@NonNull final Time time,
@NonNull final PlatformStateFacade platformStateFacade,
@NonNull final ReservedSignedStateResultPromise reservedSignedStateResultPromise,
- @NonNull final StateLifecycleManager stateLifecycleManager,
+ @NonNull final StateLifecycleManager> stateLifecycleManager,
@NonNull final Function createStateFromVirtualMap) {
this.platformContext = Objects.requireNonNull(platformContext);
From 089d78b64dae03db4e182b105a1749208ed85a97 Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Tue, 4 Nov 2025 13:37:19 -0500
Subject: [PATCH 09/23] A flaky test fix.
Signed-off-by: Ivan Malygin
---
.../protocol/ReservedSignedStateResultPromiseTest.java | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/network/protocol/ReservedSignedStateResultPromiseTest.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/network/protocol/ReservedSignedStateResultPromiseTest.java
index 95837dc5dde8..cb0f6735e078 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/network/protocol/ReservedSignedStateResultPromiseTest.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/network/protocol/ReservedSignedStateResultPromiseTest.java
@@ -357,6 +357,7 @@ void testMultipleProvidersCompeting() throws InterruptedException {
final AtomicInteger providedCount = new AtomicInteger(0);
final AtomicInteger consumedCount = new AtomicInteger(0);
final CountDownLatch allConsumed = new CountDownLatch(1);
+ final CountDownLatch allProvided = new CountDownLatch(numProviders);
// Consumer
final Thread consumer = new Thread(() -> {
@@ -378,7 +379,6 @@ void testMultipleProvidersCompeting() throws InterruptedException {
final ExecutorService executor = Executors.newFixedThreadPool(numProviders);
for (int i = 0; i < numProviders; i++) {
executor.submit(() -> {
- int provided = 0;
while (providedCount.get() < numResources) {
if (promise.acquire()) {
if (providedCount.get() < numResources) {
@@ -386,7 +386,7 @@ void testMultipleProvidersCompeting() throws InterruptedException {
final ReservedSignedState mockState = ReservedSignedState.createNullReservation();
promise.resolveWithValue(mockState);
providedCount.incrementAndGet();
- provided++;
+ allProvided.countDown();
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
return;
@@ -400,6 +400,7 @@ void testMultipleProvidersCompeting() throws InterruptedException {
});
}
+ assertTrue(allProvided.await(5, TimeUnit.SECONDS), "All resources should be provided");
assertTrue(allConsumed.await(5, TimeUnit.SECONDS), "All resources should be consumed");
assertEquals(numResources, consumedCount.get(), "Consumer should receive all resources");
assertEquals(numResources, providedCount.get(), "Exactly numResources should be provided");
From a6d272fb29b225c87e5f6e133a0762025361b2df Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Tue, 4 Nov 2025 16:34:26 -0500
Subject: [PATCH 10/23] Addressed review comments: - removed an unused field -
updated javadoc - gathered all copy and update refs logic in one method
Signed-off-by: Ivan Malygin
---
.../com/swirlds/platform/SwirldsPlatform.java | 2 +-
.../DefaultSavedStateController.java | 10 +---
.../platform/StateFileManagerTests.java | 2 +-
.../swirlds/state/StateLifecycleManager.java | 8 +--
.../merkle/StateLifecycleManagerImpl.java | 52 +++++++------------
5 files changed, 27 insertions(+), 47 deletions(-)
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
index 2fcaa7ac595f..5b2ccd78a11a 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
@@ -190,7 +190,7 @@ public SwirldsPlatform(@NonNull final PlatformComponentBuilder builder) {
final LatestCompleteStateNexus latestCompleteStateNexus = new DefaultLatestCompleteStateNexus(platformContext);
- savedStateController = new DefaultSavedStateController(platformContext, blocks.stateLifecycleManager());
+ savedStateController = new DefaultSavedStateController(platformContext);
stateLifecycleManager = blocks.stateLifecycleManager();
final SignedStateMetrics signedStateMetrics = new SignedStateMetrics(platformContext.getMetrics());
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/DefaultSavedStateController.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/DefaultSavedStateController.java
index 3dc076c4abdf..65fe4dde7cea 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/DefaultSavedStateController.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/DefaultSavedStateController.java
@@ -13,7 +13,6 @@
import com.swirlds.platform.state.signed.ReservedSignedState;
import com.swirlds.platform.state.signed.SignedState;
import com.swirlds.platform.state.snapshot.StateToDiskReason;
-import com.swirlds.state.StateLifecycleManager;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.time.Instant;
@@ -32,19 +31,14 @@ public class DefaultSavedStateController implements SavedStateController {
private Instant previousSavedStateTimestamp;
private final StateConfig stateConfig;
- private final StateLifecycleManager stateLifecycleManager;
/**
* Constructor
*
- * @param platformContext the platform context
- * @param stateLifecycleManager
+ * @param platformContext the platform context*
*/
- public DefaultSavedStateController(
- @NonNull final PlatformContext platformContext,
- @NonNull final StateLifecycleManager stateLifecycleManager) {
+ public DefaultSavedStateController(@NonNull final PlatformContext platformContext) {
this.stateConfig = platformContext.getConfiguration().getConfigData(StateConfig.class);
- this.stateLifecycleManager = stateLifecycleManager;
}
/**
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/StateFileManagerTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/StateFileManagerTests.java
index c8831755e6ee..693e0963b6db 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/StateFileManagerTests.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/StateFileManagerTests.java
@@ -232,7 +232,7 @@ void sequenceOfStatesTest(final boolean startAtGenesis) throws IOException {
final StateSnapshotManager manager = new DefaultStateSnapshotManager(
context, MAIN_CLASS_NAME, SELF_ID, SWIRLD_NAME, TEST_PLATFORM_STATE_FACADE, stateLifecycleManager);
- final SavedStateController controller = new DefaultSavedStateController(context, stateLifecycleManager);
+ final SavedStateController controller = new DefaultSavedStateController(context);
Instant timestamp;
final long firstRound;
diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
index 8db30f69175a..4ba9217ae01f 100644
--- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
+++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
@@ -12,24 +12,26 @@
* Loading snapshots of the state.
* Creating a mutable copy of the state, while making the current mutable state immutable.
*
+ *
+ * An implementation of this class must be thread-safe.
* @param A type of the snapshot source, which should implement {@link MerkleNodeStateAware}.
*/
public interface StateLifecycleManager {
/**
- * Set the initial State. This method should only be on a startup of after a reconnect.
+ * Set the initial State. This method should only be on a startup or after a reconnect.
*
* @param state the initial state
*/
void initState(@NonNull final MerkleNodeState state, boolean onStartup);
/**
- * Get the mutable state.
+ * Get the mutable state. This method is idempotent.
*/
MerkleNodeState getMutableState();
/**
- * Get the latest immutable state.
+ * Get the latest immutable state. This method is idempotent.
* @return the latest immutable state.
*/
MerkleNodeState getLatestImmutableState();
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
index 2db7f583c45c..9d31b99b0d14 100644
--- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
+++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
@@ -126,47 +126,31 @@ public MerkleNodeState copyMutableState() {
}
/**
- * Copies the provided state and updates both the latest immutable state and the mutable state reference.
- * @param state the state to copy and update references for
+ * Copies the provided to and updates both the latest immutable to and the mutable to reference.
+ * @param stateToCopy the state to copy and update references for
*/
- private void copyAndUpdateStateRefs(MerkleNodeState state) {
- // Create a fast copy so there is always an immutable state to
- // invoke handleTransaction on for pre-consensus transactions
+ private synchronized void copyAndUpdateStateRefs(MerkleNodeState stateToCopy) {
final long copyStart = System.nanoTime();
- // Create a fast copy
- final MerkleNodeState copy = state.copy();
+ final MerkleNodeState newMutableState = stateToCopy.copy();
// Increment the reference count because this reference becomes the new value
- copy.getRoot().reserve();
+ newMutableState.getRoot().reserve();
final long copyEnd = System.nanoTime();
stateMetrics.stateCopyMicros((copyEnd - copyStart) * NANOSECONDS_TO_MICROSECONDS);
- // Set latest immutable first to prevent the newly immutable stateRoot from being deleted between setting the
- // stateRef and the latestImmutableState
- setLatestImmutableState(state);
- updateStateRef(copy);
- }
-
- /**
- * Sets the consensus state to the state provided. Must be mutable and have a reference count of at least 1.
- *
- * @param state a new mutable state
- */
- private void updateStateRef(final MerkleNodeState state) {
- final var currVal = stateRef.get();
- if (currVal != null && !currVal.isDestroyed()) {
- currVal.release();
+ // releasing previous immutable previousMutableState
+ final State previousImmutableState = latestImmutableStateRef.get();
+ if (previousImmutableState != null && !previousImmutableState.isDestroyed()) {
+ previousImmutableState.release();
}
- // Do not increment the reference count because the state provided already has a reference count of at least
- // one to represent this reference and to prevent it from being deleted before this reference is set.
- stateRef.set(state);
- }
-
- private void setLatestImmutableState(final MerkleNodeState latestImmutableState) {
- final State currVal = latestImmutableStateRef.get();
- if (currVal != null && !currVal.isDestroyed()) {
- currVal.release();
+ stateToCopy.getRoot().reserve();
+ latestImmutableStateRef.set(stateToCopy);
+ final var previousMutableState = stateRef.get();
+ if (previousMutableState != null && !previousMutableState.isDestroyed()) {
+ previousMutableState.release();
}
- latestImmutableState.getRoot().reserve();
- latestImmutableStateRef.set(latestImmutableState);
+ // Do not increment the reference count because the stateToCopy provided already has a reference count of at
+ // least
+ // one to represent this reference and to prevent it from being deleted before this reference is set.
+ stateRef.set(newMutableState);
}
/**
From afdfcb3b4018f1da15603e0f122a85b16b514a0e Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Tue, 4 Nov 2025 17:18:14 -0500
Subject: [PATCH 11/23] Addressed review comments: - improved javadoc - added
missing annotations - added missing final modifiers
Signed-off-by: Ivan Malygin
---
.../com/swirlds/platform/SwirldsPlatform.java | 3 +-
.../reconnect/ReconnectController.java | 4 +-
.../snapshot/DefaultStateSnapshotManager.java | 2 +-
.../state/snapshot/SignedStateFileWriter.java | 6 +--
.../reconnect/ReconnectControllerTest.java | 2 +-
.../swirlds/state/StateLifecycleManager.java | 14 ++++---
.../merkle/StateLifecycleManagerImpl.java | 37 +++++++++++++++----
7 files changed, 45 insertions(+), 23 deletions(-)
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
index 5b2ccd78a11a..5cf05fdfd992 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
@@ -217,7 +217,7 @@ public SwirldsPlatform(@NonNull final PlatformComponentBuilder builder) {
initializeState(this, platformContext, initialState, blocks.consensusStateEventHandler(), platformStateFacade);
// This object makes a copy of the state. After this point, initialState becomes immutable.
- final StateLifecycleManager> stateLifecycleManager = blocks.stateLifecycleManager();
+ final StateLifecycleManager stateLifecycleManager = blocks.stateLifecycleManager();
stateLifecycleManager.initState(initialState.getState(), true);
platformStateFacade.setCreationSoftwareVersionTo(stateLifecycleManager.getMutableState(), blocks.appVersion());
@@ -388,7 +388,6 @@ public void performPcesRecovery() {
} else {
final SignedState signedState = reservedState.get();
signedState.markAsStateToSave(StateToDiskReason.PCES_RECOVERY_COMPLETE);
- stateLifecycleManager.setSnapshotSource(signedState);
final StateDumpRequest request =
StateDumpRequest.create(signedState.reserve("dumping PCES recovery state"));
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectController.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectController.java
index 6684cd559fbd..f68115e7dc1c 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectController.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectController.java
@@ -72,7 +72,7 @@ public class ReconnectController implements Runnable {
private final Platform platform;
private final PlatformContext platformContext;
private final PlatformCoordinator platformCoordinator;
- private final StateLifecycleManager> stateLifecycleManager;
+ private final StateLifecycleManager stateLifecycleManager;
private final SavedStateController savedStateController;
private final ConsensusStateEventHandler consensusStateEventHandler;
private final ReservedSignedStateResultPromise peerReservedSignedStateResultPromise;
@@ -90,7 +90,7 @@ public ReconnectController(
@NonNull final Platform platform,
@NonNull final PlatformContext platformContext,
@NonNull final PlatformCoordinator platformCoordinator,
- @NonNull final StateLifecycleManager> stateLifecycleManager,
+ @NonNull final StateLifecycleManager stateLifecycleManager,
@NonNull final SavedStateController savedStateController,
@NonNull final ConsensusStateEventHandler consensusStateEventHandler,
@NonNull final ReservedSignedStateResultPromise peerReservedSignedStateResultPromise,
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/DefaultStateSnapshotManager.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/DefaultStateSnapshotManager.java
index 88e4d680ac98..aef40b3a0c77 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/DefaultStateSnapshotManager.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/DefaultStateSnapshotManager.java
@@ -102,7 +102,7 @@ public DefaultStateSnapshotManager(
@NonNull final NodeId selfId,
@NonNull final String swirldName,
@NonNull final PlatformStateFacade platformStateFacade,
- @NonNull StateLifecycleManager stateLifecycleManager) {
+ @NonNull final StateLifecycleManager stateLifecycleManager) {
this.platformContext = Objects.requireNonNull(platformContext);
this.time = platformContext.getTime();
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/SignedStateFileWriter.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/SignedStateFileWriter.java
index 09c5e5b79b2e..bcfe8e8876fd 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/SignedStateFileWriter.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/SignedStateFileWriter.java
@@ -131,6 +131,8 @@ public static void writeSignatureSetFile(final @NonNull Path directory, final @N
* @param platformContext the platform context
* @param selfId the id of the platform
* @param directory the directory where all files should be placed
+ * @param platformStateFacade the facade to access the platform state
+ * @param stateLifecycleManager the state lifecycle manager
*/
public static void writeSignedStateFilesToDirectory(
@Nullable final PlatformContext platformContext,
@@ -150,9 +152,7 @@ public static void writeSignedStateFilesToDirectory(
writeMetadataFile(selfId, directory, snapshotSource, platformStateFacade);
writeEmergencyRecoveryFile(directory, snapshotSource);
final Roster currentRoster = snapshotSource.getRoster();
- if (currentRoster != null) {
- writeRosterFile(directory, currentRoster);
- }
+ writeRosterFile(directory, currentRoster);
writeSettingsUsed(directory, platformContext.getConfiguration());
if (selfId != null) {
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectControllerTest.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectControllerTest.java
index bff45adc9777..7b7944e30684 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectControllerTest.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectControllerTest.java
@@ -84,7 +84,7 @@ class ReconnectControllerTest {
private MerkleCryptography merkleCryptography;
private Platform platform;
private PlatformCoordinator platformCoordinator;
- private StateLifecycleManager> stateLifecycleManager;
+ private StateLifecycleManager stateLifecycleManager;
private SavedStateController savedStateController;
private ConsensusStateEventHandler consensusStateEventHandler;
private ReservedSignedStateResultPromise peerReservedSignedStateResultPromise;
diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
index 4ba9217ae01f..c367cf802d3c 100644
--- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
+++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
@@ -26,12 +26,14 @@ public interface StateLifecycleManager {
void initState(@NonNull final MerkleNodeState state, boolean onStartup);
/**
- * Get the mutable state. This method is idempotent.
+ * Get the mutable state. Consecutive calls to this method may return different instances,
+ * if this method is not called on the one and the only thread that is calling {@link #copyMutableState}
*/
MerkleNodeState getMutableState();
/**
- * Get the latest immutable state. This method is idempotent.
+ * Get the latest immutable state. Consecutive calls to this method may return different instances,
+ * if this method is not called on the one and the only thread that is calling {@link #copyMutableState}
* @return the latest immutable state.
*/
MerkleNodeState getLatestImmutableState();
@@ -46,8 +48,8 @@ public interface StateLifecycleManager {
/**
* Creates a snapshot for the state that was previously set with {@link #setSnapshotSource(MerkleNodeStateAware)}.
- * The state has to be hashed before calling this method. Once the snapshot is created, the manager releases the source
- * state of the snapshot and clears the reference to it.
+ * The state has to be hashed before calling this method. Once the snapshot is created, the manager resets the snapshot source
+ * to null. Therefore, this method is not idempotent.
*
* @param targetPath The path to save the snapshot.
*/
@@ -62,10 +64,10 @@ public interface StateLifecycleManager {
MerkleNodeState loadSnapshot(@NonNull Path targetPath);
/**
- * Creates a mutable copy of the state. The previous mutable state becomes immutable,
+ * Creates a mutable copy of the mutable state. The previous mutable state becomes immutable,
* replacing the latest immutable state.
*
- * @return a mutable copy of the current mutable state which became the latest immutable state.
+ * @return a mutable copy of the previous mutable state
*/
MerkleNodeState copyMutableState();
}
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
index 9d31b99b0d14..62fbc0f3ebf5 100644
--- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
+++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
@@ -15,6 +15,8 @@
import com.swirlds.state.StateLifecycleManager;
import com.swirlds.virtualmap.VirtualMap;
import edu.umd.cs.findbugs.annotations.NonNull;
+import edu.umd.cs.findbugs.annotations.Nullable;
+
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
@@ -24,6 +26,8 @@
/**
* This class is responsible for maintaining references to the mutable state and the latest immutable state.
* It also updates these references upon state signing.
+ *
+ * @param A type of the snapshot source, which should implement {@link MerkleNodeStateAware}.
*/
public class StateLifecycleManagerImpl implements StateLifecycleManager {
@@ -37,9 +41,19 @@ public class StateLifecycleManagerImpl implement
*/
private final MerkleRootSnapshotMetrics snapshotMetrics;
+ /**
+ * The object for time measurements
+ */
private final Time time;
+ /**
+ * The metrics registry
+ */
private final Metrics metrics;
+
+ /**
+ * A factory object to create an instance of a class implementing {@link MerkleNodeState} from a {@link VirtualMap}
+ */
private final Function stateSupplier;
/**
@@ -80,7 +94,7 @@ public StateLifecycleManagerImpl(
*
* @param state the initial state
*/
- public void initState(@NonNull final MerkleNodeState state, boolean onStartup) {
+ public void initState(@NonNull final MerkleNodeState state, final boolean onStartup) {
requireNonNull(state);
state.throwIfDestroyed("state must not be destroyed");
@@ -94,9 +108,9 @@ public void initState(@NonNull final MerkleNodeState state, boolean onStartup) {
}
@Override
- public void setSnapshotSource(@NonNull T source) {
+ public void setSnapshotSource(@NonNull final T source) {
requireNonNull(source);
- MerkleNodeState state = source.getState();
+ final MerkleNodeState state = source.getState();
state.throwIfDestroyed("state must not be destroyed");
state.throwIfMutable("state must be immutable");
boolean result = snapshotSource.compareAndSet(null, source);
@@ -104,6 +118,7 @@ public void setSnapshotSource(@NonNull T source) {
}
@Override
+ @Nullable
public T getSnapshotSource() {
return snapshotSource.get();
}
@@ -114,11 +129,13 @@ public MerkleNodeState getMutableState() {
}
@Override
+ @Nullable
public MerkleNodeState getLatestImmutableState() {
return latestImmutableStateRef.get();
}
@Override
+ @NonNull
public MerkleNodeState copyMutableState() {
final MerkleNodeState state = stateRef.get();
copyAndUpdateStateRefs(state);
@@ -129,7 +146,7 @@ public MerkleNodeState copyMutableState() {
* Copies the provided to and updates both the latest immutable to and the mutable to reference.
* @param stateToCopy the state to copy and update references for
*/
- private synchronized void copyAndUpdateStateRefs(MerkleNodeState stateToCopy) {
+ private synchronized void copyAndUpdateStateRefs(final @NonNull MerkleNodeState stateToCopy) {
final long copyStart = System.nanoTime();
final MerkleNodeState newMutableState = stateToCopy.copy();
// Increment the reference count because this reference becomes the new value
@@ -143,7 +160,7 @@ private synchronized void copyAndUpdateStateRefs(MerkleNodeState stateToCopy) {
}
stateToCopy.getRoot().reserve();
latestImmutableStateRef.set(stateToCopy);
- final var previousMutableState = stateRef.get();
+ final MerkleNodeState previousMutableState = stateRef.get();
if (previousMutableState != null && !previousMutableState.isDestroyed()) {
previousMutableState.release();
}
@@ -158,9 +175,12 @@ private synchronized void copyAndUpdateStateRefs(MerkleNodeState stateToCopy) {
*/
@Override
public void createSnapshot(final @NonNull Path targetPath) {
+ if( snapshotSource.get() == null) {
+ throw new IllegalStateException("Snapshot source is not set");
+ }
requireNonNull(time);
requireNonNull(snapshotMetrics);
- VirtualMapState> state = (VirtualMapState>) snapshotSource.get().getState();
+ final VirtualMapState> state = (VirtualMapState>) snapshotSource.get().getState();
state.throwIfMutable();
state.throwIfDestroyed();
final long startTime = time.currentTimeMillis();
@@ -172,8 +192,9 @@ public void createSnapshot(final @NonNull Path targetPath) {
/**
* {@inheritDoc}
*/
+ @NonNull
@Override
- public MerkleNodeState loadSnapshot(@NonNull Path targetPath) {
+ public MerkleNodeState loadSnapshot(@NonNull final Path targetPath) {
final MerkleNode root;
try {
root = MerkleTreeSnapshotReader.readStateFileData(targetPath).stateRoot();
@@ -185,7 +206,7 @@ public MerkleNodeState loadSnapshot(@NonNull Path targetPath) {
"Root should be a VirtualMap, but it is " + root.getClass().getSimpleName() + " instead");
}
- final var mutableCopy = readVirtualMap.copy();
+ final VirtualMap mutableCopy = readVirtualMap.copy();
mutableCopy.registerMetrics(metrics);
readVirtualMap.release();
readVirtualMap = mutableCopy;
From a632b74624e25424764cabcbbb9586a0604cbdcc Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Tue, 4 Nov 2025 21:07:32 -0500
Subject: [PATCH 12/23] Addressed review comments: - got rid of
`MerkleNodeStateAware` - changed a signature of `createSnapshot` to take a
state instance as an argument - added `getRound` method to `MerkleNodeState`
Signed-off-by: Ivan Malygin
---
.../app/state/merkle/SerializationTest.java | 15 +++---
.../node/app/fixtures/state/FakeState.java | 5 ++
.../BlockStreamRecoveryWorkflow.java | 10 ++--
.../otter/fixtures/app/OtterAppState.java | 2 +-
.../swirlds/demo/stats/StatsDemoState.java | 2 +-
.../ConsistencyTestingToolState.java | 2 +-
.../swirlds/demo/iss/ISSTestingToolState.java | 2 +-
.../migration/MigrationTestingToolState.java | 2 +-
.../platform/PlatformTestingToolState.java | 2 +-
.../com/swirlds/platform/SwirldsPlatform.java | 12 ++---
.../platform/builder/PlatformBuilder.java | 4 +-
.../builder/PlatformBuildingBlocks.java | 3 +-
.../cli/GenesisPlatformStateCommand.java | 13 +++--
.../DefaultTransactionHandler.java | 4 +-
.../platform/gossip/SyncGossipModular.java | 4 +-
.../protocol/ReconnectStateSyncProtocol.java | 4 +-
.../reconnect/ReconnectController.java | 4 +-
.../reconnect/ReconnectStatePeerProtocol.java | 4 +-
.../recovery/EventRecoveryWorkflow.java | 17 ++++---
.../state/editor/StateEditorSave.java | 11 +++--
.../platform/state/signed/SignedState.java | 3 +-
.../snapshot/DefaultStateSnapshotManager.java | 13 +++--
.../state/snapshot/SignedStateFileWriter.java | 47 +++++++++++--------
.../SignedStateFileReadWriteTest.java | 9 ++--
.../platform/StateFileManagerTests.java | 4 +-
.../TransactionHandlerTester.java | 6 +--
.../reconnect/ReconnectControllerTest.java | 2 +-
.../state/StateLifecycleManagerTests.java | 4 +-
.../state/signed/StartupStateUtilsTests.java | 6 +--
.../com/swirlds/state/MerkleNodeState.java | 7 +++
.../swirlds/state/MerkleNodeStateAware.java | 16 -------
.../swirlds/state/StateLifecycleManager.java | 19 ++------
.../merkle/StateLifecycleManagerImpl.java | 41 +++-------------
.../swirlds/state/merkle/VirtualMapState.java | 7 ---
.../fixtures/merkle/TestVirtualMapState.java | 2 +-
35 files changed, 140 insertions(+), 168 deletions(-)
delete mode 100644 platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeStateAware.java
diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java
index 4b4966c80b5e..5d40028214a4 100644
--- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java
+++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java
@@ -162,7 +162,7 @@ private void forceFlush(ReadableKVState, ?> state) {
@ValueSource(booleans = {true, false})
void simpleReadAndWrite(boolean forceFlush) throws IOException, ConstructableRegistryException {
final Schema schemaV1 = createV1Schema();
- final StateLifecycleManager> stateLifecycleManager = createStateLifecycleManager(schemaV1);
+ final StateLifecycleManager stateLifecycleManager = createStateLifecycleManager(schemaV1);
final MerkleNodeState originalTree = stateLifecycleManager.getMutableState();
// When we serialize it to bytes and deserialize it back into a tree
@@ -201,8 +201,7 @@ void snapshot() throws IOException {
// prepare the tree and create a snapshot
stateLifecycleManager.getMutableState().release();
originalTree.computeHash();
- stateLifecycleManager.setSnapshotSource(() -> originalTree);
- stateLifecycleManager.createSnapshot(tempDir);
+ stateLifecycleManager.createSnapshot(originalTree, tempDir);
originalTree.release();
final MerkleNodeState state =
@@ -221,7 +220,7 @@ void snapshot() throws IOException {
@Test
void dualReadAndWrite() throws IOException, ConstructableRegistryException {
final Schema schemaV1 = createV1Schema();
- final StateLifecycleManager> stateLifecycleManager = createStateLifecycleManager(schemaV1);
+ final StateLifecycleManager stateLifecycleManager = createStateLifecycleManager(schemaV1);
final MerkleNodeState originalTree = stateLifecycleManager.getMutableState();
MerkleNodeState copy = stateLifecycleManager.copyMutableState(); // make a copy to make VM flushable
@@ -285,11 +284,11 @@ private void initServices(Schema schemaV1, MerkleNodeState load
loadedTree.getRoot().migrate(MINIMUM_SUPPORTED_VERSION);
}
- private StateLifecycleManager> createStateLifecycleManager(Schema schemaV1) {
+ private StateLifecycleManager createStateLifecycleManager(Schema schemaV1) {
final SignedState randomState =
new RandomSignedStateGenerator().setRound(1).build();
- final var originalTree = randomState.getState();
+ final MerkleNodeState originalTree = randomState.getState();
// the state is not hashed yet
final var originalTreeCopy = originalTree.copy();
originalTree.release();
@@ -306,8 +305,8 @@ private StateLifecycleManager> createStateLifecycleManager(Schema schemaV1) {
startupNetworks,
TEST_PLATFORM_STATE_FACADE);
- final StateLifecycleManager stateLifecycleManager =
- new StateLifecycleManagerImpl<>(new NoOpMetrics(), new FakeTime(), TestVirtualMapState::new);
+ final StateLifecycleManager stateLifecycleManager =
+ new StateLifecycleManagerImpl(new NoOpMetrics(), new FakeTime(), TestVirtualMapState::new);
stateLifecycleManager.initState(originalTreeCopy, true);
return stateLifecycleManager;
diff --git a/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeState.java b/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeState.java
index a8c81e8bae5c..48d91ee9ba41 100644
--- a/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeState.java
+++ b/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeState.java
@@ -302,4 +302,9 @@ public long queueElementPath(final int stateId, @NonNull final Bytes expectedVal
public void initializeState(@NonNull final StateMetadata, ?> md) {
// do nothing
}
+
+ @Override
+ public long getRound() {
+ return 0;
+ }
}
diff --git a/hedera-state-validator/src/main/java/com/hedera/statevalidation/blockstream/BlockStreamRecoveryWorkflow.java b/hedera-state-validator/src/main/java/com/hedera/statevalidation/blockstream/BlockStreamRecoveryWorkflow.java
index 5449b1a0ca21..b9e2cbd8b155 100644
--- a/hedera-state-validator/src/main/java/com/hedera/statevalidation/blockstream/BlockStreamRecoveryWorkflow.java
+++ b/hedera-state-validator/src/main/java/com/hedera/statevalidation/blockstream/BlockStreamRecoveryWorkflow.java
@@ -152,14 +152,18 @@ public void applyBlocks(
false,
DEFAULT_PLATFORM_STATE_FACADE);
- final StateLifecycleManager stateLifecycleManager = new StateLifecycleManagerImpl<>(
+ final StateLifecycleManager stateLifecycleManager = new StateLifecycleManagerImpl(
platformContext.getMetrics(),
platformContext.getTime(),
vm -> new HederaVirtualMapState(vm, platformContext.getMetrics(), platformContext.getTime()));
- stateLifecycleManager.setSnapshotSource(signedState);
try {
SignedStateFileWriter.writeSignedStateFilesToDirectory(
- platformContext, selfId, outputPath, DEFAULT_PLATFORM_STATE_FACADE, stateLifecycleManager);
+ platformContext,
+ selfId,
+ outputPath,
+ signedState,
+ DEFAULT_PLATFORM_STATE_FACADE,
+ stateLifecycleManager);
} catch (IOException e) {
throw new RuntimeException(e);
}
diff --git a/platform-sdk/consensus-otter-tests/src/testFixtures/java/org/hiero/otter/fixtures/app/OtterAppState.java b/platform-sdk/consensus-otter-tests/src/testFixtures/java/org/hiero/otter/fixtures/app/OtterAppState.java
index ef753bd91844..45481d0b1f40 100644
--- a/platform-sdk/consensus-otter-tests/src/testFixtures/java/org/hiero/otter/fixtures/app/OtterAppState.java
+++ b/platform-sdk/consensus-otter-tests/src/testFixtures/java/org/hiero/otter/fixtures/app/OtterAppState.java
@@ -86,7 +86,7 @@ protected OtterAppState copyingConstructor() {
* {@inheritDoc}
*/
@Override
- protected long getRound() {
+ public long getRound() {
return DEFAULT_PLATFORM_STATE_FACADE.roundOf(this);
}
diff --git a/platform-sdk/platform-apps/demos/StatsDemo/src/main/java/com/swirlds/demo/stats/StatsDemoState.java b/platform-sdk/platform-apps/demos/StatsDemo/src/main/java/com/swirlds/demo/stats/StatsDemoState.java
index 7700db34ad50..db820fa0c581 100644
--- a/platform-sdk/platform-apps/demos/StatsDemo/src/main/java/com/swirlds/demo/stats/StatsDemoState.java
+++ b/platform-sdk/platform-apps/demos/StatsDemo/src/main/java/com/swirlds/demo/stats/StatsDemoState.java
@@ -66,7 +66,7 @@ private StatsDemoState(final StatsDemoState sourceState) {
* {@inheritDoc}
*/
@Override
- protected long getRound() {
+ public long getRound() {
return DEFAULT_PLATFORM_STATE_FACADE.roundOf(this);
}
diff --git a/platform-sdk/platform-apps/tests/ConsistencyTestingTool/src/main/java/com/swirlds/demo/consistency/ConsistencyTestingToolState.java b/platform-sdk/platform-apps/tests/ConsistencyTestingTool/src/main/java/com/swirlds/demo/consistency/ConsistencyTestingToolState.java
index b41c9e329adc..31d37678bef7 100644
--- a/platform-sdk/platform-apps/tests/ConsistencyTestingTool/src/main/java/com/swirlds/demo/consistency/ConsistencyTestingToolState.java
+++ b/platform-sdk/platform-apps/tests/ConsistencyTestingTool/src/main/java/com/swirlds/demo/consistency/ConsistencyTestingToolState.java
@@ -154,7 +154,7 @@ void initState(Path logFilePath) {
* {@inheritDoc}
*/
@Override
- protected long getRound() {
+ public long getRound() {
return DEFAULT_PLATFORM_STATE_FACADE.roundOf(this);
}
diff --git a/platform-sdk/platform-apps/tests/ISSTestingTool/src/main/java/com/swirlds/demo/iss/ISSTestingToolState.java b/platform-sdk/platform-apps/tests/ISSTestingTool/src/main/java/com/swirlds/demo/iss/ISSTestingToolState.java
index 732781ed0a88..0a41e74110c5 100644
--- a/platform-sdk/platform-apps/tests/ISSTestingTool/src/main/java/com/swirlds/demo/iss/ISSTestingToolState.java
+++ b/platform-sdk/platform-apps/tests/ISSTestingTool/src/main/java/com/swirlds/demo/iss/ISSTestingToolState.java
@@ -192,7 +192,7 @@ List getPlannedLogErrorList() {
* {@inheritDoc}
*/
@Override
- protected long getRound() {
+ public long getRound() {
return DEFAULT_PLATFORM_STATE_FACADE.roundOf(this);
}
}
diff --git a/platform-sdk/platform-apps/tests/MigrationTestingTool/src/main/java/com/swirlds/demo/migration/MigrationTestingToolState.java b/platform-sdk/platform-apps/tests/MigrationTestingTool/src/main/java/com/swirlds/demo/migration/MigrationTestingToolState.java
index b705d02e2b52..0bd0353025de 100644
--- a/platform-sdk/platform-apps/tests/MigrationTestingTool/src/main/java/com/swirlds/demo/migration/MigrationTestingToolState.java
+++ b/platform-sdk/platform-apps/tests/MigrationTestingTool/src/main/java/com/swirlds/demo/migration/MigrationTestingToolState.java
@@ -36,7 +36,7 @@ protected MigrationTestingToolState copyingConstructor() {
* {@inheritDoc}
*/
@Override
- protected long getRound() {
+ public long getRound() {
return DEFAULT_PLATFORM_STATE_FACADE.roundOf(this);
}
}
diff --git a/platform-sdk/platform-apps/tests/PlatformTestingTool/src/main/java/com/swirlds/demo/platform/PlatformTestingToolState.java b/platform-sdk/platform-apps/tests/PlatformTestingTool/src/main/java/com/swirlds/demo/platform/PlatformTestingToolState.java
index 4728e1763cd0..9fe131b1116d 100644
--- a/platform-sdk/platform-apps/tests/PlatformTestingTool/src/main/java/com/swirlds/demo/platform/PlatformTestingToolState.java
+++ b/platform-sdk/platform-apps/tests/PlatformTestingTool/src/main/java/com/swirlds/demo/platform/PlatformTestingToolState.java
@@ -147,7 +147,7 @@ protected PlatformTestingToolState(final PlatformTestingToolState sourceState) {
* {@inheritDoc}
*/
@Override
- protected long getRound() {
+ public long getRound() {
return DEFAULT_PLATFORM_STATE_FACADE.roundOf(this);
}
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
index 5cf05fdfd992..f9f78d7f5636 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java
@@ -57,6 +57,7 @@
import com.swirlds.platform.system.status.actions.StartedReplayingEventsAction;
import com.swirlds.platform.wiring.PlatformComponents;
import com.swirlds.platform.wiring.PlatformCoordinator;
+import com.swirlds.state.MerkleNodeState;
import com.swirlds.state.State;
import com.swirlds.state.StateLifecycleManager;
import edu.umd.cs.findbugs.annotations.NonNull;
@@ -138,11 +139,6 @@ public class SwirldsPlatform implements Platform {
*/
private final SavedStateController savedStateController;
- /**
- * Manages the lifecycle of the state.
- */
- private final StateLifecycleManager stateLifecycleManager;
-
/**
* Encapsulated wiring for the platform.
*/
@@ -191,7 +187,6 @@ public SwirldsPlatform(@NonNull final PlatformComponentBuilder builder) {
final LatestCompleteStateNexus latestCompleteStateNexus = new DefaultLatestCompleteStateNexus(platformContext);
savedStateController = new DefaultSavedStateController(platformContext);
- stateLifecycleManager = blocks.stateLifecycleManager();
final SignedStateMetrics signedStateMetrics = new SignedStateMetrics(platformContext.getMetrics());
final StateSignatureCollector stateSignatureCollector =
@@ -217,8 +212,9 @@ public SwirldsPlatform(@NonNull final PlatformComponentBuilder builder) {
initializeState(this, platformContext, initialState, blocks.consensusStateEventHandler(), platformStateFacade);
// This object makes a copy of the state. After this point, initialState becomes immutable.
- final StateLifecycleManager stateLifecycleManager = blocks.stateLifecycleManager();
- stateLifecycleManager.initState(initialState.getState(), true);
+ final StateLifecycleManager stateLifecycleManager = blocks.stateLifecycleManager();
+ final MerkleNodeState state = initialState.getState();
+ stateLifecycleManager.initState(state, true);
platformStateFacade.setCreationSoftwareVersionTo(stateLifecycleManager.getMutableState(), blocks.appVersion());
final EventWindowManager eventWindowManager = new DefaultEventWindowManager();
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuilder.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuilder.java
index 6a5e1c569fa5..55db64dca99e 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuilder.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuilder.java
@@ -37,7 +37,6 @@
import com.swirlds.platform.state.iss.IssScratchpad;
import com.swirlds.platform.state.service.PlatformStateFacade;
import com.swirlds.platform.state.signed.ReservedSignedState;
-import com.swirlds.platform.state.signed.SignedState;
import com.swirlds.platform.system.Platform;
import com.swirlds.platform.wiring.PlatformComponents;
import com.swirlds.platform.wiring.PlatformWiring;
@@ -435,7 +434,8 @@ public PlatformComponentBuilder buildComponentBuilder() {
final ApplicationCallbacks callbacks =
new ApplicationCallbacks(preconsensusEventConsumer, snapshotOverrideConsumer, staleEventConsumer);
- final StateLifecycleManager stateLifecycleManager = new StateLifecycleManagerImpl<>(
+ @SuppressWarnings("unchecked")
+ final StateLifecycleManager stateLifecycleManager = new StateLifecycleManagerImpl(
platformContext.getMetrics(), platformContext.getTime(), createStateFromVirtualMap);
if (model == null) {
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuildingBlocks.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuildingBlocks.java
index 5cf9520c7cee..27094e6f47cb 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuildingBlocks.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformBuildingBlocks.java
@@ -18,7 +18,6 @@
import com.swirlds.platform.state.iss.IssScratchpad;
import com.swirlds.platform.state.service.PlatformStateFacade;
import com.swirlds.platform.state.signed.ReservedSignedState;
-import com.swirlds.platform.state.signed.SignedState;
import com.swirlds.platform.system.status.StatusActionSubmitter;
import com.swirlds.platform.wiring.PlatformComponents;
import com.swirlds.state.MerkleNodeState;
@@ -111,7 +110,7 @@ public record PlatformBuildingBlocks(
@NonNull Scratchpad issScratchpad,
@NonNull NotificationEngine notificationEngine,
@NonNull AtomicReference statusActionSubmitterReference,
- @NonNull StateLifecycleManager stateLifecycleManager,
+ @NonNull StateLifecycleManager stateLifecycleManager,
@NonNull AtomicReference> getLatestCompleteStateReference,
boolean firstPlatform,
@NonNull ConsensusStateEventHandler consensusStateEventHandler,
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/GenesisPlatformStateCommand.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/GenesisPlatformStateCommand.java
index 192a2de7718e..6ba440b0d152 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/GenesisPlatformStateCommand.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/GenesisPlatformStateCommand.java
@@ -16,7 +16,6 @@
import com.swirlds.platform.state.PlatformStateAccessor;
import com.swirlds.platform.state.service.PlatformStateFacade;
import com.swirlds.platform.state.signed.ReservedSignedState;
-import com.swirlds.platform.state.signed.SignedState;
import com.swirlds.platform.state.snapshot.DeserializedSignedState;
import com.swirlds.platform.state.snapshot.SignedStateFileReader;
import com.swirlds.platform.util.BootstrapUtils;
@@ -63,8 +62,8 @@ public Integer call() throws IOException, ExecutionException, InterruptedExcepti
BootstrapUtils.setupConstructableRegistry();
final PlatformContext platformContext = PlatformContext.create(configuration);
- final StateLifecycleManager stateLifecycleManager = new StateLifecycleManagerImpl<>(
- platformContext.getMetrics(), platformContext.getTime(), (virtualMap) -> {
+ final StateLifecycleManager stateLifecycleManager =
+ new StateLifecycleManagerImpl(platformContext.getMetrics(), platformContext.getTime(), (virtualMap) -> {
// FUTURE WORK: https://github.com/hiero-ledger/hiero-consensus-node/issues/19003
throw new UnsupportedOperationException();
});
@@ -99,9 +98,13 @@ public Integer call() throws IOException, ExecutionException, InterruptedExcepti
.digestTreeAsync(reservedSignedState.get().getState().getRoot())
.get();
System.out.printf("Writing modified state to %s %n", outputDir.toAbsolutePath());
- stateLifecycleManager.setSnapshotSource(reservedSignedState.get());
writeSignedStateFilesToDirectory(
- platformContext, NO_NODE_ID, outputDir, stateFacade, stateLifecycleManager);
+ platformContext,
+ NO_NODE_ID,
+ outputDir,
+ reservedSignedState.get(),
+ stateFacade,
+ stateLifecycleManager);
}
return 0;
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/DefaultTransactionHandler.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/DefaultTransactionHandler.java
index 39a8821936f6..279360daa267 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/DefaultTransactionHandler.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/DefaultTransactionHandler.java
@@ -62,7 +62,7 @@ public class DefaultTransactionHandler implements TransactionHandler {
/**
* The class responsible for all interactions with the swirld state
*/
- private final StateLifecycleManager> stateLifecycleManager;
+ private final StateLifecycleManager stateLifecycleManager;
private final RoundHandlingMetrics handlerMetrics;
@@ -137,7 +137,7 @@ public class DefaultTransactionHandler implements TransactionHandler {
*/
public DefaultTransactionHandler(
@NonNull final PlatformContext platformContext,
- @NonNull final StateLifecycleManager> stateLifecycleManager,
+ @NonNull final StateLifecycleManager stateLifecycleManager,
@NonNull final StatusActionSubmitter statusActionSubmitter,
@NonNull final SemanticVersion softwareVersion,
@NonNull final PlatformStateFacade platformStateFacade,
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gossip/SyncGossipModular.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gossip/SyncGossipModular.java
index e097941e0fae..6c2a408f1d7f 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gossip/SyncGossipModular.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gossip/SyncGossipModular.java
@@ -72,7 +72,7 @@ public class SyncGossipModular implements Gossip {
private final AbstractSyncProtocol> syncProtocol;
private final FallenBehindMonitor fallenBehindMonitor;
private final AbstractShadowgraphSynchronizer synchronizer;
- private final StateLifecycleManager> stateLifecycleManager;
+ private final StateLifecycleManager stateLifecycleManager;
private final Function createStateFromVirtualMap;
// this is not a nice dependency, should be removed as well as the sharedState
@@ -103,7 +103,7 @@ public SyncGossipModular(
@NonNull final Roster roster,
@NonNull final NodeId selfId,
@NonNull final SemanticVersion appVersion,
- @NonNull final StateLifecycleManager> stateLifecycleManager,
+ @NonNull final StateLifecycleManager stateLifecycleManager,
@NonNull final Supplier latestCompleteState,
@NonNull final IntakeEventCounter intakeEventCounter,
@NonNull final PlatformStateFacade platformStateFacade,
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/network/protocol/ReconnectStateSyncProtocol.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/network/protocol/ReconnectStateSyncProtocol.java
index 2e3c5845ac90..1e26ab142232 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/network/protocol/ReconnectStateSyncProtocol.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/network/protocol/ReconnectStateSyncProtocol.java
@@ -39,7 +39,7 @@ public class ReconnectStateSyncProtocol implements Protocol {
private final PlatformContext platformContext;
private final AtomicReference platformStatus = new AtomicReference<>(PlatformStatus.STARTING_UP);
private final ReservedSignedStateResultPromise reservedSignedStateResultPromise;
- private final StateLifecycleManager> stateLifecycleManager;
+ private final StateLifecycleManager stateLifecycleManager;
private final Function createStateFromVirtualMap;
public ReconnectStateSyncProtocol(
@@ -52,7 +52,7 @@ public ReconnectStateSyncProtocol(
@NonNull final FallenBehindMonitor fallenBehindManager,
@NonNull final PlatformStateFacade platformStateFacade,
@NonNull final ReservedSignedStateResultPromise reservedSignedStateResultPromise,
- @NonNull final StateLifecycleManager> stateLifecycleManager,
+ @NonNull final StateLifecycleManager stateLifecycleManager,
@NonNull final Function createStateFromVirtualMap) {
this.platformContext = Objects.requireNonNull(platformContext);
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectController.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectController.java
index f68115e7dc1c..a72262f9e143 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectController.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectController.java
@@ -72,7 +72,7 @@ public class ReconnectController implements Runnable {
private final Platform platform;
private final PlatformContext platformContext;
private final PlatformCoordinator platformCoordinator;
- private final StateLifecycleManager stateLifecycleManager;
+ private final StateLifecycleManager stateLifecycleManager;
private final SavedStateController savedStateController;
private final ConsensusStateEventHandler consensusStateEventHandler;
private final ReservedSignedStateResultPromise peerReservedSignedStateResultPromise;
@@ -90,7 +90,7 @@ public ReconnectController(
@NonNull final Platform platform,
@NonNull final PlatformContext platformContext,
@NonNull final PlatformCoordinator platformCoordinator,
- @NonNull final StateLifecycleManager stateLifecycleManager,
+ @NonNull final StateLifecycleManager stateLifecycleManager,
@NonNull final SavedStateController savedStateController,
@NonNull final ConsensusStateEventHandler consensusStateEventHandler,
@NonNull final ReservedSignedStateResultPromise peerReservedSignedStateResultPromise,
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectStatePeerProtocol.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectStatePeerProtocol.java
index 07784d68e60f..2e4e282db531 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectStatePeerProtocol.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectStatePeerProtocol.java
@@ -82,7 +82,7 @@ public class ReconnectStatePeerProtocol implements PeerProtocol {
private final Time time;
private final PlatformContext platformContext;
private final ReservedSignedStateResultPromise reservedSignedStateResultPromise;
- private final StateLifecycleManager> stateLifecycleManager;
+ private final StateLifecycleManager stateLifecycleManager;
private final Function createStateFromVirtualMap;
/**
@@ -112,7 +112,7 @@ public ReconnectStatePeerProtocol(
@NonNull final Time time,
@NonNull final PlatformStateFacade platformStateFacade,
@NonNull final ReservedSignedStateResultPromise reservedSignedStateResultPromise,
- @NonNull final StateLifecycleManager> stateLifecycleManager,
+ @NonNull final StateLifecycleManager stateLifecycleManager,
@NonNull final Function createStateFromVirtualMap) {
this.platformContext = Objects.requireNonNull(platformContext);
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/recovery/EventRecoveryWorkflow.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/recovery/EventRecoveryWorkflow.java
index 05527497d16b..877371f91acf 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/recovery/EventRecoveryWorkflow.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/recovery/EventRecoveryWorkflow.java
@@ -50,6 +50,7 @@
import com.swirlds.state.State;
import com.swirlds.state.StateLifecycleManager;
import com.swirlds.state.merkle.StateLifecycleManagerImpl;
+import com.swirlds.virtualmap.VirtualMap;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.IOException;
import java.nio.file.Files;
@@ -59,6 +60,7 @@
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
+import java.util.function.Function;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hiero.base.CompareTo;
@@ -157,12 +159,10 @@ public static void recoverState(
.apply(v),
platformStateFacade,
platformContext);
- final StateLifecycleManager stateLifecycleManager = new StateLifecycleManagerImpl<>(
- platformContext.getMetrics(),
- platformContext.getTime(),
- hederaApp.stateRootFromVirtualMap(platformContext.getMetrics(), platformContext.getTime()));
+ final StateLifecycleManager stateLifecycleManager = new StateLifecycleManagerImpl(
+ platformContext.getMetrics(), platformContext.getTime(), (Function)
+ hederaApp.stateRootFromVirtualMap(platformContext.getMetrics(), platformContext.getTime()));
try (final ReservedSignedState initialState = deserializedSignedState.reservedSignedState()) {
- stateLifecycleManager.setSnapshotSource(initialState.get());
HederaUtils.updateStateHash(hederaApp, deserializedSignedState);
logger.info(
@@ -199,7 +199,12 @@ public static void recoverState(
recoveredState.state().get().getState().copy();
SignedStateFileWriter.writeSignedStateFilesToDirectory(
- platformContext, selfId, resultingStateDirectory, platformStateFacade, stateLifecycleManager);
+ platformContext,
+ selfId,
+ resultingStateDirectory,
+ recoveredState.state().get(),
+ platformStateFacade,
+ stateLifecycleManager);
final StateConfig stateConfig = platformContext.getConfiguration().getConfigData(StateConfig.class);
updateEmergencyRecoveryFile(
stateConfig, resultingStateDirectory, initialState.get().getConsensusTimestamp());
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/editor/StateEditorSave.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/editor/StateEditorSave.java
index efc3df669f01..8d1eeb7ca92b 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/editor/StateEditorSave.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/editor/StateEditorSave.java
@@ -14,7 +14,6 @@
import com.swirlds.logging.legacy.LogMarker;
import com.swirlds.platform.config.DefaultConfiguration;
import com.swirlds.platform.state.signed.ReservedSignedState;
-import com.swirlds.platform.state.signed.SignedState;
import com.swirlds.state.StateLifecycleManager;
import com.swirlds.state.merkle.StateLifecycleManagerImpl;
import java.io.IOException;
@@ -49,7 +48,7 @@ public void run() {
final PlatformContext platformContext = PlatformContext.create(configuration);
logger.info(LogMarker.CLI.getMarker(), "Hashing state");
- final StateLifecycleManager stateLifecycleManager = new StateLifecycleManagerImpl<>(
+ final StateLifecycleManager stateLifecycleManager = new StateLifecycleManagerImpl(
platformContext.getMetrics(), platformContext.getTime(), (virtualMap) -> {
// FUTURE WORK: https://github.com/hiero-ledger/hiero-consensus-node/issues/19003
throw new UnsupportedOperationException();
@@ -69,9 +68,13 @@ public void run() {
}
try (final ReservedSignedState signedState = getStateEditor().getSignedStateCopy()) {
- stateLifecycleManager.setSnapshotSource(signedState.get());
writeSignedStateFilesToDirectory(
- platformContext, NO_NODE_ID, directory, DEFAULT_PLATFORM_STATE_FACADE, stateLifecycleManager);
+ platformContext,
+ NO_NODE_ID,
+ directory,
+ signedState.get(),
+ DEFAULT_PLATFORM_STATE_FACADE,
+ stateLifecycleManager);
}
} catch (final IOException e) {
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/signed/SignedState.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/signed/SignedState.java
index 40b7dd9fb871..0a8e3bca82e8 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/signed/SignedState.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/signed/SignedState.java
@@ -23,7 +23,6 @@
import com.swirlds.platform.state.signed.SignedStateHistory.SignedStateAction;
import com.swirlds.platform.state.snapshot.StateToDiskReason;
import com.swirlds.state.MerkleNodeState;
-import com.swirlds.state.MerkleNodeStateAware;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.security.cert.X509Certificate;
@@ -63,7 +62,7 @@
* rejoining after a long absence.
*
*/
-public class SignedState implements MerkleNodeStateAware {
+public class SignedState {
private static final Logger logger = LogManager.getLogger(SignedState.class);
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/DefaultStateSnapshotManager.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/DefaultStateSnapshotManager.java
index aef40b3a0c77..89cf104bd71a 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/DefaultStateSnapshotManager.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/DefaultStateSnapshotManager.java
@@ -84,7 +84,7 @@ public class DefaultStateSnapshotManager implements StateSnapshotManager {
/**
* Provides access to the state
*/
- private final StateLifecycleManager stateLifecycleManager;
+ private final StateLifecycleManager stateLifecycleManager;
/**
* Creates a new instance.
@@ -102,7 +102,7 @@ public DefaultStateSnapshotManager(
@NonNull final NodeId selfId,
@NonNull final String swirldName,
@NonNull final PlatformStateFacade platformStateFacade,
- @NonNull final StateLifecycleManager stateLifecycleManager) {
+ @NonNull final StateLifecycleManager stateLifecycleManager) {
this.platformContext = Objects.requireNonNull(platformContext);
this.time = platformContext.getTime();
@@ -180,9 +180,14 @@ private static StateToDiskReason getReason(@NonNull final SignedState state) {
private boolean saveStateTask(@NonNull final SignedState state, @NonNull final Path directory) {
try {
- stateLifecycleManager.setSnapshotSource(state);
SignedStateFileWriter.writeSignedStateToDisk(
- platformContext, selfId, directory, getReason(state), platformStateFacade, stateLifecycleManager);
+ platformContext,
+ selfId,
+ directory,
+ getReason(state),
+ state,
+ platformStateFacade,
+ stateLifecycleManager);
return true;
} catch (final Throwable e) {
logger.error(
diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/SignedStateFileWriter.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/SignedStateFileWriter.java
index bcfe8e8876fd..965be992161a 100644
--- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/SignedStateFileWriter.java
+++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/SignedStateFileWriter.java
@@ -138,20 +138,22 @@ public static void writeSignedStateFilesToDirectory(
@Nullable final PlatformContext platformContext,
@Nullable final NodeId selfId,
@NonNull final Path directory,
+ @NonNull final SignedState signedState,
@NonNull final PlatformStateFacade platformStateFacade,
- @NonNull final StateLifecycleManager stateLifecycleManager)
+ @NonNull final StateLifecycleManager stateLifecycleManager)
throws IOException {
requireNonNull(platformContext);
requireNonNull(directory);
- requireNonNull(stateLifecycleManager.getSnapshotSource());
-
- SignedState snapshotSource = stateLifecycleManager.getSnapshotSource();
- stateLifecycleManager.createSnapshot(directory);
- writeSignatureSetFile(directory, snapshotSource);
- writeHashInfoFile(platformContext, directory, snapshotSource.getState(), platformStateFacade);
- writeMetadataFile(selfId, directory, snapshotSource, platformStateFacade);
- writeEmergencyRecoveryFile(directory, snapshotSource);
- final Roster currentRoster = snapshotSource.getRoster();
+ requireNonNull(signedState);
+ requireNonNull(platformStateFacade);
+ requireNonNull(stateLifecycleManager);
+
+ stateLifecycleManager.createSnapshot(signedState.getState(), directory);
+ writeSignatureSetFile(directory, signedState);
+ writeHashInfoFile(platformContext, directory, signedState.getState(), platformStateFacade);
+ writeMetadataFile(selfId, directory, signedState, platformStateFacade);
+ writeEmergencyRecoveryFile(directory, signedState);
+ final Roster currentRoster = signedState.getRoster();
writeRosterFile(directory, currentRoster);
writeSettingsUsed(directory, platformContext.getConfiguration());
@@ -160,8 +162,8 @@ public static void writeSignedStateFilesToDirectory(
platformContext,
selfId,
directory,
- platformStateFacade.ancientThresholdOf(snapshotSource.getState()),
- snapshotSource.getRound());
+ platformStateFacade.ancientThresholdOf(signedState.getState()),
+ signedState.getRound());
}
}
@@ -196,33 +198,38 @@ public static void writeSignedStateToDisk(
@Nullable final NodeId selfId,
@NonNull final Path savedStateDirectory,
@Nullable final StateToDiskReason stateToDiskReason,
+ @NonNull final SignedState signedState,
@NonNull final PlatformStateFacade platformStateFacade,
- @NonNull final StateLifecycleManager stateLifecycleManager)
+ @NonNull final StateLifecycleManager stateLifecycleManager)
throws IOException {
+ requireNonNull(signedState);
requireNonNull(platformContext);
requireNonNull(savedStateDirectory);
requireNonNull(stateLifecycleManager);
- final SignedState snapshotSource = stateLifecycleManager.getSnapshotSource();
- requireNonNull(snapshotSource);
try {
logger.info(
STATE_TO_DISK.getMarker(),
"Started writing round {} state to disk. Reason: {}, directory: {}",
- snapshotSource.getRound(),
+ signedState.getRound(),
stateToDiskReason == null ? "UNKNOWN" : stateToDiskReason,
savedStateDirectory);
executeAndRename(
savedStateDirectory,
directory -> writeSignedStateFilesToDirectory(
- platformContext, selfId, directory, platformStateFacade, stateLifecycleManager),
+ platformContext,
+ selfId,
+ directory,
+ signedState,
+ platformStateFacade,
+ stateLifecycleManager),
platformContext.getConfiguration());
logger.info(STATE_TO_DISK.getMarker(), () -> new StateSavedToDiskPayload(
- snapshotSource.getRound(),
- snapshotSource.isFreezeState(),
+ signedState.getRound(),
+ signedState.isFreezeState(),
stateToDiskReason == null ? "UNKNOWN" : stateToDiskReason.toString(),
savedStateDirectory)
.toString());
@@ -230,7 +237,7 @@ public static void writeSignedStateToDisk(
logger.error(
EXCEPTION.getMarker(),
"Exception when writing the signed state for round {} to disk:",
- snapshotSource.getRound(),
+ signedState.getRound(),
e);
throw e;
}
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/SignedStateFileReadWriteTest.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/SignedStateFileReadWriteTest.java
index 8a63496d9e2d..45c9d600e410 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/SignedStateFileReadWriteTest.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/SignedStateFileReadWriteTest.java
@@ -62,7 +62,7 @@ class SignedStateFileReadWriteTest {
private static SemanticVersion platformVersion;
private static PlatformStateFacade stateFacade;
- private StateLifecycleManager stateLifecycleManager;
+ private StateLifecycleManager stateLifecycleManager;
@BeforeAll
static void beforeAll() throws ConstructableRegistryException {
@@ -82,7 +82,7 @@ static void beforeAll() throws ConstructableRegistryException {
void beforeEach() throws IOException {
testDirectory = LegacyTemporaryFileBuilder.buildTemporaryFile("SignedStateFileReadWriteTest", CONFIGURATION);
stateLifecycleManager =
- new StateLifecycleManagerImpl<>(new NoOpMetrics(), new FakeTime(), TestVirtualMapState::new);
+ new StateLifecycleManagerImpl(new NoOpMetrics(), new FakeTime(), TestVirtualMapState::new);
LegacyTemporaryFileBuilder.overrideTemporaryFileLocation(testDirectory.resolve("tmp"));
}
@@ -134,8 +134,7 @@ void writeThenReadStateFileTest() throws IOException {
MerkleNodeState state = signedState.getState();
state.copy().release();
hashState(signedState);
- stateLifecycleManager.setSnapshotSource(signedState);
- stateLifecycleManager.createSnapshot(testDirectory);
+ stateLifecycleManager.createSnapshot(signedState.getState(), testDirectory);
writeSignatureSetFile(testDirectory, signedState);
assertTrue(exists(stateFile), "signed state file should be present");
@@ -182,13 +181,13 @@ void writeSavedStateToDiskTest() throws IOException {
stateLifecycleManager.getMutableState().release();
hashState(signedState);
- stateLifecycleManager.setSnapshotSource(signedState);
writeSignedStateToDisk(
platformContext,
NodeId.of(0),
directory,
StateToDiskReason.PERIODIC_SNAPSHOT,
+ signedState,
stateFacade,
stateLifecycleManager);
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/StateFileManagerTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/StateFileManagerTests.java
index 693e0963b6db..eb12933be39b 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/StateFileManagerTests.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/StateFileManagerTests.java
@@ -77,7 +77,7 @@ class StateFileManagerTests {
private SignedStateFilePath signedStateFilePath;
Path testDirectory;
- private StateLifecycleManager stateLifecycleManager;
+ private StateLifecycleManager stateLifecycleManager;
@BeforeAll
static void beforeAll() throws ConstructableRegistryException {
@@ -101,7 +101,7 @@ void beforeEach() throws IOException {
signedStateFilePath =
new SignedStateFilePath(context.getConfiguration().getConfigData(StateCommonConfig.class));
stateLifecycleManager =
- new StateLifecycleManagerImpl<>(context.getMetrics(), context.getTime(), TestVirtualMapState::new);
+ new StateLifecycleManagerImpl(context.getMetrics(), context.getTime(), TestVirtualMapState::new);
}
@AfterEach
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/TransactionHandlerTester.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/TransactionHandlerTester.java
index 701fc3684259..34465087a29a 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/TransactionHandlerTester.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/TransactionHandlerTester.java
@@ -33,7 +33,7 @@
*/
public class TransactionHandlerTester {
private final PlatformStateModifier platformState;
- private final StateLifecycleManager> stateLifecycleManager;
+ private final StateLifecycleManager stateLifecycleManager;
private final DefaultTransactionHandler defaultTransactionHandler;
private final List submittedActions = new ArrayList<>();
private final List handledRounds = new ArrayList<>();
@@ -66,7 +66,7 @@ public TransactionHandlerTester() {
.when(consensusStateEventHandler)
.onHandleConsensusRound(any(), same(consensusState), any());
final StatusActionSubmitter statusActionSubmitter = submittedActions::add;
- stateLifecycleManager = new StateLifecycleManagerImpl<>(
+ stateLifecycleManager = new StateLifecycleManagerImpl(
platformContext.getMetrics(), platformContext.getTime(), vm -> consensusState);
stateLifecycleManager.initState(consensusState, true);
defaultTransactionHandler = new DefaultTransactionHandler(
@@ -110,7 +110,7 @@ public List getHandledRounds() {
/**
* @return the {@link StateLifecycleManager} used by this tester
*/
- public StateLifecycleManager> getStateLifecycleManager() {
+ public StateLifecycleManager getStateLifecycleManager() {
return stateLifecycleManager;
}
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectControllerTest.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectControllerTest.java
index 7b7944e30684..ab7b976449b9 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectControllerTest.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/reconnect/ReconnectControllerTest.java
@@ -84,7 +84,7 @@ class ReconnectControllerTest {
private MerkleCryptography merkleCryptography;
private Platform platform;
private PlatformCoordinator platformCoordinator;
- private StateLifecycleManager stateLifecycleManager;
+ private StateLifecycleManager stateLifecycleManager;
private SavedStateController savedStateController;
private ConsensusStateEventHandler consensusStateEventHandler;
private ReservedSignedStateResultPromise peerReservedSignedStateResultPromise;
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateLifecycleManagerTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateLifecycleManagerTests.java
index 4b3d348ec65f..17a3e44842b6 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateLifecycleManagerTests.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateLifecycleManagerTests.java
@@ -30,7 +30,7 @@
class StateLifecycleManagerTests {
- private StateLifecycleManager stateLifecycleManager;
+ private StateLifecycleManager stateLifecycleManager;
private MerkleNodeState initialState;
@BeforeEach
@@ -43,7 +43,7 @@ void setup() {
final PlatformContext platformContext =
TestPlatformContextBuilder.create().build();
- stateLifecycleManager = new StateLifecycleManagerImpl<>(
+ stateLifecycleManager = new StateLifecycleManagerImpl(
platformContext.getMetrics(), platformContext.getTime(), TestVirtualMapState::new);
stateLifecycleManager.initState(initialState, true);
}
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/signed/StartupStateUtilsTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/signed/StartupStateUtilsTests.java
index 7632767bb0c6..f6b7355512b5 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/signed/StartupStateUtilsTests.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/signed/StartupStateUtilsTests.java
@@ -74,7 +74,7 @@ public class StartupStateUtilsTests {
private final String swirldName = "swirldName";
private SemanticVersion currentSoftwareVersion;
private PlatformStateFacade platformStateFacade;
- private StateLifecycleManager stateLifecycleManager;
+ private StateLifecycleManager stateLifecycleManager;
@BeforeEach
void beforeEach() throws IOException {
@@ -131,7 +131,7 @@ private SignedState writeState(
new RandomSignedStateGenerator(random).setRound(round).build();
stateLifecycleManager =
- new StateLifecycleManagerImpl<>(new NoOpMetrics(), new FakeTime(), TestVirtualMapState::new);
+ new StateLifecycleManagerImpl(new NoOpMetrics(), new FakeTime(), TestVirtualMapState::new);
stateLifecycleManager.initState(signedState.getState(), true);
stateLifecycleManager.getMutableState().release();
// FUTURE WORK: https://github.com/hiero-ledger/hiero-consensus-node/issues/19905
@@ -140,12 +140,12 @@ private SignedState writeState(
final Path savedStateDirectory =
signedStateFilePath.getSignedStateDirectory(mainClassName, selfId, swirldName, round);
- stateLifecycleManager.setSnapshotSource(signedState);
writeSignedStateToDisk(
platformContext,
selfId,
savedStateDirectory,
StateToDiskReason.PERIODIC_SNAPSHOT,
+ signedState,
platformStateFacade,
stateLifecycleManager);
diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeState.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeState.java
index ac7097fe2b3a..68b3a9abb793 100644
--- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeState.java
+++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeState.java
@@ -23,6 +23,13 @@ default MerkleNode getRoot() {
return (MerkleNode) this;
}
+ /**
+ * Retrieves the round number associated with this state.
+ *
+ * @return a round number of the state.
+ */
+ long getRound();
+
/**
* {@inheritDoc}
*/
diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeStateAware.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeStateAware.java
deleted file mode 100644
index 69806a468c37..000000000000
--- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeStateAware.java
+++ /dev/null
@@ -1,16 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-package com.swirlds.state;
-
-import edu.umd.cs.findbugs.annotations.NonNull;
-
-/**
- * Implementations of this interface can provide access to their {@link MerkleNodeState}.
- */
-public interface MerkleNodeStateAware {
- /**
- * An instance of {@link MerkleNodeState} associated with this object.
- * @return an instance of {@link MerkleNodeState} associated with this object.
- */
- @NonNull
- MerkleNodeState getState();
-}
diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
index c367cf802d3c..538a8a7d2ba6 100644
--- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
+++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
@@ -14,9 +14,9 @@
*
*
* An implementation of this class must be thread-safe.
- * @param A type of the snapshot source, which should implement {@link MerkleNodeStateAware}.
+ *
*/
-public interface StateLifecycleManager {
+public interface StateLifecycleManager {
/**
* Set the initial State. This method should only be on a startup or after a reconnect.
@@ -39,21 +39,12 @@ public interface StateLifecycleManager {
MerkleNodeState getLatestImmutableState();
/**
- * Sets the state to create a snapshot from.
- * @param source the state to create a snapshot from.
- */
- void setSnapshotSource(@NonNull T source);
-
- T getSnapshotSource();
-
- /**
- * Creates a snapshot for the state that was previously set with {@link #setSnapshotSource(MerkleNodeStateAware)}.
- * The state has to be hashed before calling this method. Once the snapshot is created, the manager resets the snapshot source
- * to null. Therefore, this method is not idempotent.
+ * Creates a snapshot for the state provided as a parameter. The state has to be hashed before calling this method.
*
+ * @param merkleNodeState The state to save.
* @param targetPath The path to save the snapshot.
*/
- void createSnapshot(@NonNull Path targetPath);
+ void createSnapshot(@NonNull MerkleNodeState merkleNodeState, @NonNull Path targetPath);
/**
* Loads a snapshot of a state.
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
index 62fbc0f3ebf5..48b1c2de4190 100644
--- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
+++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
@@ -10,13 +10,11 @@
import com.swirlds.common.merkle.utility.MerkleTreeSnapshotWriter;
import com.swirlds.metrics.api.Metrics;
import com.swirlds.state.MerkleNodeState;
-import com.swirlds.state.MerkleNodeStateAware;
import com.swirlds.state.State;
import com.swirlds.state.StateLifecycleManager;
import com.swirlds.virtualmap.VirtualMap;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
-
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
@@ -27,9 +25,8 @@
* This class is responsible for maintaining references to the mutable state and the latest immutable state.
* It also updates these references upon state signing.
*
- * @param A type of the snapshot source, which should implement {@link MerkleNodeStateAware}.
*/
-public class StateLifecycleManagerImpl implements StateLifecycleManager {
+public class StateLifecycleManagerImpl implements StateLifecycleManager {
/**
* Metrics for the state object
@@ -54,7 +51,7 @@ public class StateLifecycleManagerImpl implement
/**
* A factory object to create an instance of a class implementing {@link MerkleNodeState} from a {@link VirtualMap}
*/
- private final Function stateSupplier;
+ private final Function stateSupplier;
/**
* reference to the state that reflects all known consensus transactions
@@ -67,10 +64,8 @@ public class StateLifecycleManagerImpl implement
private final AtomicReference latestImmutableStateRef = new AtomicReference<>();
/**
- * The most recent immutable state. No value until the first fast copy is created.
+ * The source of the snapshot. This is set when a snapshot is initiated and cleared when the snapshot is complete.
*/
- private final AtomicReference snapshotSource = new AtomicReference<>();
-
/**
* Constructor.
*
@@ -80,8 +75,9 @@ public class StateLifecycleManagerImpl implement
public StateLifecycleManagerImpl(
@NonNull final Metrics metrics,
@NonNull final Time time,
- @NonNull final Function stateSupplier) {
+ @NonNull final Function stateSupplier) {
requireNonNull(metrics);
+ requireNonNull(time);
this.stateSupplier = stateSupplier;
this.metrics = metrics;
this.stateMetrics = new StateMetrics(metrics);
@@ -107,22 +103,6 @@ public void initState(@NonNull final MerkleNodeState state, final boolean onStar
copyAndUpdateStateRefs(state);
}
- @Override
- public void setSnapshotSource(@NonNull final T source) {
- requireNonNull(source);
- final MerkleNodeState state = source.getState();
- state.throwIfDestroyed("state must not be destroyed");
- state.throwIfMutable("state must be immutable");
- boolean result = snapshotSource.compareAndSet(null, source);
- assert result : "Snapshot source was already set";
- }
-
- @Override
- @Nullable
- public T getSnapshotSource() {
- return snapshotSource.get();
- }
-
@Override
public MerkleNodeState getMutableState() {
return stateRef.get();
@@ -174,19 +154,12 @@ private synchronized void copyAndUpdateStateRefs(final @NonNull MerkleNodeState
* {@inheritDoc}
*/
@Override
- public void createSnapshot(final @NonNull Path targetPath) {
- if( snapshotSource.get() == null) {
- throw new IllegalStateException("Snapshot source is not set");
- }
- requireNonNull(time);
- requireNonNull(snapshotMetrics);
- final VirtualMapState> state = (VirtualMapState>) snapshotSource.get().getState();
+ public void createSnapshot(final @NonNull MerkleNodeState state, final @NonNull Path targetPath) {
state.throwIfMutable();
state.throwIfDestroyed();
final long startTime = time.currentTimeMillis();
- MerkleTreeSnapshotWriter.createSnapshot(state.virtualMap, targetPath, state.getRound());
+ MerkleTreeSnapshotWriter.createSnapshot(state.getRoot(), targetPath, state.getRound());
snapshotMetrics.updateWriteStateToDiskTimeMetric(time.currentTimeMillis() - startTime);
- snapshotSource.set(null);
}
/**
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java
index 4e423cd76613..cb1902e24e20 100644
--- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java
+++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java
@@ -304,13 +304,6 @@ public void removeServiceState(@NonNull final String serviceName, final int stat
// Getters and setters
- /**
- * Retrieves the round number associated with this state.
- *
- * @return the round number as a long value
- */
- protected abstract long getRound();
-
public Map>> getServices() {
return services;
}
diff --git a/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/TestVirtualMapState.java b/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/TestVirtualMapState.java
index cb658a311161..1f1366ac6763 100644
--- a/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/TestVirtualMapState.java
+++ b/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/TestVirtualMapState.java
@@ -45,7 +45,7 @@ public static TestVirtualMapState createInstanceWithVirtualMapLabel(@NonNull fin
* {@inheritDoc}
*/
@Override
- protected long getRound() {
+ public long getRound() {
return 0; // genesis round
}
}
From 7ef4a1e6895ed4a2ca855a682733d87232ab3ef9 Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Wed, 5 Nov 2025 10:50:45 -0500
Subject: [PATCH 13/23] Addressed review comments: - updated javadoc - made
`copyMutableState` and `initState` synchronized
Signed-off-by: Ivan Malygin
---
.../state/merkle/StateLifecycleManagerImpl.java | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
index 48b1c2de4190..3f72efd0c7b1 100644
--- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
+++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
@@ -23,8 +23,7 @@
/**
* This class is responsible for maintaining references to the mutable state and the latest immutable state.
- * It also updates these references upon state signing.
- *
+ * It also updates these references upon state signing. This implementation is thread-safe.
*/
public class StateLifecycleManagerImpl implements StateLifecycleManager {
@@ -63,9 +62,6 @@ public class StateLifecycleManagerImpl implements StateLifecycleManager {
*/
private final AtomicReference latestImmutableStateRef = new AtomicReference<>();
- /**
- * The source of the snapshot. This is set when a snapshot is initiated and cleared when the snapshot is complete.
- */
/**
* Constructor.
*
@@ -90,7 +86,7 @@ public StateLifecycleManagerImpl(
*
* @param state the initial state
*/
- public void initState(@NonNull final MerkleNodeState state, final boolean onStartup) {
+ public synchronized void initState(@NonNull final MerkleNodeState state, final boolean onStartup) {
requireNonNull(state);
state.throwIfDestroyed("state must not be destroyed");
@@ -116,7 +112,7 @@ public MerkleNodeState getLatestImmutableState() {
@Override
@NonNull
- public MerkleNodeState copyMutableState() {
+ public synchronized MerkleNodeState copyMutableState() {
final MerkleNodeState state = stateRef.get();
copyAndUpdateStateRefs(state);
return stateRef.get();
@@ -126,7 +122,7 @@ public MerkleNodeState copyMutableState() {
* Copies the provided to and updates both the latest immutable to and the mutable to reference.
* @param stateToCopy the state to copy and update references for
*/
- private synchronized void copyAndUpdateStateRefs(final @NonNull MerkleNodeState stateToCopy) {
+ private void copyAndUpdateStateRefs(final @NonNull MerkleNodeState stateToCopy) {
final long copyStart = System.nanoTime();
final MerkleNodeState newMutableState = stateToCopy.copy();
// Increment the reference count because this reference becomes the new value
From f0df304b76f0257c639acb4ca21b1c3d47d75039 Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Wed, 5 Nov 2025 12:32:47 -0500
Subject: [PATCH 14/23] Added unit tests
Signed-off-by: Ivan Malygin
---
.../state/StateLifecycleManagerTests.java | 58 +++++++++++++++++++
1 file changed, 58 insertions(+)
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateLifecycleManagerTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateLifecycleManagerTests.java
index 17a3e44842b6..1c8a4075d203 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateLifecycleManagerTests.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateLifecycleManagerTests.java
@@ -3,11 +3,15 @@
import static org.hiero.base.utility.test.fixtures.RandomUtils.nextInt;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.hedera.hapi.node.base.SemanticVersion;
import com.hedera.hapi.node.state.roster.Roster;
+import com.swirlds.base.state.MutabilityException;
import com.swirlds.common.Reservable;
import com.swirlds.common.context.PlatformContext;
import com.swirlds.common.test.fixtures.Randotron;
@@ -23,7 +27,13 @@
import com.swirlds.state.StateLifecycleManager;
import com.swirlds.state.merkle.StateLifecycleManagerImpl;
import com.swirlds.state.test.fixtures.merkle.TestVirtualMapState;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.hiero.base.constructable.ConstructableRegistry;
+import org.hiero.base.constructable.ConstructableRegistryException;
import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -33,6 +43,16 @@ class StateLifecycleManagerTests {
private StateLifecycleManager stateLifecycleManager;
private MerkleNodeState initialState;
+ @BeforeAll
+ static void beforeAll() throws ConstructableRegistryException {
+ final var registry = ConstructableRegistry.getInstance();
+ registry.registerConstructables("org.hiero");
+ registry.registerConstructables("com.swirlds.platform");
+ registry.registerConstructables("com.swirlds.state");
+ registry.registerConstructables("com.swirlds.virtualmap");
+ registry.registerConstructables("com.swirlds.merkledb");
+ }
+
@BeforeEach
void setup() {
final SwirldsPlatform platform = mock(SwirldsPlatform.class);
@@ -53,6 +73,10 @@ void tearDown() {
if (!initialState.isDestroyed()) {
initialState.release();
}
+ final MerkleNodeState latestImmutable = stateLifecycleManager.getLatestImmutableState();
+ if (latestImmutable != null && latestImmutable != initialState && !latestImmutable.isDestroyed()) {
+ latestImmutable.release();
+ }
if (!stateLifecycleManager.getMutableState().isDestroyed()) {
stateLifecycleManager.getMutableState().release();
}
@@ -115,6 +139,40 @@ void initStateRefCount() {
consensusState2.release();
}
+ @Test
+ @DisplayName("copyMutableState() updates references and reservation counts")
+ void copyMutableStateReferenceCounts() {
+ final MerkleNodeState beforeMutable = stateLifecycleManager.getMutableState();
+ final MerkleNodeState beforeImmutable = stateLifecycleManager.getLatestImmutableState();
+
+ final MerkleNodeState afterMutable = stateLifecycleManager.copyMutableState();
+ final MerkleNodeState newLatestImmutable = stateLifecycleManager.getLatestImmutableState();
+
+ assertSame(beforeMutable, newLatestImmutable, "Previous mutable should become latest immutable");
+ assertNotSame(beforeMutable, afterMutable, "A new mutable state instance should be created");
+
+ assertEquals(1, afterMutable.getRoot().getReservationCount(), "Mutable state should have one reference");
+ assertEquals(1, newLatestImmutable.getRoot().getReservationCount(), "Latest immutable should have one ref");
+ assertEquals(-1, beforeImmutable.getRoot().getReservationCount(), "Old immutable should be released");
+ }
+
+ @Test
+ @DisplayName("initState() rejects second startup initialization")
+ void initStateRejectsSecondStartup() {
+ final PlatformStateFacade psf = new PlatformStateFacade();
+ final MerkleNodeState another = newState(psf);
+ assertThrows(IllegalStateException.class, () -> stateLifecycleManager.initState(another, true));
+ another.release();
+ }
+
+ @Test
+ @DisplayName("initState() rejects immutable input state")
+ void initStateRejectsImmutableInput() {
+ final MerkleNodeState immutable = stateLifecycleManager.getLatestImmutableState();
+ assertThrows(MutabilityException.class, () -> stateLifecycleManager.initState(immutable, false));
+ }
+
+
private static MerkleNodeState newState(PlatformStateFacade platformStateFacade) {
final String virtualMapLabel =
StateLifecycleManagerTests.class.getSimpleName() + "-" + java.util.UUID.randomUUID();
From cc47000e02c6443d62d97f5bcfdc1795984e83de Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Wed, 5 Nov 2025 13:13:16 -0500
Subject: [PATCH 15/23] Spotless
Signed-off-by: Ivan Malygin
---
.../swirlds/platform/state/StateLifecycleManagerTests.java | 4 ----
1 file changed, 4 deletions(-)
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateLifecycleManagerTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateLifecycleManagerTests.java
index 1c8a4075d203..52e61ff0e168 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateLifecycleManagerTests.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateLifecycleManagerTests.java
@@ -27,9 +27,6 @@
import com.swirlds.state.StateLifecycleManager;
import com.swirlds.state.merkle.StateLifecycleManagerImpl;
import com.swirlds.state.test.fixtures.merkle.TestVirtualMapState;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
import org.hiero.base.constructable.ConstructableRegistry;
import org.hiero.base.constructable.ConstructableRegistryException;
import org.junit.jupiter.api.AfterEach;
@@ -172,7 +169,6 @@ void initStateRejectsImmutableInput() {
assertThrows(MutabilityException.class, () -> stateLifecycleManager.initState(immutable, false));
}
-
private static MerkleNodeState newState(PlatformStateFacade platformStateFacade) {
final String virtualMapLabel =
StateLifecycleManagerTests.class.getSimpleName() + "-" + java.util.UUID.randomUUID();
From 627822d3082c174c6042b26071e42b5777a339e2 Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Thu, 6 Nov 2025 11:04:41 -0500
Subject: [PATCH 16/23] Addressed review comments: - got rid of `getRound`
method - improved javadoc - added missing nullity annotations
Signed-off-by: Ivan Malygin
---
.../node/app/HederaVirtualMapState.java | 17 ++++----
.../node/app/fixtures/state/FakeState.java | 5 ---
.../otter/fixtures/app/OtterAppState.java | 13 +-----
.../ConsistencyTestingToolState.java | 13 +-----
.../swirlds/demo/iss/ISSTestingToolState.java | 13 +-----
.../migration/MigrationTestingToolState.java | 14 +------
.../utility/MerkleTreeSnapshotWriter.java | 12 +++---
.../DefaultSavedStateController.java | 2 +-
.../state/StateLifecycleManagerTests.java | 20 ++++++++++
.../com/swirlds/state/MerkleNodeState.java | 7 ----
.../swirlds/state/StateLifecycleManager.java | 7 +++-
.../merkle/StateLifecycleManagerImpl.java | 40 ++++++++++++++-----
.../swirlds/state/merkle/VirtualMapState.java | 14 +++----
.../fixtures/merkle/TestVirtualMapState.java | 13 +-----
14 files changed, 84 insertions(+), 106 deletions(-)
diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/HederaVirtualMapState.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/HederaVirtualMapState.java
index f00f53f28b08..4db1bbdcf921 100644
--- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/HederaVirtualMapState.java
+++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/HederaVirtualMapState.java
@@ -43,7 +43,7 @@ public class HederaVirtualMapState extends VirtualMapState md) {
// do nothing
}
-
- @Override
- public long getRound() {
- return 0;
- }
}
diff --git a/platform-sdk/consensus-otter-tests/src/testFixtures/java/org/hiero/otter/fixtures/app/OtterAppState.java b/platform-sdk/consensus-otter-tests/src/testFixtures/java/org/hiero/otter/fixtures/app/OtterAppState.java
index 45481d0b1f40..08c6a4c19a67 100644
--- a/platform-sdk/consensus-otter-tests/src/testFixtures/java/org/hiero/otter/fixtures/app/OtterAppState.java
+++ b/platform-sdk/consensus-otter-tests/src/testFixtures/java/org/hiero/otter/fixtures/app/OtterAppState.java
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
package org.hiero.otter.fixtures.app;
-import static com.swirlds.platform.state.service.PlatformStateFacade.DEFAULT_PLATFORM_STATE_FACADE;
import static org.hiero.otter.fixtures.app.state.OtterStateInitializer.initOtterAppState;
import com.hedera.hapi.node.base.SemanticVersion;
@@ -23,12 +22,12 @@ public class OtterAppState extends VirtualMapState implements Mer
public OtterAppState(
@NonNull final Configuration configuration, @NonNull final Metrics metrics, @NonNull final Time time) {
- super(configuration, metrics, time);
+ super(configuration, metrics);
}
public OtterAppState(
@NonNull final VirtualMap virtualMap, @NonNull final Metrics metrics, @NonNull final Time time) {
- super(virtualMap, metrics, time);
+ super(virtualMap, metrics);
}
/**
@@ -82,14 +81,6 @@ protected OtterAppState copyingConstructor() {
return new OtterAppState(this);
}
- /**
- * {@inheritDoc}
- */
- @Override
- public long getRound() {
- return DEFAULT_PLATFORM_STATE_FACADE.roundOf(this);
- }
-
/**
* Commit the state of all services.
*/
diff --git a/platform-sdk/platform-apps/tests/ConsistencyTestingTool/src/main/java/com/swirlds/demo/consistency/ConsistencyTestingToolState.java b/platform-sdk/platform-apps/tests/ConsistencyTestingTool/src/main/java/com/swirlds/demo/consistency/ConsistencyTestingToolState.java
index 31d37678bef7..6c33e67fc809 100644
--- a/platform-sdk/platform-apps/tests/ConsistencyTestingTool/src/main/java/com/swirlds/demo/consistency/ConsistencyTestingToolState.java
+++ b/platform-sdk/platform-apps/tests/ConsistencyTestingTool/src/main/java/com/swirlds/demo/consistency/ConsistencyTestingToolState.java
@@ -6,7 +6,6 @@
import static com.swirlds.demo.consistency.V0680ConsistencyTestingToolSchema.STATE_LONG_STATE_ID;
import static com.swirlds.logging.legacy.LogMarker.EXCEPTION;
import static com.swirlds.logging.legacy.LogMarker.STARTUP;
-import static com.swirlds.platform.state.service.PlatformStateFacade.DEFAULT_PLATFORM_STATE_FACADE;
import static org.hiero.base.utility.ByteUtils.byteArrayToLong;
import static org.hiero.base.utility.NonCryptographicHashing.hash64;
@@ -84,7 +83,7 @@ public class ConsistencyTestingToolState extends VirtualMapState im
public ISSTestingToolState(
@NonNull final Configuration configuration, @NonNull final Metrics metrics, @NonNull final Time time) {
- super(configuration, metrics, time);
+ super(configuration, metrics);
}
public ISSTestingToolState(
@NonNull final VirtualMap virtualMap, @NonNull final Metrics metrics, @NonNull final Time time) {
- super(virtualMap, metrics, time);
+ super(virtualMap, metrics);
}
/**
@@ -187,12 +186,4 @@ List getPlannedIssList() {
List getPlannedLogErrorList() {
return plannedLogErrorList;
}
-
- /**
- * {@inheritDoc}
- */
- @Override
- public long getRound() {
- return DEFAULT_PLATFORM_STATE_FACADE.roundOf(this);
- }
}
diff --git a/platform-sdk/platform-apps/tests/MigrationTestingTool/src/main/java/com/swirlds/demo/migration/MigrationTestingToolState.java b/platform-sdk/platform-apps/tests/MigrationTestingTool/src/main/java/com/swirlds/demo/migration/MigrationTestingToolState.java
index 0bd0353025de..823d0178585d 100644
--- a/platform-sdk/platform-apps/tests/MigrationTestingTool/src/main/java/com/swirlds/demo/migration/MigrationTestingToolState.java
+++ b/platform-sdk/platform-apps/tests/MigrationTestingTool/src/main/java/com/swirlds/demo/migration/MigrationTestingToolState.java
@@ -1,8 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
package com.swirlds.demo.migration;
-import static com.swirlds.platform.state.service.PlatformStateFacade.DEFAULT_PLATFORM_STATE_FACADE;
-
import com.swirlds.base.time.Time;
import com.swirlds.config.api.Configuration;
import com.swirlds.metrics.api.Metrics;
@@ -15,12 +13,12 @@ public class MigrationTestingToolState extends VirtualMapState stateLifecycleManager.initState(immutable, false));
}
+ @Test
+ @DisplayName("getMutableState() throws if not initialized")
+ void getMutableStateThrowsIfNotInitialized() {
+ final PlatformContext platformContext =
+ TestPlatformContextBuilder.create().build();
+ final StateLifecycleManager uninitialized = new StateLifecycleManagerImpl(
+ platformContext.getMetrics(), platformContext.getTime(), TestVirtualMapState::new);
+ assertThrows(IllegalStateException.class, uninitialized::getMutableState);
+ }
+
+ @Test
+ @DisplayName("getLatestImmutableState() throws if not initialized")
+ void getLatestImmutableStateThrowsIfNotInitialized() {
+ final PlatformContext platformContext =
+ TestPlatformContextBuilder.create().build();
+ final StateLifecycleManager uninitialized = new StateLifecycleManagerImpl(
+ platformContext.getMetrics(), platformContext.getTime(), TestVirtualMapState::new);
+ assertThrows(IllegalStateException.class, uninitialized::getLatestImmutableState);
+ }
+
private static MerkleNodeState newState(PlatformStateFacade platformStateFacade) {
final String virtualMapLabel =
StateLifecycleManagerTests.class.getSimpleName() + "-" + java.util.UUID.randomUUID();
diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeState.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeState.java
index 68b3a9abb793..ac7097fe2b3a 100644
--- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeState.java
+++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/MerkleNodeState.java
@@ -23,13 +23,6 @@ default MerkleNode getRoot() {
return (MerkleNode) this;
}
- /**
- * Retrieves the round number associated with this state.
- *
- * @return a round number of the state.
- */
- long getRound();
-
/**
* {@inheritDoc}
*/
diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
index 538a8a7d2ba6..f3483bfe14b7 100644
--- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
+++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
@@ -27,13 +27,18 @@ public interface StateLifecycleManager {
/**
* Get the mutable state. Consecutive calls to this method may return different instances,
- * if this method is not called on the one and the only thread that is calling {@link #copyMutableState}
+ * if this method is not called on the one and the only thread that is calling {@link #copyMutableState}.
+ * If a parallel thread calls {@link #copyMutableState}, the returned object will become immutable and
+ * on the subsequent call of {@link #copyMutableState} it will be destroyed.
+ *
+ * @return the mutable state.
*/
MerkleNodeState getMutableState();
/**
* Get the latest immutable state. Consecutive calls to this method may return different instances,
* if this method is not called on the one and the only thread that is calling {@link #copyMutableState}
+ * If a parallel thread calls {@link #copyMutableState}, the returned object will become destroyed.
* @return the latest immutable state.
*/
MerkleNodeState getLatestImmutableState();
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
index 3f72efd0c7b1..e0380fde3ac1 100644
--- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
+++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
@@ -14,7 +14,6 @@
import com.swirlds.state.StateLifecycleManager;
import com.swirlds.virtualmap.VirtualMap;
import edu.umd.cs.findbugs.annotations.NonNull;
-import edu.umd.cs.findbugs.annotations.Nullable;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
@@ -23,7 +22,7 @@
/**
* This class is responsible for maintaining references to the mutable state and the latest immutable state.
- * It also updates these references upon state signing. This implementation is thread-safe.
+ * It also updates these references upon request. This implementation is thread-safe.
*/
public class StateLifecycleManagerImpl implements StateLifecycleManager {
@@ -67,6 +66,7 @@ public class StateLifecycleManagerImpl implements StateLifecycleManager {
*
* @param metrics the metrics object to gather state metrics
* @param time the time object
+ * @param stateSupplier a factory object to create an instance of a class implementing {@link MerkleNodeState} from a {@link VirtualMap}
*/
public StateLifecycleManagerImpl(
@NonNull final Metrics metrics,
@@ -82,9 +82,7 @@ public StateLifecycleManagerImpl(
}
/**
- * Set the initial State. This method should only be on a startup of after a reconnect.
- *
- * @param state the initial state
+ * {@inheritDoc}
*/
public synchronized void initState(@NonNull final MerkleNodeState state, final boolean onStartup) {
requireNonNull(state);
@@ -99,17 +97,35 @@ public synchronized void initState(@NonNull final MerkleNodeState state, final b
copyAndUpdateStateRefs(state);
}
+ /**
+ * {@inheritDoc}
+ */
+ @NonNull
@Override
public MerkleNodeState getMutableState() {
- return stateRef.get();
+ final MerkleNodeState mutableState = stateRef.get();
+ if (mutableState == null) {
+ throw new IllegalStateException("StateLifecycleManager has not been initialized.");
+ }
+ return mutableState;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
- @Nullable
+ @NonNull
public MerkleNodeState getLatestImmutableState() {
- return latestImmutableStateRef.get();
+ final MerkleNodeState latestImmutableState = latestImmutableStateRef.get();
+ if (latestImmutableState == null) {
+ throw new IllegalStateException("StateLifecycleManager has not been initialized.");
+ }
+ return latestImmutableState;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
@NonNull
public synchronized MerkleNodeState copyMutableState() {
@@ -131,13 +147,15 @@ private void copyAndUpdateStateRefs(final @NonNull MerkleNodeState stateToCopy)
stateMetrics.stateCopyMicros((copyEnd - copyStart) * NANOSECONDS_TO_MICROSECONDS);
// releasing previous immutable previousMutableState
final State previousImmutableState = latestImmutableStateRef.get();
- if (previousImmutableState != null && !previousImmutableState.isDestroyed()) {
+ if (previousImmutableState != null) {
+ previousImmutableState.throwIfDestroyed();
previousImmutableState.release();
}
stateToCopy.getRoot().reserve();
latestImmutableStateRef.set(stateToCopy);
final MerkleNodeState previousMutableState = stateRef.get();
- if (previousMutableState != null && !previousMutableState.isDestroyed()) {
+ if (previousMutableState != null) {
+ previousMutableState.throwIfDestroyed();
previousMutableState.release();
}
// Do not increment the reference count because the stateToCopy provided already has a reference count of at
@@ -154,7 +172,7 @@ public void createSnapshot(final @NonNull MerkleNodeState state, final @NonNull
state.throwIfMutable();
state.throwIfDestroyed();
final long startTime = time.currentTimeMillis();
- MerkleTreeSnapshotWriter.createSnapshot(state.getRoot(), targetPath, state.getRound());
+ MerkleTreeSnapshotWriter.createSnapshot(state.getRoot(), targetPath, state.toString());
snapshotMetrics.updateWriteStateToDiskTimeMetric(time.currentTimeMillis() - startTime);
}
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java
index cb1902e24e20..712b540a278a 100644
--- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java
+++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/VirtualMapState.java
@@ -16,7 +16,6 @@
import com.hedera.pbj.runtime.Codec;
import com.hedera.pbj.runtime.io.buffer.Bytes;
-import com.swirlds.base.time.Time;
import com.swirlds.common.Reservable;
import com.swirlds.common.merkle.MerkleNode;
import com.swirlds.config.api.Configuration;
@@ -100,6 +99,9 @@ public abstract class VirtualMapState> implements M
private final Metrics metrics;
+ /**
+ * The state storage
+ */
protected VirtualMap virtualMap;
/**
@@ -113,10 +115,8 @@ public abstract class VirtualMapState> implements M
*
* @param configuration the platform configuration instance to use when creating the new instance of state
* @param metrics the platform metric instance to use when creating the new instance of state
- * @param time the time instance to use when creating the new instance of state
*/
- public VirtualMapState(
- @NonNull final Configuration configuration, @NonNull final Metrics metrics, @NonNull final Time time) {
+ public VirtualMapState(@NonNull final Configuration configuration, @NonNull final Metrics metrics) {
requireNonNull(configuration);
this.metrics = requireNonNull(metrics);
final MerkleDbDataSourceBuilder dsBuilder;
@@ -133,10 +133,8 @@ public VirtualMapState(
*
* @param virtualMap the virtual map with pre-registered metrics
* @param metrics the platform metric instance to use when creating the new instance of state
- * @param time the time instance to use when creating the new instance of state
*/
- public VirtualMapState(
- @NonNull final VirtualMap virtualMap, @NonNull final Metrics metrics, @NonNull final Time time) {
+ public VirtualMapState(@NonNull final VirtualMap virtualMap, @NonNull final Metrics metrics) {
this.virtualMap = requireNonNull(virtualMap);
this.metrics = requireNonNull(metrics);
}
@@ -165,8 +163,6 @@ protected VirtualMapState(@NonNull final VirtualMapState from) {
*/
protected abstract T copyingConstructor();
- // State interface implementation
-
/**
* {@inheritDoc}
*/
diff --git a/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/TestVirtualMapState.java b/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/TestVirtualMapState.java
index 1f1366ac6763..5c1bf6fcaecb 100644
--- a/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/TestVirtualMapState.java
+++ b/platform-sdk/swirlds-state-impl/src/testFixtures/java/com/swirlds/state/test/fixtures/merkle/TestVirtualMapState.java
@@ -3,7 +3,6 @@
import static com.swirlds.state.test.fixtures.merkle.VirtualMapUtils.CONFIGURATION;
-import com.swirlds.base.test.fixtures.time.FakeTime;
import com.swirlds.common.metrics.noop.NoOpMetrics;
import com.swirlds.state.MerkleNodeState;
import com.swirlds.state.State;
@@ -17,11 +16,11 @@
public class TestVirtualMapState extends VirtualMapState implements MerkleNodeState {
public TestVirtualMapState() {
- super(CONFIGURATION, new NoOpMetrics(), new FakeTime());
+ super(CONFIGURATION, new NoOpMetrics());
}
public TestVirtualMapState(@NonNull final VirtualMap virtualMap) {
- super(virtualMap, new NoOpMetrics(), new FakeTime());
+ super(virtualMap, new NoOpMetrics());
}
protected TestVirtualMapState(@NonNull final TestVirtualMapState from) {
@@ -40,12 +39,4 @@ public static TestVirtualMapState createInstanceWithVirtualMapLabel(@NonNull fin
final var virtualMap = VirtualMapUtils.createVirtualMap(CONFIGURATION, virtualMapLabel);
return new TestVirtualMapState(virtualMap);
}
-
- /**
- * {@inheritDoc}
- */
- @Override
- public long getRound() {
- return 0; // genesis round
- }
}
From 8cb6973cadcd2dd2984b1f5a6772fbe1055d745f Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Thu, 6 Nov 2025 11:19:49 -0500
Subject: [PATCH 17/23] Fixed dependencies
Signed-off-by: Ivan Malygin
---
.../swirlds-state-impl/src/testFixtures/java/module-info.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/platform-sdk/swirlds-state-impl/src/testFixtures/java/module-info.java b/platform-sdk/swirlds-state-impl/src/testFixtures/java/module-info.java
index 78cd8e903968..9b0baffa0eec 100644
--- a/platform-sdk/swirlds-state-impl/src/testFixtures/java/module-info.java
+++ b/platform-sdk/swirlds-state-impl/src/testFixtures/java/module-info.java
@@ -12,7 +12,6 @@
requires transitive com.swirlds.virtualmap;
requires transitive org.hiero.base.utility;
requires transitive org.junit.jupiter.params;
- requires com.swirlds.base.test.fixtures;
requires com.swirlds.common.test.fixtures;
requires com.swirlds.config.extensions;
requires com.swirlds.fcqueue;
From c2d10cc06768b8fe5c03e2dc0cd2e1da09ad6875 Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Thu, 6 Nov 2025 13:23:56 -0500
Subject: [PATCH 18/23] Unit test fix
Signed-off-by: Ivan Malygin
---
.../hedera/node/app/state/merkle/SerializationTest.java | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java
index 5d40028214a4..004eb0a92f58 100644
--- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java
+++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java
@@ -141,7 +141,9 @@ private void forceFlush(ReadableKVState, ?> state) {
if (vm.size() > 1) {
vm.enableFlush();
- vm.release();
+ if (vm.getReservationCount() > 0) {
+ vm.release();
+ }
vm.waitUntilFlushed();
}
} catch (IllegalAccessException | NoSuchFieldException | InterruptedException e) {
@@ -218,17 +220,17 @@ void snapshot() throws IOException {
* After it gets saved to disk again, and then loaded back in, it results in ClassCastException due to incorrect classId.
*/
@Test
- void dualReadAndWrite() throws IOException, ConstructableRegistryException {
+ void dualReadAndWrite() throws IOException {
final Schema schemaV1 = createV1Schema();
final StateLifecycleManager stateLifecycleManager = createStateLifecycleManager(schemaV1);
final MerkleNodeState originalTree = stateLifecycleManager.getMutableState();
MerkleNodeState copy = stateLifecycleManager.copyMutableState(); // make a copy to make VM flushable
- forceFlush(originalTree.getReadableStates(FIRST_SERVICE).get(FRUIT_STATE_ID));
stateLifecycleManager
.copyMutableState()
.release(); // make a fast copy because we can only write to disk an immutable copy
+ forceFlush(originalTree.getReadableStates(FIRST_SERVICE).get(FRUIT_STATE_ID));
copy.getRoot().getHash();
final byte[] serializedBytes = writeTree(copy.getRoot(), dir);
From 5ba04549c3310e0b0d8f78c335d23d1f3b6363d2 Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Thu, 6 Nov 2025 14:42:05 -0500
Subject: [PATCH 19/23] Fixed unit tests. Addressed review comments: - improved
javadocs - removed `synchronized` modifier from `initState` and
`copyMutableState`
Signed-off-by: Ivan Malygin
---
.../swirlds/platform/StateFileManagerTests.java | 15 ++++++++++++---
.../swirlds/state/StateLifecycleManager.java | 2 --
.../state/merkle/StateLifecycleManagerImpl.java | 17 ++++++++++++++---
3 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/StateFileManagerTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/StateFileManagerTests.java
index eb12933be39b..a83d73eac7a2 100644
--- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/StateFileManagerTests.java
+++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/StateFileManagerTests.java
@@ -271,7 +271,7 @@ void sequenceOfStatesTest(final boolean startAtGenesis) throws IOException {
.build();
final ReservedSignedState reservedSignedState = signedState.reserve("initialTestReservation");
- initLifecycleManagerAndMakeStateImmutable(reservedSignedState.get());
+ initLifecycleManagerAndMakeStateImmutable(reservedSignedState.get(), round != firstRound);
controller.markSavedState(new StateWithHashComplexity(reservedSignedState, 1));
hashState(signedState);
@@ -382,7 +382,7 @@ void stateDeletionTest() throws IOException {
.resolve("node" + SELF_ID + "_round" + fatalRound);
final SignedState fatalState =
new RandomSignedStateGenerator(random).setRound(fatalRound).build();
- initLifecycleManagerAndMakeStateImmutable(fatalState);
+ initLifecycleManagerAndMakeStateImmutable(fatalState, true);
hashState(fatalState);
fatalState.markAsStateToSave(FATAL_ERROR);
manager.dumpStateTask(StateDumpRequest.create(fatalState.reserve("test")));
@@ -395,7 +395,7 @@ void stateDeletionTest() throws IOException {
new RandomSignedStateGenerator(random).setRound(round).build();
issState.markAsStateToSave(PERIODIC_SNAPSHOT);
states.add(signedState);
- initLifecycleManagerAndMakeStateImmutable(signedState);
+ initLifecycleManagerAndMakeStateImmutable(signedState, true);
hashState(signedState);
manager.saveStateTask(signedState.reserve("test"));
@@ -430,6 +430,15 @@ static void hashState(SignedState signedState) {
}
void initLifecycleManagerAndMakeStateImmutable(SignedState state) {
+ initLifecycleManagerAndMakeStateImmutable(state, false);
+ }
+
+ void initLifecycleManagerAndMakeStateImmutable(SignedState state, boolean createNewStateLifecycleManager) {
+ if (createNewStateLifecycleManager) {
+ stateLifecycleManager =
+ new StateLifecycleManagerImpl(context.getMetrics(), context.getTime(), TestVirtualMapState::new);
+ }
+
stateLifecycleManager.initState(state.getState(), false);
stateLifecycleManager.getMutableState().release();
}
diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
index f3483bfe14b7..1057beb779ce 100644
--- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
+++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
@@ -13,8 +13,6 @@
* Creating a mutable copy of the state, while making the current mutable state immutable.
*
*
- * An implementation of this class must be thread-safe.
- *
*/
public interface StateLifecycleManager {
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
index e0380fde3ac1..3555d6af7dac 100644
--- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
+++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
@@ -22,7 +22,17 @@
/**
* This class is responsible for maintaining references to the mutable state and the latest immutable state.
- * It also updates these references upon request. This implementation is thread-safe.
+ * It also updates these references upon request.
+ * This implementation is NOT thread-safe. However, it provides the following guarantees:
+ *
+ * - After {@link #initState(MerkleNodeState, boolean)}, calls to {@link #getMutableState()} and {@link #getLatestImmutableState()} will always return
+ * non-null values.
+ * - After {@link #copyMutableState()}, the updated mutable state will be visible and available to all threads via {@link #getMutableState()}, and
+ * the updated latest immutable state will be visible and available to all threads via {@link #getLatestImmutableState()}.
+ *
+ *
+ * Important: {@link #initState(MerkleNodeState, boolean)} and {@link #copyMutableState()} are NOT supposed to be called from multiple threads.
+ * They only provide the happens-before guarantees that are described above.
*/
public class StateLifecycleManagerImpl implements StateLifecycleManager {
@@ -84,7 +94,7 @@ public StateLifecycleManagerImpl(
/**
* {@inheritDoc}
*/
- public synchronized void initState(@NonNull final MerkleNodeState state, final boolean onStartup) {
+ public void initState(@NonNull final MerkleNodeState state, final boolean onStartup) {
requireNonNull(state);
state.throwIfDestroyed("state must not be destroyed");
@@ -128,7 +138,7 @@ public MerkleNodeState getLatestImmutableState() {
*/
@Override
@NonNull
- public synchronized MerkleNodeState copyMutableState() {
+ public MerkleNodeState copyMutableState() {
final MerkleNodeState state = stateRef.get();
copyAndUpdateStateRefs(state);
return stateRef.get();
@@ -157,6 +167,7 @@ private void copyAndUpdateStateRefs(final @NonNull MerkleNodeState stateToCopy)
if (previousMutableState != null) {
previousMutableState.throwIfDestroyed();
previousMutableState.release();
+ previousMutableState.release();
}
// Do not increment the reference count because the stateToCopy provided already has a reference count of at
// least
From 7e0628ffbe06ec53eb1a8fa7e9f656556064b2f0 Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Thu, 6 Nov 2025 15:32:18 -0500
Subject: [PATCH 20/23] Removed accidentally added
`previousMutableState.release()`
Signed-off-by: Ivan Malygin
---
.../java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
index 3555d6af7dac..c0a16b83fc81 100644
--- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
+++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
@@ -167,7 +167,6 @@ private void copyAndUpdateStateRefs(final @NonNull MerkleNodeState stateToCopy)
if (previousMutableState != null) {
previousMutableState.throwIfDestroyed();
previousMutableState.release();
- previousMutableState.release();
}
// Do not increment the reference count because the stateToCopy provided already has a reference count of at
// least
From 33abfaf6893c65c1e4b8f21c936b11a1ccbccfaf Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Fri, 7 Nov 2025 10:56:12 -0500
Subject: [PATCH 21/23] Addressed review comment: - improved javadoc
Signed-off-by: Ivan Malygin
---
.../com/swirlds/state/StateLifecycleManager.java | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
index 1057beb779ce..9d8e5e7c7bc2 100644
--- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
+++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
@@ -27,16 +27,22 @@ public interface StateLifecycleManager {
* Get the mutable state. Consecutive calls to this method may return different instances,
* if this method is not called on the one and the only thread that is calling {@link #copyMutableState}.
* If a parallel thread calls {@link #copyMutableState}, the returned object will become immutable and
- * on the subsequent call of {@link #copyMutableState} it will be destroyed.
+ * on the subsequent call of {@link #copyMutableState} it will be destroyed and, therefore, not usable in some contexts.
*
* @return the mutable state.
*/
MerkleNodeState getMutableState();
/**
- * Get the latest immutable state. Consecutive calls to this method may return different instances,
- * if this method is not called on the one and the only thread that is calling {@link #copyMutableState}
- * If a parallel thread calls {@link #copyMutableState}, the returned object will become destroyed.
+ * Get the latest immutable state. Consecutive calls to this method may return different instances
+ * if this method is not called on the one and only thread that is calling {@link #copyMutableState}.
+ * If a parallel thread calls {@link #copyMutableState}, the returned object will become destroyed and, therefore, not usable in some contexts.
+ *
+ * If a durable long-term reference to the immutable state returned by this method is required, it is the
+ * responsibility of the caller to ensure a reference is maintained to prevent its garbage collection. Also,
+ * it is the responsibility of the caller to ensure that the object is not used in contexts in which it may become unusable
+ * (e.g., hashing of the destroyed state is not possible).
+ *
* @return the latest immutable state.
*/
MerkleNodeState getLatestImmutableState();
From b156da99325dcf83cb176db6e1e522e5dec2fbc0 Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Fri, 7 Nov 2025 14:13:02 -0500
Subject: [PATCH 22/23] Addressed review comment: - improved javadoc - improved
handling of destroyed states
Signed-off-by: Ivan Malygin
---
.../swirlds/state/StateLifecycleManager.java | 6 ++++--
.../merkle/StateLifecycleManagerImpl.java | 21 +++++++++++++++----
2 files changed, 21 insertions(+), 6 deletions(-)
diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
index 9d8e5e7c7bc2..6d4d6a8ad495 100644
--- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
+++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/StateLifecycleManager.java
@@ -27,7 +27,8 @@ public interface StateLifecycleManager {
* Get the mutable state. Consecutive calls to this method may return different instances,
* if this method is not called on the one and the only thread that is calling {@link #copyMutableState}.
* If a parallel thread calls {@link #copyMutableState}, the returned object will become immutable and
- * on the subsequent call of {@link #copyMutableState} it will be destroyed and, therefore, not usable in some contexts.
+ * on the subsequent call of {@link #copyMutableState} it will be destroyed (unless it was explicitly reserved outside of this class)
+ * and, therefore, not usable in some contexts.
*
* @return the mutable state.
*/
@@ -36,7 +37,8 @@ public interface StateLifecycleManager {
/**
* Get the latest immutable state. Consecutive calls to this method may return different instances
* if this method is not called on the one and only thread that is calling {@link #copyMutableState}.
- * If a parallel thread calls {@link #copyMutableState}, the returned object will become destroyed and, therefore, not usable in some contexts.
+ * If a parallel thread calls {@link #copyMutableState}, the returned object will become destroyed (unless it was explicitly reserved outside of this class)
+ * and, therefore, not usable in some contexts.
*
* If a durable long-term reference to the immutable state returned by this method is required, it is the
* responsibility of the caller to ensure a reference is maintained to prevent its garbage collection. Also,
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
index c0a16b83fc81..bf650b4a8124 100644
--- a/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
+++ b/platform-sdk/swirlds-state-impl/src/main/java/com/swirlds/state/merkle/StateLifecycleManagerImpl.java
@@ -2,6 +2,7 @@
package com.swirlds.state.merkle;
import static com.swirlds.base.units.UnitConstants.NANOSECONDS_TO_MICROSECONDS;
+import static com.swirlds.logging.legacy.LogMarker.EXCEPTION;
import static java.util.Objects.requireNonNull;
import com.swirlds.base.time.Time;
@@ -19,6 +20,8 @@
import java.nio.file.Path;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
/**
* This class is responsible for maintaining references to the mutable state and the latest immutable state.
@@ -36,6 +39,8 @@
*/
public class StateLifecycleManagerImpl implements StateLifecycleManager {
+ private static final Logger log = LogManager.getLogger(StateLifecycleManagerImpl.class);
+
/**
* Metrics for the state object
*/
@@ -158,15 +163,23 @@ private void copyAndUpdateStateRefs(final @NonNull MerkleNodeState stateToCopy)
// releasing previous immutable previousMutableState
final State previousImmutableState = latestImmutableStateRef.get();
if (previousImmutableState != null) {
- previousImmutableState.throwIfDestroyed();
- previousImmutableState.release();
+ assert !previousImmutableState.isDestroyed();
+ if (previousImmutableState.isDestroyed()) {
+ log.error(EXCEPTION.getMarker(), "previousImmutableState is in destroyed state");
+ } else {
+ previousImmutableState.release();
+ }
}
stateToCopy.getRoot().reserve();
latestImmutableStateRef.set(stateToCopy);
final MerkleNodeState previousMutableState = stateRef.get();
if (previousMutableState != null) {
- previousMutableState.throwIfDestroyed();
- previousMutableState.release();
+ assert !previousMutableState.isDestroyed();
+ if (previousMutableState.isDestroyed()) {
+ log.error(EXCEPTION.getMarker(), "previousImmutableState is in destroyed state");
+ } else {
+ previousMutableState.release();
+ }
}
// Do not increment the reference count because the stateToCopy provided already has a reference count of at
// least
From cce3cef6b902fbc8da00a16f5479220e931463a9 Mon Sep 17 00:00:00 2001
From: Ivan Malygin
Date: Fri, 7 Nov 2025 14:59:04 -0500
Subject: [PATCH 23/23] Resolved dependency issue
Signed-off-by: Ivan Malygin
---
platform-sdk/swirlds-state-impl/src/main/java/module-info.java | 1 +
1 file changed, 1 insertion(+)
diff --git a/platform-sdk/swirlds-state-impl/src/main/java/module-info.java b/platform-sdk/swirlds-state-impl/src/main/java/module-info.java
index a0dbea12c468..49f571133015 100644
--- a/platform-sdk/swirlds-state-impl/src/main/java/module-info.java
+++ b/platform-sdk/swirlds-state-impl/src/main/java/module-info.java
@@ -16,6 +16,7 @@
requires transitive com.swirlds.virtualmap;
requires transitive org.hiero.base.crypto;
requires transitive org.hiero.base.utility;
+ requires com.swirlds.logging;
requires com.swirlds.merkledb;
requires org.apache.logging.log4j;
requires static transitive com.github.spotbugs.annotations;