diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java index 99e4902c554d..04d009e9ab37 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java @@ -102,7 +102,6 @@ import com.hedera.node.app.store.ReadableStoreFactory; import com.hedera.node.app.throttle.CongestionThrottleService; import com.hedera.node.app.tss.TssBaseService; -import com.hedera.node.app.version.HederaSoftwareVersion; import com.hedera.node.app.version.ServicesSoftwareVersion; import com.hedera.node.app.workflows.handle.HandleWorkflow; import com.hedera.node.app.workflows.ingest.IngestWorkflow; @@ -141,6 +140,7 @@ import com.swirlds.platform.system.Round; import com.swirlds.platform.system.SoftwareVersion; import com.swirlds.platform.system.SwirldMain; +import com.swirlds.platform.system.address.AddressBook; import com.swirlds.platform.system.events.Event; import com.swirlds.platform.system.state.notifications.StateHashedListener; import com.swirlds.platform.system.status.PlatformStatus; @@ -324,6 +324,9 @@ public final class Hedera implements SwirldMain, PlatformStatusChangeListener, A @Nullable private CompletableFuture initialStateHashFuture; + @Nullable + private List migrationStateChanges; + /*================================================================================================================== * * Hedera Object Construction. @@ -337,9 +340,9 @@ public final class Hedera implements SwirldMain, PlatformStatusChangeListener, A *

This registration is a critical side effect that must happen called before any Platform initialization * steps that try to create or deserialize a {@link MerkleStateRoot}. * - * @param constructableRegistry the registry to register {@link RuntimeConstructable} factories with - * @param registryFactory the factory to use for creating the services registry - * @param migrator the migrator to use with the services + * @param constructableRegistry the registry to register {@link RuntimeConstructable} factories with + * @param registryFactory the factory to use for creating the services registry + * @param migrator the migrator to use with the services * @param tssBaseServiceFactory the factory for the TSS base service */ public Hedera( @@ -477,46 +480,15 @@ public void notify(@NonNull final PlatformStatusChangeNotification notification) * =================================================================================================================*/ - /** - * Invoked by {@link MerkleStateRoot} when it needs to ensure the {@link PlatformStateService} is initialized. - * - * @param state the root state to be initialized - * @return the state changes after initialization - */ - public List initPlatformState(@NonNull final State state) { - requireNonNull(state); - logger.info("Initializing Hedera platform state"); - final var deserializedVersion = serviceMigrator.creationVersionOf(state); - return serviceMigrator.doMigrations( - state, - servicesRegistry.subRegistryFor(EntityIdService.NAME, PlatformStateService.NAME, RosterStateId.NAME), - deserializedVersion == null ? null : new ServicesSoftwareVersion(deserializedVersion), - version, - bootstrapConfigProvider.getConfiguration(), - UNAVAILABLE_NETWORK_INFO, - UNAVAILABLE_METRICS); - } - - /** - * Invoked by the platform when the state should be initialized. This happens BEFORE - * {@link SwirldMain#init(Platform, NodeId)} and after {@link #newMerkleStateRoot()}. - */ - @SuppressWarnings("java:S1181") // catching Throwable instead of Exception when we do a direct System.exit() - public void onStateInitialized( + public void initializeStatesApi( @NonNull final State state, - @NonNull final Platform platform, + @NonNull final Metrics metrics, @NonNull final InitTrigger trigger, - @Nullable final SoftwareVersion previousVersion) { - // A Hedera object can receive multiple onStateInitialized() calls throughout its lifetime if - // the platform needs to initialize a learned state after reconnect; however, it cannot be - // used by multiple platform instances - if (this.platform != null && this.platform != platform) { - logger.fatal("Fatal error, platform should never change once set"); - throw new IllegalStateException("Platform should never change once set"); - } - this.platform = requireNonNull(platform); - this.metrics = platform.getContext().getMetrics(); + @Nullable final AddressBook genesisAddressBook) { + requireNonNull(state); + this.metrics = requireNonNull(metrics); this.configProvider = new ConfigProviderImpl(trigger == GENESIS, metrics); + final var deserializedVersion = serviceMigrator.creationVersionOf(state); logger.info( "Initializing Hedera state version {} in {} mode with trigger {} and previous version {}", version, @@ -525,41 +497,66 @@ public void onStateInitialized( .getConfigData(HederaConfig.class) .activeProfile(), trigger, - previousVersion == null ? "" : previousVersion); + deserializedVersion == null ? "" : deserializedVersion); + + logger.info("Initializing Hedera platform state"); + final var platformStateMigrations = serviceMigrator.doMigrations( + state, + servicesRegistry.subRegistryFor(EntityIdService.NAME, PlatformStateService.NAME, RosterStateId.NAME), + deserializedVersion == null ? null : new ServicesSoftwareVersion(deserializedVersion), + version, + bootstrapConfigProvider.getConfiguration(), + UNAVAILABLE_NETWORK_INFO, + UNAVAILABLE_METRICS); + migrationStateChanges = new ArrayList<>(); + migrationStateChanges.addAll(platformStateMigrations); + final var readableStore = new ReadablePlatformStateStore(state.getReadableStates(PlatformStateService.NAME)); logger.info( "Platform state includes freeze time={} and last frozen={}", readableStore.getFreezeTime(), readableStore.getLastFrozenTime()); - ServicesSoftwareVersion deserializedVersion = null; - // We do not support downgrading from one version to an older version. - if (previousVersion instanceof ServicesSoftwareVersion servicesSoftwareVersion) { - deserializedVersion = servicesSoftwareVersion; - } else if (previousVersion instanceof HederaSoftwareVersion hederaSoftwareVersion) { - deserializedVersion = new ServicesSoftwareVersion( - hederaSoftwareVersion.servicesVersion(), hederaSoftwareVersion.configVersion()); - } else { - if (previousVersion != null) { - logger.fatal("Deserialized state not created with Hedera software"); - throw new IllegalStateException("Deserialized state not created with Hedera software"); - } - } - if (version.compareTo(deserializedVersion) < 0) { + ServicesSoftwareVersion savedStateVersion = + deserializedVersion == null ? null : new ServicesSoftwareVersion(deserializedVersion); + if (version.compareTo(savedStateVersion) < 0) { logger.fatal( "Fatal error, state source version {} is higher than node software version {}", - deserializedVersion, + savedStateVersion, version); - throw new IllegalStateException("Cannot downgrade from " + deserializedVersion + " to " + version); + throw new IllegalStateException("Cannot downgrade from " + savedStateVersion + " to " + version); } + try { - migrateAndInitialize(state, deserializedVersion, trigger, metrics); + migrateAndInitialize(state, savedStateVersion, trigger, metrics, genesisAddressBook); } catch (final Throwable t) { logger.fatal("Critical failure during initialization", t); throw new IllegalStateException("Critical failure during initialization", t); } } + /** + * Invoked by the platform when the state should be initialized. This happens BEFORE + * {@link SwirldMain#init(Platform, NodeId)} and after {@link #newMerkleStateRoot()}. + */ + @SuppressWarnings("java:S1181") // catching Throwable instead of Exception when we do a direct System.exit() + public void onStateInitialized( + @NonNull final State state, @NonNull final Platform platform, @NonNull final InitTrigger trigger) { + // A Hedera object can receive multiple onStateInitialized() calls throughout its lifetime if + // the platform needs to initialize a learned state after reconnect; however, it cannot be + // used by multiple platform instances + if (this.platform != null && this.platform != platform) { + logger.fatal("Fatal error, platform should never change once set"); + throw new IllegalStateException("Platform should never change once set"); + } + this.platform = requireNonNull(platform); + if (state.getReadableStates(PlatformStateService.NAME).isEmpty()) { + initializeStatesApi(state, metrics, trigger, platform.getAddressBook()); + } + // With the States API grounded in the working state, we can create the object graph from it + initializeDagger(state, trigger); + } + /** * Called by this class when we detect it is time to do migration. The {@code deserializedVersion} must not be newer * than the current software version. If it is prior to the current version, then each migration between the @@ -572,13 +569,15 @@ public void onStateInitialized( * @param state current state * @param deserializedVersion version deserialized * @param trigger trigger that is calling migration + * @param genesisAddressBook * @return the state changes caused by the migration */ private List onMigrate( @NonNull final State state, @Nullable final ServicesSoftwareVersion deserializedVersion, @NonNull final InitTrigger trigger, - @NonNull final Metrics metrics) { + @NonNull final Metrics metrics, + @Nullable final AddressBook genesisAddressBook) { final var previousVersion = deserializedVersion == null ? null : deserializedVersion.getPbjSemanticVersion(); final var isUpgrade = version.compareTo(deserializedVersion) > 0; logger.info( @@ -595,18 +594,11 @@ private List onMigrate( if (trigger == GENESIS) { final var config = configProvider.getConfiguration(); final var ledgerConfig = config.getConfigData(LedgerConfig.class); - final var readableStore = - new ReadablePlatformStateStore(state.getReadableStates(PlatformStateService.NAME)); - final var genesisRoster = createRoster(requireNonNull(readableStore.getAddressBook())); + final var genesisRoster = createRoster(requireNonNull(genesisAddressBook)); + genesisNetworkInfo = new GenesisNetworkInfo(genesisRoster, ledgerConfig.id()); } final List migrationStateChanges = new ArrayList<>(); - if (isNotEmbedded()) { - if (!(state instanceof MerkleStateRoot merkleStateRoot)) { - throw new IllegalStateException("State must be a MerkleStateRoot"); - } - migrationStateChanges.addAll(merkleStateRoot.platformStateInitChangesOrThrow()); - } // (FUTURE) In principle, the FileService could actually change the active configuration during a // migration, which implies we should be passing the config provider and not a static configuration // here; but this is a currently unneeded affordance @@ -647,7 +639,7 @@ private List onMigrate( * {@link #newMerkleStateRoot()} or an instance of {@link MerkleStateRoot} created by the platform and * loaded from the saved state). * - *

(FUTURE) Consider moving this initialization into {@link #onStateInitialized(State, Platform, InitTrigger, SoftwareVersion)} + *

(FUTURE) Consider moving this initialization into {@link #onStateInitialized(State, Platform, InitTrigger)} * instead, as there is no special significance to having it here instead. */ @SuppressWarnings("java:S1181") // catching Throwable instead of Exception when we do a direct System.exit() @@ -900,7 +892,8 @@ private void migrateAndInitialize( @NonNull final State state, @Nullable final ServicesSoftwareVersion deserializedVersion, @NonNull final InitTrigger trigger, - @NonNull final Metrics metrics) { + @NonNull final Metrics metrics, + @Nullable final AddressBook genesisAddressBook) { if (trigger != GENESIS) { requireNonNull(deserializedVersion, "Deserialized version cannot be null for trigger " + trigger); } @@ -908,9 +901,8 @@ private void migrateAndInitialize( // the States API, even if it already has all its children in the Merkle tree, as it will lack // state definitions for those children. (And note services may even require migrations for // those children to be usable with the current version of the software.) - final var migrationStateChanges = onMigrate(state, deserializedVersion, trigger, metrics); - // With the States API grounded in the working state, we can create the object graph from it - initializeDagger(state, trigger, migrationStateChanges); + final var changes = onMigrate(state, deserializedVersion, trigger, metrics, genesisAddressBook); + requireNonNull(migrationStateChanges).addAll(changes); // Log the active configuration logConfiguration(); } @@ -921,10 +913,7 @@ private void migrateAndInitialize( * =================================================================================================================*/ - private void initializeDagger( - @NonNull final State state, - @NonNull final InitTrigger trigger, - @NonNull final List migrationStateChanges) { + private void initializeDagger(@NonNull final State state, @NonNull final InitTrigger trigger) { final var notifications = platform.getNotificationEngine(); final var blockStreamEnabled = isBlockStreamEnabled(); // The Dagger component should be constructed every time we reach this point, even if @@ -975,7 +964,7 @@ private void initializeDagger( .metrics(metrics) .kvStateChangeListener(kvStateChangeListener) .boundaryStateChangeListener(boundaryStateChangeListener) - .migrationStateChanges(migrationStateChanges) + .migrationStateChanges(migrationStateChanges != null ? migrationStateChanges : new ArrayList<>()) .initialStateHash(initialStateHash) .networkInfo(networkInfo) .build(); @@ -996,6 +985,7 @@ private void initializeDagger( .orElseGet(() -> startBlockHashFrom(state)); }); daggerApp.tssBaseService().registerLedgerSignatureConsumer(daggerApp.blockStreamManager()); + migrationStateChanges = null; } } diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/ServicesMain.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/ServicesMain.java index 7c76ac3a9007..b8691c0c0353 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/ServicesMain.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/ServicesMain.java @@ -47,7 +47,6 @@ import com.swirlds.common.crypto.CryptographyFactory; import com.swirlds.common.crypto.CryptographyHolder; import com.swirlds.common.io.filesystem.FileSystemManager; -import com.swirlds.common.io.utility.FileUtils; import com.swirlds.common.io.utility.RecycleBin; import com.swirlds.common.merkle.crypto.MerkleCryptoFactory; import com.swirlds.common.merkle.crypto.MerkleCryptographyFactory; @@ -56,6 +55,7 @@ import com.swirlds.config.api.ConfigurationBuilder; import com.swirlds.config.extensions.sources.SystemEnvironmentConfigSource; import com.swirlds.config.extensions.sources.SystemPropertiesConfigSource; +import com.swirlds.platform.Browser; import com.swirlds.platform.CommandLineArgs; import com.swirlds.platform.ParameterProvider; import com.swirlds.platform.builder.PlatformBuilder; @@ -77,6 +77,7 @@ import java.util.List; import java.util.Set; import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.atomic.AtomicBoolean; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -134,7 +135,7 @@ public void run() { } /** - * Launches Services directly, without use of the "app browser" from {@link com.swirlds.platform.Browser}. The + * Launches Services directly, without use of the "app browser" from {@link Browser}. The * approximate startup sequence is: *

    *
  1. Scan the classpath for {@link RuntimeConstructable} classes, @@ -169,7 +170,6 @@ public void run() { public static void main(final String... args) throws Exception { BootstrapUtils.setupConstructableRegistry(); final Hedera hedera = newHedera(); - // Determine which node to run locally // Load config.txt address book file and parse address book final AddressBook diskAddressBook = loadAddressBook(DEFAULT_CONFIG_FILE_NAME); @@ -207,6 +207,33 @@ public static void main(final String... args) throws Exception { final var recycleBin = RecycleBin.create(metrics, configuration, getStaticThreadManager(), time, fileSystemManager, selfId); + // Create initial state for the platform + final var isGenesis = new AtomicBoolean(false); + final var reservedState = getInitialState( + configuration, + recycleBin, + version, + () -> { + isGenesis.set(true); + final var genesisState = hedera.newMerkleStateRoot(); + hedera.initializeStatesApi( + (MerkleStateRoot) genesisState, metrics, InitTrigger.GENESIS, diskAddressBook); + return genesisState; + }, + SignedStateFileUtils::readState, + Hedera.APP_NAME, + Hedera.SWIRLD_NAME, + selfId, + diskAddressBook); + final var initialState = reservedState.state(); + if (!isGenesis.get()) { + hedera.initializeStatesApi( + (MerkleStateRoot) initialState.get().getState().getSwirldState(), + metrics, + InitTrigger.RESTART, + null); + } + final var cryptography = CryptographyFactory.create(); CryptographyHolder.set(cryptography); // the AddressBook is not changed after this point, so we calculate the hash now @@ -225,17 +252,7 @@ public static void main(final String... args) throws Exception { FileSystemManager.create(configuration), recycleBin, merkleCryptography); - // Create initial state for the platform - final var reservedState = getInitialState( - platformContext, - version, - hedera::newMerkleStateRoot, - SignedStateFileUtils::readState, - Hedera.APP_NAME, - Hedera.SWIRLD_NAME, - selfId, - diskAddressBook); - final var initialState = reservedState.state(); + final var stateHash = reservedState.hash(); // Initialize the address book and set on platform builder @@ -355,7 +372,7 @@ private static AddressBook loadAddressBook(@NonNull final String addressBookPath requireNonNull(addressBookPath); try { final LegacyConfigProperties props = - LegacyConfigPropertiesLoader.loadConfigFile(FileUtils.getAbsolutePath(addressBookPath)); + LegacyConfigPropertiesLoader.loadConfigFile(getAbsolutePath(addressBookPath)); props.appConfig().ifPresent(c -> ParameterProvider.getInstance().setParameters(c.params())); return props.getAddressBook(); } catch (final Exception e) { diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/MerkleStateLifecyclesImpl.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/MerkleStateLifecyclesImpl.java index 7f0a2ba53d3e..c3503d68d359 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/MerkleStateLifecyclesImpl.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/MerkleStateLifecyclesImpl.java @@ -19,7 +19,6 @@ import static com.hedera.node.app.service.token.impl.schemas.V0490TokenSchema.STAKING_INFO_KEY; import static java.util.Objects.requireNonNull; -import com.hedera.hapi.block.stream.output.StateChanges; import com.hedera.hapi.node.state.common.EntityNumber; import com.hedera.hapi.node.state.token.StakingNodeInfo; import com.hedera.node.app.Hedera; @@ -42,7 +41,6 @@ import com.swirlds.virtualmap.VirtualMapMigration; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; -import java.util.List; import java.util.function.BiConsumer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -91,11 +89,6 @@ public MerkleStateLifecyclesImpl( this.weightUpdateVisitor = requireNonNull(weightUpdateVisitor); } - @Override - public List initPlatformState(@NonNull final State state) { - return hedera.initPlatformState(state); - } - @Override public void onPreHandle(@NonNull final Event event, @NonNull final State state) { hedera.onPreHandle(event, state); @@ -119,7 +112,7 @@ public void onStateInitialized( @NonNull final Platform platform, @NonNull final InitTrigger trigger, @Nullable SoftwareVersion previousVersion) { - hedera.onStateInitialized(state, platform, trigger, previousVersion); + hedera.onStateInitialized(state, platform, trigger); } @Override diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/version/ServicesSoftwareVersion.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/version/ServicesSoftwareVersion.java index 75580d8f96c9..0b88f18b0a13 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/version/ServicesSoftwareVersion.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/version/ServicesSoftwareVersion.java @@ -72,7 +72,7 @@ public ServicesSoftwareVersion(@NonNull final SemanticVersion semVer, final int @Override public int compareTo(@Nullable final SoftwareVersion other) { - if (other == null || other instanceof HederaSoftwareVersion) { + if (other == null) { return 1; } else if (other instanceof ServicesSoftwareVersion that) { return SEMANTIC_VERSION_COMPARATOR.compare(this.stateSemVer, that.stateSemVer); diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/MerkleStateLifecyclesImplTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/MerkleStateLifecyclesImplTest.java index fb85d3591342..33f83a5eb7a7 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/MerkleStateLifecyclesImplTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/MerkleStateLifecyclesImplTest.java @@ -109,7 +109,7 @@ void delegatesOnSealConsensusRound() { void delegatesOnStateInitialized() { subject.onStateInitialized(merkleStateRoot, platform, InitTrigger.GENESIS, null); - verify(hedera).onStateInitialized(merkleStateRoot, platform, InitTrigger.GENESIS, null); + verify(hedera).onStateInitialized(merkleStateRoot, platform, InitTrigger.GENESIS); } @Test 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 b5d7fce62ef5..0bd7200bd3bc 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 @@ -197,7 +197,7 @@ private void forceFlush(ReadableKVState state) { @ValueSource(booleans = {true, false}) void simpleReadAndWrite(boolean forceFlush) throws IOException, ConstructableRegistryException { final var schemaV1 = createV1Schema(); - final var originalTree = (MerkleStateRoot) createMerkleHederaState(schemaV1); + final var originalTree = createMerkleHederaState(schemaV1); // When we serialize it to bytes and deserialize it back into a tree MerkleStateRoot copy = originalTree.copy(); // make a copy to make VM flushable diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/version/HederaSoftwareVersionTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/version/HederaSoftwareVersionTest.java deleted file mode 100644 index 2ef97b8087c6..000000000000 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/version/HederaSoftwareVersionTest.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2023-2024 Hedera Hashgraph, LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.hedera.node.app.version; - -import static com.hedera.node.app.version.HederaSoftwareVersion.RELEASE_027_VERSION; -import static com.swirlds.state.spi.HapiUtils.SEMANTIC_VERSION_COMPARATOR; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import com.hedera.hapi.node.base.SemanticVersion; -import com.hedera.node.config.converter.SemanticVersionConverter; -import com.swirlds.common.constructable.ClassConstructorPair; -import com.swirlds.common.constructable.ConstructableRegistry; -import com.swirlds.common.constructable.ConstructableRegistryException; -import com.swirlds.common.io.streams.SerializableDataInputStream; -import com.swirlds.common.io.streams.SerializableDataOutputStream; -import edu.umd.cs.findbugs.annotations.NonNull; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Random; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; - -final class HederaSoftwareVersionTest { - private static final SemanticVersionConverter CONVERTER = new SemanticVersionConverter(); - - @ParameterizedTest(name = "{0} {2} {1}") - @CsvSource( - textBlock = - """ - 0.0.1, 0.0.0, > - 1.0.0, 0.0.10, > - 0.0.0, 0.0.1, < - 0.0.10, 1.0.0, < - 0.0.0, 0.0.0, = - 1.2.3, 1.2.3-alpha.1,> - 1.2.4, 1.2.3-foo, > - 1.2.2, 1.2.3-foo, < - 1.2.3-alpha.1, 1.2.3-alpha.2+1, < - 1.2.4, 1.2.3-foo+1, > - 1.2.2, 1.2.3-foo+1, < - """) - @DisplayName("compareTo()") - void compareTo(@NonNull final String a, @NonNull final String b, final String expected) { - final var versionA = new HederaSoftwareVersion(semver(a), semver(a), 0); - final var versionB = new HederaSoftwareVersion(semver(b), semver(b), 0); - - switch (expected) { - case "<" -> assertThat(versionA).isLessThan(versionB); - case "=" -> assertThat(versionA).isEqualByComparingTo(versionB); - case ">" -> assertThat(versionA).isGreaterThan(versionB); - default -> throw new IllegalArgumentException("Unknown expected value: " + expected); - } - // Ensure that the PBJ versions are also ordered correctly. - final SemanticVersion pbjA = versionA.servicesVersion(); - final SemanticVersion pbjB = versionB.servicesVersion(); - switch (expected) { - case "<" -> assertThat(SEMANTIC_VERSION_COMPARATOR.compare(pbjA, pbjB)) - .isLessThan(0); - case "=" -> assertThat(SEMANTIC_VERSION_COMPARATOR.compare(pbjA, pbjB)) - .isEqualTo(0); - case ">" -> assertThat(SEMANTIC_VERSION_COMPARATOR.compare(pbjA, pbjB)) - .isGreaterThan(0); - default -> throw new IllegalArgumentException("Unknown expected value: " + expected); - } - } - - @Test - void serializationRoundTripWithConfigVersionTest() throws IOException, ConstructableRegistryException { - ConstructableRegistry.getInstance() - .registerConstructable( - new ClassConstructorPair(HederaSoftwareVersion.class, HederaSoftwareVersion::new)); - - final HederaSoftwareVersion v1 = new HederaSoftwareVersion( - new SemanticVersion(0, 48, 0, "alpha.5", ""), new SemanticVersion(0, 48, 0, "", ""), 1); - - final HederaSoftwareVersion v2 = new HederaSoftwareVersion( - new SemanticVersion(0, 48, 0, "alpha.5", ""), new SemanticVersion(0, 48, 0, "", ""), 1); - - assertEquals(0, v1.compareTo(v2)); - - final ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); - final SerializableDataOutputStream out = new SerializableDataOutputStream(byteOut); - out.writeSerializable(v1, true); - - final SerializableDataInputStream in = - new SerializableDataInputStream(new ByteArrayInputStream(byteOut.toByteArray())); - final HederaSoftwareVersion v3 = in.readSerializable(); - - assertEquals(0, v1.compareTo(v3)); - } - - @Test - void byteFormatDoesNotChangeAfterMigration() throws IOException, ConstructableRegistryException { - ConstructableRegistry.getInstance() - .registerConstructable( - new ClassConstructorPair(HederaSoftwareVersion.class, HederaSoftwareVersion::new)); - - /* - // The following code was used to generate the serialized software version on disk. - // File was generated using the branch release/0.47. - - final HederaSoftwareVersion version = new HederaSoftwareVersion(semver("1.2.3"), semver("4.5.6")); - final FileOutputStream fos = new FileOutputStream("hederaSoftwareVersion_27.dat"); - final SerializableDataOutputStream out = new SerializableDataOutputStream(fos); - out.writeSerializable(version, true); - out.close(); - */ - - final byte[] legacyBytes; - try (final InputStream legacyFile = - HederaSoftwareVersion.class.getClassLoader().getResourceAsStream("hederaSoftwareVersion_27.dat")) { - assertNotNull(legacyFile); - legacyBytes = legacyFile.readAllBytes(); - } - - final SerializableDataInputStream legacyIn = - new SerializableDataInputStream(new ByteArrayInputStream(legacyBytes)); - final HederaSoftwareVersion deserializedVersion = legacyIn.readSerializable(); - - assertEquals(RELEASE_027_VERSION, deserializedVersion.getVersion()); - assertEquals(semver("1.2.3"), deserializedVersion.getHapiVersion()); - assertEquals(semver("4.5.6-2147483647"), deserializedVersion.getPbjSemanticVersion()); - - // Write the deserialized version back to a byte array. It should exactly match the original byte array. - final ByteArrayOutputStream newBytes = new ByteArrayOutputStream(); - final SerializableDataOutputStream newOut = new SerializableDataOutputStream(newBytes); - newOut.writeSerializable(deserializedVersion, true); - newOut.close(); - final byte[] newBytesArray = newBytes.toByteArray(); - - assertArrayEquals(legacyBytes, newBytesArray); - } - - @Test - @DisplayName("Sorting HederaSoftwareVersions") - void sorting() { - final var list = new ArrayList(); - for (int i = 0; i < 20; i++) { - list.add(new HederaSoftwareVersion(semver("1.2." + i), semver("1.2." + i), 0)); - } - - final var rand = new Random(3375); - Collections.shuffle(list, rand); - Collections.sort(list); - - for (int i = 0; i < 20; i++) { - assertThat(list.get(i).getHapiVersion().patch()).isEqualTo(i); - } - } - - @Test - @DisplayName("Serialization") - void serialization() throws IOException { - final var version = new HederaSoftwareVersion(semver("1.2.3"), semver("4.5.6"), 0); - - final var serializedBytes = new ByteArrayOutputStream(); - final var out = new SerializableDataOutputStream(serializedBytes); - version.serialize(out); - - final var in = new SerializableDataInputStream(new ByteArrayInputStream(serializedBytes.toByteArray())); - final var deserializedVersion = new HederaSoftwareVersion(); - deserializedVersion.deserialize(in, deserializedVersion.getVersion()); - - assertThat(deserializedVersion.getHapiVersion()).isEqualTo(version.getHapiVersion()); - assertThat(deserializedVersion.getPbjSemanticVersion()).isEqualTo(version.getPbjSemanticVersion()); - } - - private SemanticVersion semver(@NonNull final String s) { - return CONVERTER.convert(s); - } -} diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/version/ServicesSoftwareVersionTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/version/ServicesSoftwareVersionTest.java index 6a24a69660af..5df56958b42b 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/version/ServicesSoftwareVersionTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/version/ServicesSoftwareVersionTest.java @@ -63,13 +63,6 @@ void alwaysLaterThanNull() { assertThat(subject.compareTo(null)).isGreaterThan(0); } - @Test - void alwaysLaterThanHederaSoftwareVersion() { - final var prevVersion = new HederaSoftwareVersion(LATE, LATE, DEFAULT_CONFIG_VERSION); - final var subject = new ServicesSoftwareVersion(EARLY, DEFAULT_CONFIG_VERSION); - assertThat(subject.compareTo(prevVersion)).isGreaterThan(0); - } - @Test void majorIsLaterThanMinor() { final var prevVersion = new ServicesSoftwareVersion(MIDDLE, DEFAULT_CONFIG_VERSION); diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/hedera/embedded/AbstractEmbeddedHedera.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/hedera/embedded/AbstractEmbeddedHedera.java index 631d5015ebae..cb3976304c2b 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/hedera/embedded/AbstractEmbeddedHedera.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/hedera/embedded/AbstractEmbeddedHedera.java @@ -133,7 +133,8 @@ protected AbstractEmbeddedHedera(@NonNull final EmbeddedNode node) { @Override public void start() { - hedera.initPlatformState(state); + hedera.initializeStatesApi(state, fakePlatform().getContext().getMetrics(), GENESIS, addressBook); + final var writableStates = state.getWritableStates(PlatformStateService.NAME); final WritableSingletonState platformState = writableStates.getSingleton(PLATFORM_STATE_KEY); final var currentState = requireNonNull(platformState.get()); @@ -144,7 +145,7 @@ public void start() { ((CommittableWritableStates) writableStates).commit(); hedera.setInitialStateHash(FAKE_START_OF_STATE_HASH); - hedera.onStateInitialized(state, fakePlatform(), GENESIS, null); + hedera.onStateInitialized(state, fakePlatform(), GENESIS); hedera.init(fakePlatform(), defaultNodeId); fakePlatform().start(); fakePlatform().notifyListeners(ACTIVE_NOTIFICATION); diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/support/validators/block/StateChangesValidator.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/support/validators/block/StateChangesValidator.java index 6693616e7d63..a2e4b4cc992d 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/support/validators/block/StateChangesValidator.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/support/validators/block/StateChangesValidator.java @@ -19,8 +19,6 @@ import static com.hedera.node.app.blocks.impl.BlockImplUtils.combine; import static com.hedera.node.app.hapi.utils.CommonUtils.noThrowSha384HashOf; import static com.hedera.node.app.hapi.utils.CommonUtils.sha384DigestOrThrow; -import static com.hedera.node.app.info.UnavailableNetworkInfo.UNAVAILABLE_NETWORK_INFO; -import static com.hedera.node.app.spi.AppContext.Gossip.UNAVAILABLE_GOSSIP; import static com.hedera.services.bdd.junit.hedera.ExternalPath.APPLICATION_PROPERTIES; import static com.hedera.services.bdd.junit.hedera.ExternalPath.SAVED_STATES_DIR; import static com.hedera.services.bdd.junit.hedera.ExternalPath.SWIRLDS_LOG; @@ -31,8 +29,6 @@ import static com.hedera.services.bdd.junit.support.validators.block.ChildHashUtils.hashesByName; import static com.hedera.services.bdd.spec.TargetNetworkType.SUBPROCESS_NETWORK; import static com.swirlds.platform.state.GenesisStateBuilder.initGenesisPlatformState; -import static com.swirlds.platform.state.service.PlatformStateService.PLATFORM_STATE_SERVICE; -import static com.swirlds.platform.system.address.AddressBookUtils.createRoster; import static java.util.Objects.requireNonNull; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -45,74 +41,44 @@ import com.hedera.hapi.block.stream.output.QueuePushChange; import com.hedera.hapi.block.stream.output.SingletonUpdateChange; import com.hedera.hapi.block.stream.output.StateChanges; -import com.hedera.hapi.node.base.Key; -import com.hedera.hapi.node.base.SignatureMap; import com.hedera.hapi.node.base.TokenAssociation; import com.hedera.hapi.node.state.common.EntityIDPair; import com.hedera.hapi.node.state.common.EntityNumber; import com.hedera.hapi.node.state.primitives.ProtoBytes; import com.hedera.hapi.node.state.primitives.ProtoLong; import com.hedera.hapi.node.state.primitives.ProtoString; +import com.hedera.node.app.Hedera; import com.hedera.node.app.blocks.BlockStreamManager; -import com.hedera.node.app.blocks.BlockStreamService; import com.hedera.node.app.blocks.StreamingTreeHasher; import com.hedera.node.app.blocks.impl.NaiveStreamingTreeHasher; import com.hedera.node.app.config.BootstrapConfigProviderImpl; -import com.hedera.node.app.config.ConfigProviderImpl; -import com.hedera.node.app.fees.FeeService; -import com.hedera.node.app.ids.EntityIdService; -import com.hedera.node.app.info.GenesisNetworkInfo; -import com.hedera.node.app.records.BlockRecordService; -import com.hedera.node.app.roster.RosterService; -import com.hedera.node.app.service.addressbook.impl.AddressBookServiceImpl; -import com.hedera.node.app.service.consensus.impl.ConsensusServiceImpl; -import com.hedera.node.app.service.contract.impl.ContractServiceImpl; -import com.hedera.node.app.service.file.impl.FileServiceImpl; -import com.hedera.node.app.service.networkadmin.impl.FreezeServiceImpl; -import com.hedera.node.app.service.networkadmin.impl.NetworkServiceImpl; -import com.hedera.node.app.service.schedule.impl.ScheduleServiceImpl; -import com.hedera.node.app.service.token.impl.TokenServiceImpl; -import com.hedera.node.app.service.util.impl.UtilServiceImpl; -import com.hedera.node.app.services.AppContextImpl; import com.hedera.node.app.services.OrderedServiceMigrator; -import com.hedera.node.app.services.ServicesRegistry; import com.hedera.node.app.services.ServicesRegistryImpl; -import com.hedera.node.app.spi.signatures.SignatureVerifier; -import com.hedera.node.app.state.recordcache.RecordCacheService; -import com.hedera.node.app.throttle.CongestionThrottleService; import com.hedera.node.app.tss.PlaceholderTssLibrary; import com.hedera.node.app.tss.TssBaseServiceImpl; import com.hedera.node.app.version.ServicesSoftwareVersion; -import com.hedera.node.config.VersionedConfiguration; import com.hedera.node.config.converter.BytesConverter; import com.hedera.node.config.data.HederaConfig; import com.hedera.node.config.data.VersionConfig; import com.hedera.pbj.runtime.io.buffer.Bytes; -import com.hedera.services.bdd.junit.hedera.embedded.fakes.FakePlatformContext; import com.hedera.services.bdd.junit.hedera.subprocess.SubProcessNetwork; import com.hedera.services.bdd.junit.support.BlockStreamAccess; import com.hedera.services.bdd.junit.support.BlockStreamValidator; import com.hedera.services.bdd.spec.HapiSpec; -import com.swirlds.common.RosterStateId; import com.swirlds.common.constructable.ConstructableRegistry; -import com.swirlds.common.context.PlatformContext; import com.swirlds.common.crypto.Hash; +import com.swirlds.common.crypto.config.CryptoConfig; import com.swirlds.common.merkle.crypto.MerkleCryptoFactory; import com.swirlds.common.merkle.crypto.MerkleCryptography; import com.swirlds.common.merkle.utility.MerkleTreeVisualizer; +import com.swirlds.common.metrics.config.MetricsConfig; import com.swirlds.common.metrics.noop.NoOpMetrics; -import com.swirlds.common.platform.NodeId; import com.swirlds.config.api.Configuration; -import com.swirlds.platform.state.MerkleStateLifecycles; +import com.swirlds.config.api.ConfigurationBuilder; +import com.swirlds.platform.config.BasicConfig; +import com.swirlds.platform.config.TransactionConfig; import com.swirlds.platform.state.MerkleStateRoot; -import com.swirlds.platform.state.service.PlatformStateService; import com.swirlds.platform.system.InitTrigger; -import com.swirlds.platform.system.Platform; -import com.swirlds.platform.system.Round; -import com.swirlds.platform.system.SoftwareVersion; -import com.swirlds.platform.system.address.AddressBook; -import com.swirlds.platform.system.events.Event; -import com.swirlds.state.State; import com.swirlds.state.spi.CommittableWritableStates; import com.swirlds.state.spi.Service; import edu.umd.cs.findbugs.annotations.NonNull; @@ -129,9 +95,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; -import java.util.concurrent.Executors; import java.util.concurrent.ForkJoinPool; -import java.util.function.Function; import java.util.regex.Pattern; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -165,9 +129,9 @@ public static void main(String[] args) { .normalize(); final var validator = new StateChangesValidator( Bytes.fromHex( - "bc49350852851a2c737ef6b5db24da8ba108401952ec207a1a5a4230de8d8a626da1f3663f0560bd6cf401c601b08896"), + "0340d546d0bfeb6e2f12af275347f584231fa41928a700543c9595affa817da5423bc4aac0689a388f6a8b972de30028"), node0Dir.resolve("output/swirlds.log"), - node0Dir.resolve("genesis-config.txt"), + node0Dir.resolve("config.txt"), node0Dir.resolve("data/config/application.properties"), Bytes.fromHex("03")); final var blocks = @@ -240,41 +204,38 @@ public StateChangesValidator( "hedera.app.properties.path", pathToOverrideProperties.toAbsolutePath().toString()); final var bootstrapConfig = new BootstrapConfigProviderImpl().getConfiguration(); - final var servicesRegistry = new ServicesRegistryImpl(ConstructableRegistry.getInstance(), bootstrapConfig); - registerServices(InstantSource.system(), servicesRegistry, bootstrapConfig); final var versionConfig = bootstrapConfig.getConfigData(VersionConfig.class); final var servicesVersion = versionConfig.servicesVersion(); final var addressBook = loadAddressBookWithDeterministicCerts(pathToAddressBook); - final var roster = createRoster(addressBook); - final var networkInfo = new GenesisNetworkInfo(roster, ledgerId); - - final var migrator = new OrderedServiceMigrator(); final var configVersion = bootstrapConfig.getConfigData(HederaConfig.class).configVersion(); final var currentVersion = new ServicesSoftwareVersion(servicesVersion, configVersion); final var metrics = new NoOpMetrics(); - final var lifecycles = - newPlatformInitLifecycle(bootstrapConfig, currentVersion, migrator, servicesRegistry, metrics); - this.state = new MerkleStateRoot(lifecycles, version -> new ServicesSoftwareVersion(version, configVersion)); - initGenesisPlatformState( - new FakePlatformContext(NodeId.of(0), Executors.newSingleThreadScheduledExecutor()), - this.state.getWritablePlatformState(), - addressBook, - currentVersion); + final var hedera = new Hedera( + ConstructableRegistry.getInstance(), + ServicesRegistryImpl::new, + new OrderedServiceMigrator(), + InstantSource.system(), + appContext -> new TssBaseServiceImpl( + appContext, + ForkJoinPool.commonPool(), + ForkJoinPool.commonPool(), + new PlaceholderTssLibrary(), + ForkJoinPool.commonPool())); + this.state = (MerkleStateRoot) hedera.newMerkleStateRoot(); + hedera.initializeStatesApi(state, metrics, InitTrigger.GENESIS, addressBook); + final Configuration platformConfig = ConfigurationBuilder.create() + .withConfigDataType(MetricsConfig.class) + .withConfigDataType(TransactionConfig.class) + .withConfigDataType(CryptoConfig.class) + .withConfigDataType(BasicConfig.class) + .build(); + initGenesisPlatformState(platformConfig, this.state.getWritablePlatformState(), addressBook, currentVersion); final var stateToBeCopied = state; state = state.copy(); // get the state hash before applying the state changes from current block this.genesisStateHash = CRYPTO.digestTreeSync(stateToBeCopied); - migrator.doMigrations( - state, - servicesRegistry, - null, - new ServicesSoftwareVersion(servicesVersion, configVersion), - new ConfigProviderImpl().getConfiguration(), - networkInfo, - metrics); - logger.info("Registered all Service and migrated state definitions to version {}", servicesVersion); } @@ -524,58 +485,6 @@ public void countQueuePop(String serviceName, String stateKey) { } } - private void registerServices( - final InstantSource instantSource, - final ServicesRegistry servicesRegistry, - final VersionedConfiguration bootstrapConfig) { - final var appContext = new AppContextImpl(instantSource, fakeSignatureVerifier(), UNAVAILABLE_GOSSIP); - // Register all service schema RuntimeConstructable factories before platform init - Set.of( - new EntityIdService(), - new ConsensusServiceImpl(), - new ContractServiceImpl(appContext), - new FileServiceImpl(), - new TssBaseServiceImpl( - appContext, - ForkJoinPool.commonPool(), - ForkJoinPool.commonPool(), - new PlaceholderTssLibrary(), - ForkJoinPool.commonPool()), - new FreezeServiceImpl(), - new ScheduleServiceImpl(), - new TokenServiceImpl(), - new UtilServiceImpl(), - new RecordCacheService(), - new BlockRecordService(), - new BlockStreamService(), - new FeeService(), - new CongestionThrottleService(), - new NetworkServiceImpl(), - new AddressBookServiceImpl(), - new RosterService(), - PLATFORM_STATE_SERVICE) - .forEach(servicesRegistry::register); - } - - private SignatureVerifier fakeSignatureVerifier() { - return new SignatureVerifier() { - @Override - public boolean verifySignature( - @NonNull Key key, - @NonNull Bytes bytes, - @NonNull MessageType messageType, - @NonNull SignatureMap signatureMap, - @Nullable Function simpleKeyVerifier) { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public KeyCounts countSimpleKeys(@NonNull Key key) { - throw new UnsupportedOperationException("Not implemented"); - } - }; - } - private static @Nullable Bytes findRootHashFrom(@NonNull final Path stateMetadataPath) { try (final var lines = Files.lines(stateMetadataPath)) { return lines.filter(line -> line.startsWith("HASH:")) @@ -652,66 +561,6 @@ private static boolean isNumberDirectory(@NonNull final Path path) { return sb == null ? null : hashesByName(sb.toString()); } - private static MerkleStateLifecycles newPlatformInitLifecycle( - @NonNull final Configuration bootstrapConfig, - @NonNull final SoftwareVersion currentVersion, - @NonNull final OrderedServiceMigrator serviceMigrator, - @NonNull final ServicesRegistryImpl servicesRegistry, - @NonNull final NoOpMetrics metrics) { - return new MerkleStateLifecycles() { - @Override - public List initPlatformState(@NonNull final State state) { - final var deserializedVersion = serviceMigrator.creationVersionOf(state); - return serviceMigrator.doMigrations( - state, - servicesRegistry.subRegistryFor( - EntityIdService.NAME, PlatformStateService.NAME, RosterStateId.NAME), - deserializedVersion == null ? null : new ServicesSoftwareVersion(deserializedVersion), - currentVersion, - bootstrapConfig, - UNAVAILABLE_NETWORK_INFO, - metrics); - } - - @Override - public void onPreHandle(@NonNull Event event, @NonNull State state) { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public void onHandleConsensusRound(@NonNull final Round round, @NonNull State state) { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public void onSealConsensusRound(@NonNull final Round round, @NonNull final State state) { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public void onStateInitialized( - @NonNull State state, - @NonNull Platform platform, - @NonNull InitTrigger trigger, - @Nullable SoftwareVersion previousVersion) { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public void onUpdateWeight( - @NonNull MerkleStateRoot state, - @NonNull AddressBook configAddressBook, - @NonNull PlatformContext context) { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public void onNewRecoveredState(@NonNull MerkleStateRoot recoveredState) { - throw new UnsupportedOperationException("Not implemented"); - } - }; - } - private static Object singletonPutFor(@NonNull final SingletonUpdateChange singletonUpdateChange) { return switch (singletonUpdateChange.newValue().kind()) { case UNSET -> throw new IllegalStateException("Singleton update value is not set"); diff --git a/hedera-node/test-clients/src/main/java/module-info.java b/hedera-node/test-clients/src/main/java/module-info.java index 794ec6d26234..5d1bc39acdd2 100644 --- a/hedera-node/test-clients/src/main/java/module-info.java +++ b/hedera-node/test-clients/src/main/java/module-info.java @@ -87,17 +87,12 @@ requires transitive org.junit.platform.launcher; requires transitive org.testcontainers; requires transitive tuweni.bytes; - requires com.hedera.node.app.service.addressbook.impl; requires com.hedera.node.app.service.addressbook; - requires com.hedera.node.app.service.consensus.impl; requires com.hedera.node.app.service.contract.impl; - requires com.hedera.node.app.service.file.impl; - requires com.hedera.node.app.service.network.admin.impl; requires com.hedera.node.app.service.schedule.impl; requires com.hedera.node.app.service.schedule; requires com.hedera.node.app.service.token.impl; requires com.hedera.node.app.service.token; - requires com.hedera.node.app.service.util.impl; requires com.swirlds.base.test.fixtures; requires com.swirlds.config.extensions.test.fixtures; requires com.swirlds.platform.core.test.fixtures; diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/Browser.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/Browser.java index 58bb6a58317e..255c2680742d 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/Browser.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/Browser.java @@ -267,7 +267,8 @@ private static void launchUnhandled(@NonNull final CommandLineArgs commandLineAr MerkleCryptographyFactory.create(configuration, CryptographyHolder.get())); // Create the initial state for the platform final HashedReservedSignedState reservedState = getInitialState( - platformContext, + configuration, + recycleBin, appMain.getSoftwareVersion(), appMain::newMerkleStateRoot, SignedStateFileUtils::readState, diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/CompareStatesCommand.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/CompareStatesCommand.java index 748ad40ed69a..7e88438c37d2 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/CompareStatesCommand.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/CompareStatesCommand.java @@ -137,7 +137,7 @@ private static ReservedSignedState loadAndHashState( logger.info(LogMarker.CLI.getMarker(), "Loading state from {}", statePath); final ReservedSignedState signedState = SignedStateFileReader.readStateFile( - platformContext, statePath, SignedStateFileUtils::readState) + platformContext.getConfiguration(), statePath, SignedStateFileUtils::readState) .reservedSignedState(); logger.info(LogMarker.CLI.getMarker(), "Hashing state"); try { 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 bd083dabd6b3..e0af60a59fc1 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 @@ -74,7 +74,7 @@ public Integer call() throws IOException, ExecutionException, InterruptedExcepti System.out.printf("Reading from %s %n", statePath.toAbsolutePath()); final DeserializedSignedState deserializedSignedState = - SignedStateFileReader.readStateFile(platformContext, statePath, SignedStateFileUtils::readState); + SignedStateFileReader.readStateFile(configuration, statePath, SignedStateFileUtils::readState); try (final ReservedSignedState reservedSignedState = deserializedSignedState.reservedSignedState()) { final PlatformStateModifier platformState = reservedSignedState.get().getState().getWritablePlatformState(); diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/ValidateAddressBookStateCommand.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/ValidateAddressBookStateCommand.java index 295bd8934cd4..a9a6989bf8df 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/ValidateAddressBookStateCommand.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/ValidateAddressBookStateCommand.java @@ -19,7 +19,6 @@ import com.swirlds.cli.commands.StateCommand; import com.swirlds.cli.utility.AbstractCommand; import com.swirlds.cli.utility.SubcommandOf; -import com.swirlds.common.context.PlatformContext; import com.swirlds.config.api.Configuration; import com.swirlds.config.api.ConfigurationBuilder; import com.swirlds.platform.config.DefaultConfiguration; @@ -69,11 +68,9 @@ public Integer call() throws IOException, ExecutionException, InterruptedExcepti final Configuration configuration = DefaultConfiguration.buildBasicConfiguration(ConfigurationBuilder.create()); BootstrapUtils.setupConstructableRegistry(); - final PlatformContext platformContext = PlatformContext.create(configuration); - System.out.printf("Reading state from %s %n", statePath.toAbsolutePath()); final DeserializedSignedState deserializedSignedState = - SignedStateFileReader.readStateFile(platformContext, statePath, SignedStateFileUtils::readState); + SignedStateFileReader.readStateFile(configuration, statePath, SignedStateFileUtils::readState); System.out.printf("Reading address book from %s %n", addressBookPath.toAbsolutePath()); final String addressBookString = Files.readString(addressBookPath); 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 1e804183db43..c1c2104c3673 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 @@ -280,7 +280,7 @@ private StateAndRound createSignedState(@NonNull final ConsensusRound consensusR handlerMetrics.setPhase(CREATING_SIGNED_STATE); final SignedState signedState = new SignedState( - platformContext, + platformContext.getConfiguration(), CryptoStatic::verifySignature, immutableStateCons, "TransactionHandler.createSignedState()", diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectLearner.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectLearner.java index 055b9c77e750..a10372710db7 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectLearner.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/reconnect/ReconnectLearner.java @@ -204,7 +204,7 @@ private ReservedSignedState reconnect() throws InterruptedException { final MerkleRoot state = (MerkleRoot) synchronizer.getRoot(); final SignedState newSignedState = new SignedState( - platformContext, + platformContext.getConfiguration(), CryptoStatic::verifySignature, state, "ReconnectLearner.reconnect()", 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 76ced4608049..f6489d9029ee 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 @@ -151,7 +151,7 @@ public static void recoverState( logger.info(STARTUP.getMarker(), "Loading state from {}", signedStateFile); try (final ReservedSignedState initialState = SignedStateFileReader.readStateFile( - platformContext, signedStateFile, SignedStateFileUtils::readState) + platformContext.getConfiguration(), signedStateFile, SignedStateFileUtils::readState) .reservedSignedState()) { StaticSoftwareVersion.setSoftwareVersion( initialState.get().getState().getReadablePlatformState().getCreationSoftwareVersion()); @@ -410,7 +410,7 @@ private static ReservedSignedState handleNextRound( } final ReservedSignedState signedState = new SignedState( - platformContext, + platformContext.getConfiguration(), CryptoStatic::verifySignature, newState, "EventRecoveryWorkflow.handleNextRound()", diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/GenesisStateBuilder.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/GenesisStateBuilder.java index 55ede92be17c..7812b0c81227 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/GenesisStateBuilder.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/GenesisStateBuilder.java @@ -16,7 +16,7 @@ package com.swirlds.platform.state; -import com.swirlds.common.context.PlatformContext; +import com.swirlds.config.api.Configuration; import com.swirlds.platform.config.BasicConfig; import com.swirlds.platform.crypto.CryptoStatic; import com.swirlds.platform.state.signed.ReservedSignedState; @@ -38,7 +38,7 @@ private GenesisStateBuilder() {} * */ public static void initGenesisPlatformState( - final PlatformContext platformContext, + final Configuration configuration, final PlatformStateModifier platformState, final AddressBook addressBook, final SoftwareVersion appVersion) { @@ -49,7 +49,7 @@ public static void initGenesisPlatformState( v.setLegacyRunningEventHash(null); v.setConsensusTimestamp(Instant.ofEpochSecond(0L)); - final BasicConfig basicConfig = platformContext.getConfiguration().getConfigData(BasicConfig.class); + final BasicConfig basicConfig = configuration.getConfigData(BasicConfig.class); final long genesisFreezeTime = basicConfig.genesisFreezeTime(); if (genesisFreezeTime > 0) { @@ -61,22 +61,22 @@ public static void initGenesisPlatformState( /** * Build and initialize a genesis state. * - * @param platformContext the platform context + * @param configuration the configuration for this node * @param addressBook the current address book * @param appVersion the software version of the app * @param stateRoot the merkle root node of the state * @return a reserved genesis signed state */ public static ReservedSignedState buildGenesisState( - @NonNull final PlatformContext platformContext, + @NonNull final Configuration configuration, @NonNull final AddressBook addressBook, @NonNull final SoftwareVersion appVersion, @NonNull final MerkleRoot stateRoot) { - initGenesisPlatformState(platformContext, stateRoot.getWritablePlatformState(), addressBook, appVersion); + initGenesisPlatformState(configuration, stateRoot.getWritablePlatformState(), addressBook, appVersion); final SignedState signedState = new SignedState( - platformContext, CryptoStatic::verifySignature, stateRoot, "genesis state", false, false, false); + configuration, CryptoStatic::verifySignature, stateRoot, "genesis state", false, false, false); return signedState.reserve("initial reservation on genesis state"); } } diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/MerkleRoot.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/MerkleRoot.java index 67ccce8a2826..a3cf03cf860c 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/MerkleRoot.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/MerkleRoot.java @@ -32,13 +32,6 @@ public interface MerkleRoot extends MerkleInternal { */ @NonNull SwirldState getSwirldState(); - - /** - * This method makes sure that the platform state is initialized. - * If it's already initialized, it does nothing. - */ - void initPlatformState(); - /** * Get readable platform state. * Works on both - mutable and immutable {@link MerkleRoot} and, therefore, this method should be preferred. diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/MerkleStateLifecycles.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/MerkleStateLifecycles.java index 2b49761540fb..90e85037f99e 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/MerkleStateLifecycles.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/MerkleStateLifecycles.java @@ -16,7 +16,6 @@ package com.swirlds.platform.state; -import com.hedera.hapi.block.stream.output.StateChanges; import com.swirlds.common.context.PlatformContext; import com.swirlds.platform.system.InitTrigger; import com.swirlds.platform.system.Platform; @@ -27,7 +26,6 @@ import com.swirlds.state.State; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; -import java.util.List; /** * Implements the major lifecycle events for the Merkle state. @@ -36,15 +34,6 @@ * interface; but in the future will be callbacks registered with a platform builder. */ public interface MerkleStateLifecycles { - /** - * Called when a {@link MerkleStateRoot} needs to ensure its {@link PlatformStateModifier} implementation - * is initialized. - * - * @param state the root of the state to be initialized - * @return the list of builders for state changes that occurred during the initialization - */ - List initPlatformState(@NonNull State state); - /** * Called when an event is added to the hashgraph used to compute consensus ordering * for this node. diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/MerkleStateRoot.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/MerkleStateRoot.java index 3291e3ceb1a9..930339e1f7bd 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/MerkleStateRoot.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/MerkleStateRoot.java @@ -20,6 +20,7 @@ import static com.swirlds.logging.legacy.LogMarker.STARTUP; import static com.swirlds.platform.state.MerkleStateUtils.createInfoString; import static com.swirlds.platform.state.service.PbjConverter.toPbjPlatformState; +import static com.swirlds.platform.state.service.schemas.V0540PlatformStateSchema.PLATFORM_STATE_KEY; import static com.swirlds.platform.system.InitTrigger.EVENT_STREAM_RECOVERY; import static com.swirlds.state.StateChangeListener.StateType.MAP; import static com.swirlds.state.StateChangeListener.StateType.QUEUE; @@ -27,7 +28,6 @@ import static com.swirlds.state.merkle.StateUtils.computeLabel; import static java.util.Objects.requireNonNull; -import com.hedera.hapi.block.stream.output.StateChanges; import com.hedera.hapi.node.base.SemanticVersion; import com.swirlds.base.time.Time; import com.swirlds.common.constructable.ConstructableIgnored; @@ -43,7 +43,9 @@ import com.swirlds.metrics.api.Metrics; import com.swirlds.platform.state.service.PlatformStateService; import com.swirlds.platform.state.service.ReadablePlatformStateStore; +import com.swirlds.platform.state.service.SnapshotPlatformStateAccessor; import com.swirlds.platform.state.service.WritablePlatformStateStore; +import com.swirlds.platform.state.service.schemas.V0540PlatformStateSchema; import com.swirlds.platform.system.InitTrigger; import com.swirlds.platform.system.Platform; import com.swirlds.platform.system.Round; @@ -179,13 +181,6 @@ public class MerkleStateRoot extends PartialNaryMerkleInternal */ private final List listeners = new ArrayList<>(); - /** - * Once set, the possibly empty list of builders for state changes that occurred when initializing - * the platform state. - */ - @Nullable - private List platformStateInitChanges; - /** * Used to track the lifespan of this state. */ @@ -223,15 +218,6 @@ public MerkleStateRoot( return preV054PlatformState; } - /** - * Returns the list of builders for state changes that occurred during the initialization of the platform state. - * Must only be called after platform state is initialized. - * @throws NullPointerException if the platform state initialization changes have not been set - */ - public @NonNull List platformStateInitChangesOrThrow() { - return requireNonNull(platformStateInitChanges); - } - /** * {@inheritDoc} *

    @@ -288,7 +274,6 @@ protected MerkleStateRoot(@NonNull final MerkleStateRoot from) { this.registryRecord = RuntimeObjectRegistry.createRecord(getClass()); this.versionFactory = from.versionFactory; this.preV054PlatformState = from.preV054PlatformState; - this.platformStateInitChanges = from.platformStateInitChanges; this.listeners.addAll(from.listeners); // Copy over the metadata @@ -1017,7 +1002,16 @@ public SwirldState getSwirldState() { @NonNull @Override public PlatformStateAccessor getReadablePlatformState() { - return readablePlatformStateStore(); + return services.isEmpty() + ? new SnapshotPlatformStateAccessor(getPlatformState(), versionFactory) + : readablePlatformStateStore(); + } + + private com.hedera.hapi.platform.state.PlatformState getPlatformState() { + final var index = findNodeIndex(PlatformStateService.NAME, PLATFORM_STATE_KEY); + return index == -1 + ? V0540PlatformStateSchema.GENESIS_PLATFORM_STATE + : ((SingletonNode) getChild(index)).getValue(); } /** @@ -1041,16 +1035,6 @@ public PlatformStateModifier getWritablePlatformState() { return writablePlatformStateStore(); } - /** - * {@inheritDoc} - */ - @Override - public void initPlatformState() { - if (!services.containsKey(PlatformStateService.NAME)) { - platformStateInitChanges = lifecycles.initPlatformState(this); - } - } - /** * Updates the platform state with the values from the provided instance of {@link PlatformStateModifier} * @@ -1067,7 +1051,7 @@ public void updatePlatformState(@NonNull final PlatformStateModifier accessor) { @NonNull @Override public String getInfoString(final int hashDepth) { - return createInfoString(hashDepth, readablePlatformStateStore(), getHash(), this); + return createInfoString(hashDepth, getReadablePlatformState(), getHash(), this); } private ReadablePlatformStateStore readablePlatformStateStore() { @@ -1075,9 +1059,6 @@ private ReadablePlatformStateStore readablePlatformStateStore() { } private WritablePlatformStateStore writablePlatformStateStore() { - if (!services.containsKey(PlatformStateService.NAME)) { - platformStateInitChanges = lifecycles.initPlatformState(this); - } final var store = new WritablePlatformStateStore(getWritableStates(PlatformStateService.NAME), versionFactory); if (preV054PlatformState != null) { store.setAllFrom(preV054PlatformState); diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/State.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/State.java index 373cd7db8101..b161872e0d93 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/State.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/State.java @@ -111,12 +111,6 @@ && getSwirldState() instanceof MerkleStateRoot merkleStateRoot) { return this; } - - @Override - public void initPlatformState() { - // no initialization required - } - /** * {@inheritDoc} */ diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/editor/StateEditor.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/editor/StateEditor.java index d1d65c6e8e9a..f9da004e2a5a 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/editor/StateEditor.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/editor/StateEditor.java @@ -67,7 +67,7 @@ public StateEditor(final Path statePath) throws IOException { platformContext = PlatformContext.create(configuration); final DeserializedSignedState deserializedSignedState = - SignedStateFileReader.readStateFile(platformContext, statePath, SignedStateFileUtils::readState); + SignedStateFileReader.readStateFile(configuration, statePath, SignedStateFileUtils::readState); try (final ReservedSignedState reservedSignedState = deserializedSignedState.reservedSignedState()) { System.out.println("\nLoading state from " + statePath); @@ -203,7 +203,7 @@ public ReservedSignedState getSignedStateCopy() { try (final ReservedSignedState reservedSignedState = signedState.getAndReserve("StateEditor.getSignedStateCopy() 1")) { final SignedState newSignedState = new SignedState( - platformContext, + platformContext.getConfiguration(), CryptoStatic::verifySignature, reservedSignedState.get().getState().copy(), "StateEditor.getSignedStateCopy()", diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/service/SnapshotPlatformStateAccessor.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/service/SnapshotPlatformStateAccessor.java new file mode 100644 index 000000000000..8ff5b6d2f2b8 --- /dev/null +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/service/SnapshotPlatformStateAccessor.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.swirlds.platform.state.service; + +import static com.swirlds.platform.state.service.PbjConverter.fromPbjAddressBook; +import static com.swirlds.platform.state.service.PbjConverter.fromPbjConsensusSnapshot; +import static com.swirlds.platform.state.service.PbjConverter.fromPbjTimestamp; +import static java.util.Objects.requireNonNull; + +import com.hedera.hapi.node.base.SemanticVersion; +import com.hedera.hapi.platform.state.PlatformState; +import com.swirlds.common.crypto.Hash; +import com.swirlds.platform.consensus.ConsensusSnapshot; +import com.swirlds.platform.state.PlatformStateAccessor; +import com.swirlds.platform.system.SoftwareVersion; +import com.swirlds.platform.system.address.AddressBook; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.time.Instant; +import java.util.function.Function; + +/** + * Provides access to a snapshot of the platform state. + */ +public class SnapshotPlatformStateAccessor implements PlatformStateAccessor { + private final PlatformState state; + private final Function versionFactory; + + /** + * Constructs a new accessor for the given state. + * + * @param state the state to access + * @param versionFactory a factory for creating software versions + */ + public SnapshotPlatformStateAccessor( + @NonNull final PlatformState state, + @NonNull final Function versionFactory) { + this.state = requireNonNull(state); + this.versionFactory = requireNonNull(versionFactory); + } + + /** + * {@inheritDoc} + */ + @Override + @NonNull + public SoftwareVersion getCreationSoftwareVersion() { + return versionFactory.apply(stateOrThrow().creationSoftwareVersionOrThrow()); + } + + /** + * {@inheritDoc} + */ + @Override + @Nullable + public AddressBook getAddressBook() { + return fromPbjAddressBook(stateOrThrow().addressBook()); + } + + /** + * {@inheritDoc} + */ + @Override + @Nullable + public AddressBook getPreviousAddressBook() { + return fromPbjAddressBook(stateOrThrow().previousAddressBook()); + } + + /** + * {@inheritDoc} + */ + @Override + public long getRound() { + final var consensusSnapshot = stateOrThrow().consensusSnapshot(); + if (consensusSnapshot == null) { + return GENESIS_ROUND; + } else { + return consensusSnapshot.round(); + } + } + + /** + * {@inheritDoc} + */ + @Override + @Nullable + public Hash getLegacyRunningEventHash() { + final var hash = stateOrThrow().legacyRunningEventHash(); + return hash.length() == 0 ? null : new Hash(hash); + } + + /** + * {@inheritDoc} + */ + @Override + @Nullable + public Instant getConsensusTimestamp() { + final var consensusSnapshot = stateOrThrow().consensusSnapshot(); + if (consensusSnapshot == null) { + return null; + } + return fromPbjTimestamp(consensusSnapshot.consensusTimestamp()); + } + + /** + * {@inheritDoc} + */ + @Override + public long getAncientThreshold() { + final var consensusSnapshot = stateOrThrow().consensusSnapshot(); + requireNonNull(consensusSnapshot, "No minimum judge info found in state for round, snapshot is null"); + final var minimumJudgeInfos = consensusSnapshot.minimumJudgeInfoList(); + if (minimumJudgeInfos.isEmpty()) { + throw new IllegalStateException( + "No minimum judge info found in state for round " + consensusSnapshot.round() + ", list is empty"); + } + return minimumJudgeInfos.getFirst().minimumJudgeAncientThreshold(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getRoundsNonAncient() { + return stateOrThrow().roundsNonAncient(); + } + + /** + * {@inheritDoc} + */ + @Override + @Nullable + public ConsensusSnapshot getSnapshot() { + return fromPbjConsensusSnapshot(stateOrThrow().consensusSnapshot()); + } + + /** + * {@inheritDoc} + */ + @Nullable + @Override + public Instant getFreezeTime() { + return fromPbjTimestamp(stateOrThrow().freezeTime()); + } + + /** + * {@inheritDoc} + */ + @Nullable + @Override + public Instant getLastFrozenTime() { + return fromPbjTimestamp(stateOrThrow().lastFrozenTime()); + } + + /** + * {@inheritDoc} + */ + @Nullable + @Override + public SoftwareVersion getFirstVersionInBirthRoundMode() { + final var version = stateOrThrow().firstVersionInBirthRoundMode(); + return version == null ? null : versionFactory.apply(version); + } + + /** + * {@inheritDoc} + */ + @Override + public long getLastRoundBeforeBirthRoundMode() { + return stateOrThrow().lastRoundBeforeBirthRoundMode(); + } + + /** + * {@inheritDoc} + */ + @Override + public long getLowestJudgeGenerationBeforeBirthRoundMode() { + return stateOrThrow().lowestJudgeGenerationBeforeBirthRoundMode(); + } + + private @NonNull PlatformState stateOrThrow() { + return requireNonNull(state); + } +} 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 964243f9f7ae..3c0dd9ff0fe1 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 @@ -25,13 +25,13 @@ import static com.swirlds.platform.state.signed.SignedStateHistory.SignedStateAction.RESERVE; import com.swirlds.base.time.Time; -import com.swirlds.common.context.PlatformContext; import com.swirlds.common.crypto.Signature; import com.swirlds.common.platform.NodeId; import com.swirlds.common.utility.ReferenceCounter; import com.swirlds.common.utility.RuntimeObjectRecord; import com.swirlds.common.utility.RuntimeObjectRegistry; import com.swirlds.common.utility.Threshold; +import com.swirlds.config.api.Configuration; import com.swirlds.platform.config.StateConfig; import com.swirlds.platform.crypto.SignatureVerifier; import com.swirlds.platform.state.MerkleRoot; @@ -164,7 +164,7 @@ public class SignedState implements SignedStateInfo { /** * Instantiate a signed state. * - * @param platformContext the platform context + * @param configuration the configuration for this node * @param signatureVerifier the signature verifier * @param state a fast copy of the state resulting from all transactions in consensus order from * all events with received rounds up through the round this SignedState represents @@ -179,7 +179,7 @@ public class SignedState implements SignedStateInfo { * event stream */ public SignedState( - @NonNull final PlatformContext platformContext, + @NonNull final Configuration configuration, @NonNull final SignatureVerifier signatureVerifier, @NonNull final MerkleRoot state, @NonNull final String reason, @@ -192,7 +192,7 @@ public SignedState( this.signatureVerifier = Objects.requireNonNull(signatureVerifier); this.state = state; - final StateConfig stateConfig = platformContext.getConfiguration().getConfigData(StateConfig.class); + final StateConfig stateConfig = configuration.getConfigData(StateConfig.class); if (stateConfig.stateHistoryEnabled()) { history = new SignedStateHistory(Time.getCurrent(), getRound(), stateConfig.debugStackTracesEnabled()); history.recordAction(CREATION, getReservationCount(), reason, null); @@ -241,7 +241,6 @@ public boolean isGenesisState() { public void setSigSet(@NonNull final SigSet sigSet) { this.sigSet = Objects.requireNonNull(sigSet); signingWeight = 0; - state.initPlatformState(); if (!isGenesisState()) { // Only non-genesis states will have signing weight final AddressBook addressBook = getAddressBook(); diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/signed/StartupStateUtils.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/signed/StartupStateUtils.java index 56279b8c48bb..39c645df32cc 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/signed/StartupStateUtils.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/signed/StartupStateUtils.java @@ -26,12 +26,12 @@ import com.swirlds.base.function.CheckedBiFunction; import com.swirlds.common.config.StateCommonConfig; -import com.swirlds.common.context.PlatformContext; import com.swirlds.common.crypto.Hash; import com.swirlds.common.io.streams.MerkleDataInputStream; import com.swirlds.common.io.utility.RecycleBin; import com.swirlds.common.merkle.crypto.MerkleCryptoFactory; import com.swirlds.common.platform.NodeId; +import com.swirlds.config.api.Configuration; import com.swirlds.logging.legacy.payload.SavedStateLoadedPayload; import com.swirlds.platform.config.StateConfig; import com.swirlds.platform.crypto.CryptoStatic; @@ -65,7 +65,7 @@ private StartupStateUtils() {} * Get the initial state to be used by this node. May return a state loaded from disk, or may return a genesis state * if no valid state is found on disk. * - * @param platformContext the platform context + * @param configuration the configuration for this node * @param softwareVersion the software version of the app * @param genesisStateBuilder a supplier that can build a genesis state * @param snapshotStateReader a function to read an existing state snapshot @@ -79,7 +79,8 @@ private StartupStateUtils() {} */ @NonNull public static HashedReservedSignedState getInitialState( - @NonNull final PlatformContext platformContext, + @NonNull final Configuration configuration, + @NonNull final RecycleBin recycleBin, @NonNull final SoftwareVersion softwareVersion, @NonNull final Supplier genesisStateBuilder, @NonNull final CheckedBiFunction snapshotStateReader, @@ -89,14 +90,14 @@ public static HashedReservedSignedState getInitialState( @NonNull final AddressBook configAddressBook) throws SignedStateLoadingException { - requireNonNull(platformContext); + requireNonNull(configuration); requireNonNull(mainClassName); requireNonNull(swirldName); requireNonNull(selfId); requireNonNull(configAddressBook); final ReservedSignedState loadedState = StartupStateUtils.loadStateFile( - platformContext, selfId, mainClassName, swirldName, softwareVersion, snapshotStateReader); + configuration, recycleBin, selfId, mainClassName, swirldName, softwareVersion, snapshotStateReader); try (loadedState) { if (loadedState.isNotNull()) { @@ -105,22 +106,22 @@ public static HashedReservedSignedState getInitialState( new SavedStateLoadedPayload( loadedState.get().getRound(), loadedState.get().getConsensusTimestamp())); - return copyInitialSignedState(platformContext, loadedState.get()); + return copyInitialSignedState(configuration, loadedState.get()); } } final ReservedSignedState genesisState = - buildGenesisState(platformContext, configAddressBook, softwareVersion, genesisStateBuilder.get()); + buildGenesisState(configuration, configAddressBook, softwareVersion, genesisStateBuilder.get()); try (genesisState) { - return copyInitialSignedState(platformContext, genesisState.get()); + return copyInitialSignedState(configuration, genesisState.get()); } } /** * Looks at the states on disk, chooses one to load, and then loads the chosen state. * - * @param platformContext the platform context + * @param configuration the configuration for this node * @param selfId the ID of this node * @param mainClassName the name of the main class * @param swirldName the name of the swirld @@ -132,7 +133,8 @@ public static HashedReservedSignedState getInitialState( */ @NonNull static ReservedSignedState loadStateFile( - @NonNull final PlatformContext platformContext, + @NonNull final Configuration configuration, + @NonNull final RecycleBin recycleBin, @NonNull final NodeId selfId, @NonNull final String mainClassName, @NonNull final String swirldName, @@ -140,11 +142,11 @@ static ReservedSignedState loadStateFile( @NonNull final CheckedBiFunction snapshotStateReader) { - final StateConfig stateConfig = platformContext.getConfiguration().getConfigData(StateConfig.class); + final StateConfig stateConfig = configuration.getConfigData(StateConfig.class); final String actualMainClassName = stateConfig.getMainClassName(mainClassName); final List savedStateFiles = new SignedStateFilePath( - platformContext.getConfiguration().getConfigData(StateCommonConfig.class)) + configuration.getConfigData(StateCommonConfig.class)) .getSavedStateFiles(actualMainClassName, selfId, swirldName); logStatesFound(savedStateFiles); @@ -153,8 +155,8 @@ static ReservedSignedState loadStateFile( return createNullReservation(); } - final ReservedSignedState state = - loadLatestState(platformContext, currentSoftwareVersion, savedStateFiles, snapshotStateReader); + final ReservedSignedState state = loadLatestState( + configuration, recycleBin, currentSoftwareVersion, savedStateFiles, snapshotStateReader); return state; } @@ -162,18 +164,18 @@ static ReservedSignedState loadStateFile( * Create a copy of the initial signed state. There are currently data structures that become immutable after being * hashed, and we need to make a copy to force it to become mutable again. * - * @param platformContext the platform's context + * @param configuration the configuration for this node * @param initialSignedState the initial signed state * @return a copy of the initial signed state */ public static @NonNull HashedReservedSignedState copyInitialSignedState( - @NonNull final PlatformContext platformContext, @NonNull final SignedState initialSignedState) { - requireNonNull(platformContext); + @NonNull final Configuration configuration, @NonNull final SignedState initialSignedState) { + requireNonNull(configuration); requireNonNull(initialSignedState); final MerkleRoot stateCopy = initialSignedState.getState().copy(); final SignedState signedStateCopy = new SignedState( - platformContext, + configuration, CryptoStatic::verifySignature, stateCopy, "StartupStateUtils: copy initial state", @@ -215,7 +217,8 @@ private static void logStatesFound(@NonNull final List savedStat * @return the loaded state */ private static ReservedSignedState loadLatestState( - @NonNull final PlatformContext platformContext, + @NonNull final Configuration configuration, + @NonNull final RecycleBin recycleBin, @NonNull final SoftwareVersion currentSoftwareVersion, @NonNull final List savedStateFiles, @NonNull final CheckedBiFunction snapshotStateReader) @@ -224,8 +227,8 @@ private static ReservedSignedState loadLatestState( logger.info(STARTUP.getMarker(), "Loading latest state from disk."); for (final SavedStateInfo savedStateFile : savedStateFiles) { - final ReservedSignedState state = - loadStateFile(platformContext, currentSoftwareVersion, savedStateFile, snapshotStateReader); + final ReservedSignedState state = loadStateFile( + configuration, recycleBin, currentSoftwareVersion, savedStateFile, snapshotStateReader); if (state != null) { return state; } @@ -246,7 +249,8 @@ private static ReservedSignedState loadLatestState( */ @Nullable private static ReservedSignedState loadStateFile( - @NonNull final PlatformContext platformContext, + @NonNull final Configuration configuration, + @NonNull final RecycleBin recycleBin, @NonNull final SoftwareVersion currentSoftwareVersion, @NonNull final SavedStateInfo savedStateFile, @NonNull final CheckedBiFunction snapshotStateReader) @@ -256,13 +260,13 @@ private static ReservedSignedState loadStateFile( final DeserializedSignedState deserializedSignedState; try { - deserializedSignedState = readStateFile(platformContext, savedStateFile.stateFile(), snapshotStateReader); + deserializedSignedState = readStateFile(configuration, savedStateFile.stateFile(), snapshotStateReader); } catch (final IOException e) { logger.error(EXCEPTION.getMarker(), "unable to load state file {}", savedStateFile.stateFile(), e); - final StateConfig stateConfig = platformContext.getConfiguration().getConfigData(StateConfig.class); + final StateConfig stateConfig = configuration.getConfigData(StateConfig.class); if (stateConfig.deleteInvalidStateFiles()) { - recycleState(platformContext.getRecycleBin(), savedStateFile); + recycleState(recycleBin, savedStateFile); return null; } else { throw new SignedStateLoadingException("unable to load state, this is unrecoverable"); diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/SignedStateFileReader.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/SignedStateFileReader.java index d9452361fbf2..e8fd71090d86 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/SignedStateFileReader.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/snapshot/SignedStateFileReader.java @@ -32,6 +32,7 @@ import com.swirlds.common.crypto.Hash; import com.swirlds.common.io.streams.MerkleDataInputStream; import com.swirlds.common.platform.NodeId; +import com.swirlds.config.api.Configuration; import com.swirlds.platform.crypto.CryptoStatic; import com.swirlds.platform.state.MerkleRoot; import com.swirlds.platform.state.signed.SigSet; @@ -86,19 +87,19 @@ public record StateFileData( * Reads a SignedState from disk using the provided snapshot reader function. If the reader throws * an exception, it is propagated by this method to the caller. * - * @param platformContext the platform context + * @param configuration the configuration for this node * @param stateFile the file to read from * @param snapshotStateReader state snapshot reading function * @return a signed state with it's associated hash (as computed when the state was serialized) * @throws IOException if there is any problems with reading from a file */ public static @NonNull DeserializedSignedState readStateFile( - @NonNull final PlatformContext platformContext, + @NonNull final Configuration configuration, @NonNull final Path stateFile, @NonNull final CheckedBiFunction snapshotStateReader) throws IOException { - Objects.requireNonNull(platformContext); + Objects.requireNonNull(configuration); Objects.requireNonNull(stateFile); checkSignedStatePath(stateFile); @@ -122,7 +123,7 @@ public record StateFileData( } final SignedState newSignedState = new SignedState( - platformContext, + configuration, CryptoStatic::verifySignature, normalizedData.state, "SignedStateFileReader.readStateFile()", 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 b3f598340ba5..3d2e225b429f 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 @@ -144,8 +144,10 @@ void writeThenReadStateFileTest() throws IOException { assertTrue(exists(stateFile), "signed state file should be present"); assertTrue(exists(signatureSetFile), "signature set file should be present"); - final DeserializedSignedState deserializedSignedState = - readStateFile(TestPlatformContextBuilder.create().build(), stateFile, SignedStateFileUtils::readState); + final DeserializedSignedState deserializedSignedState = readStateFile( + TestPlatformContextBuilder.create().build().getConfiguration(), + stateFile, + SignedStateFileUtils::readState); MerkleCryptoFactory.getInstance() .digestTreeSync( deserializedSignedState.reservedSignedState().get().getState()); @@ -191,8 +193,10 @@ void writeThenReadStateFileTest_v1() throws IOException { assertTrue(exists(stateFile), "signed state file should be present"); - final DeserializedSignedState deserializedSignedState = - readStateFile(TestPlatformContextBuilder.create().build(), stateFile, SignedStateFileUtils::readState); + final DeserializedSignedState deserializedSignedState = readStateFile( + TestPlatformContextBuilder.create().build().getConfiguration(), + stateFile, + SignedStateFileUtils::readState); MerkleCryptoFactory.getInstance() .digestTreeSync( deserializedSignedState.reservedSignedState().get().getState()); 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 bb8ae2ec9fd5..551c17a9cb26 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 @@ -146,8 +146,10 @@ private void validateSavingOfState(final SignedState originalState, final Path s assertEquals(-1, originalState.getReservationCount(), "invalid reservation count"); - final DeserializedSignedState deserializedSignedState = - readStateFile(TestPlatformContextBuilder.create().build(), stateFile, SignedStateFileUtils::readState); + final DeserializedSignedState deserializedSignedState = readStateFile( + TestPlatformContextBuilder.create().build().getConfiguration(), + stateFile, + SignedStateFileUtils::readState); MerkleCryptoFactory.getInstance() .digestTreeSync( deserializedSignedState.reservedSignedState().get().getState()); @@ -341,7 +343,9 @@ void sequenceOfStatesTest(final boolean startAtGenesis) throws IOException { final SignedState stateFromDisk = assertDoesNotThrow( () -> SignedStateFileReader.readStateFile( - TestPlatformContextBuilder.create().build(), + TestPlatformContextBuilder.create() + .build() + .getConfiguration(), savedStateInfo.stateFile(), SignedStateFileUtils::readState) .reservedSignedState() diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/MerkleStateRootTest.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/MerkleStateRootTest.java index d8314a0aaff9..1e7ea909afc8 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/MerkleStateRootTest.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/MerkleStateRootTest.java @@ -26,9 +26,6 @@ import static com.swirlds.state.merkle.StateUtils.computeLabel; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; @@ -40,7 +37,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import com.hedera.hapi.block.stream.output.StateChanges; import com.hedera.hapi.platform.state.PlatformState; import com.swirlds.base.state.MutabilityException; import com.swirlds.common.context.PlatformContext; @@ -54,8 +50,6 @@ import com.swirlds.config.api.ConfigurationBuilder; import com.swirlds.merkle.map.MerkleMap; import com.swirlds.platform.state.service.PlatformStateService; -import com.swirlds.platform.state.service.ReadablePlatformStateStore; -import com.swirlds.platform.state.service.WritablePlatformStateStore; import com.swirlds.platform.state.service.schemas.V0540PlatformStateSchema; import com.swirlds.platform.system.InitTrigger; import com.swirlds.platform.system.Platform; @@ -107,10 +101,6 @@ class MerkleStateRootTest extends MerkleTestBase { private final AtomicBoolean onUpdateWeightCalled = new AtomicBoolean(false); private final MerkleStateLifecycles lifecycles = new MerkleStateLifecycles() { - @Override - public List initPlatformState(@NonNull final State state) { - return FAKE_MERKLE_STATE_LIFECYCLES.initPlatformState(state); - } @Override public void onSealConsensusRound(@NonNull Round round, @NonNull State state) { @@ -158,6 +148,7 @@ void setUp() { FakeMerkleStateLifecycles.registerMerkleStateRootClassIds(); setupFruitMerkleMap(); stateRoot = new MerkleStateRoot(lifecycles, softwareVersionSupplier); + FAKE_MERKLE_STATE_LIFECYCLES.initPlatformState(stateRoot); } /** Looks for a merkle node with the given label */ @@ -213,7 +204,7 @@ void addingBadServiceNodeNameThrows() { @DisplayName("Adding a service") void addingService() { stateRoot.putServiceStateIfAbsent(fruitMetadata, () -> fruitMerkleMap); - assertThat(stateRoot.getNumberOfChildren()).isEqualTo(1); + assertThat(stateRoot.getNumberOfChildren()).isEqualTo(2); assertThat(getNodeForLabel(fruitLabel)).isSameAs(fruitMerkleMap); } @@ -227,7 +218,7 @@ void addingVirtualMapService() { stateRoot.putServiceStateIfAbsent(fruitMetadata, () -> fruitVirtualMap); // Then we can see it is on the tree - assertThat(stateRoot.getNumberOfChildren()).isEqualTo(1); + assertThat(stateRoot.getNumberOfChildren()).isEqualTo(2); assertThat(getNodeForLabel(fruitLabel)).isSameAs(fruitVirtualMap); } @@ -241,7 +232,7 @@ void addingSingletonService() { stateRoot.putServiceStateIfAbsent(countryMetadata, () -> countrySingleton); // Then we can see it is on the tree - assertThat(stateRoot.getNumberOfChildren()).isEqualTo(1); + assertThat(stateRoot.getNumberOfChildren()).isEqualTo(2); assertThat(getNodeForLabel(countryLabel)).isSameAs(countrySingleton); } @@ -255,7 +246,7 @@ void addingQueueService() { stateRoot.putServiceStateIfAbsent(steamMetadata, () -> steamQueue); // Then we can see it is on the tree - assertThat(stateRoot.getNumberOfChildren()).isEqualTo(1); + assertThat(stateRoot.getNumberOfChildren()).isEqualTo(2); assertThat(getNodeForLabel(steamLabel)).isSameAs(steamQueue); } @@ -273,7 +264,7 @@ void addingServiceWhenNonServiceNodeChildrenExist() { void addingServiceTwiceIsIdempotent() { stateRoot.putServiceStateIfAbsent(fruitMetadata, () -> fruitMerkleMap); stateRoot.putServiceStateIfAbsent(fruitMetadata, () -> fruitMerkleMap); - assertThat(stateRoot.getNumberOfChildren()).isEqualTo(1); + assertThat(stateRoot.getNumberOfChildren()).isEqualTo(2); assertThat(getNodeForLabel(fruitLabel)).isSameAs(fruitMerkleMap); } @@ -287,7 +278,7 @@ void addingServiceTwiceWithDifferentNodesDoesNotReplaceFirstNode() { stateRoot.putServiceStateIfAbsent(fruitMetadata, () -> map2); // Then the original node is kept and the second node ignored - assertThat(stateRoot.getNumberOfChildren()).isEqualTo(1); + assertThat(stateRoot.getNumberOfChildren()).isEqualTo(2); assertThat(getNodeForLabel(fruitLabel)).isSameAs(fruitMerkleMap); } @@ -305,7 +296,7 @@ void addingServiceTwiceWithDifferentMetadata() { stateRoot.putServiceStateIfAbsent(fruitMetadata2, () -> fruitMerkleMap); // Then the original node is kept and the second node ignored - assertThat(stateRoot.getNumberOfChildren()).isEqualTo(1); + assertThat(stateRoot.getNumberOfChildren()).isEqualTo(2); assertThat(getNodeForLabel(fruitLabel)).isSameAs(fruitMerkleMap); // NOTE: I don't have a good way to test that the metadata is intact... @@ -479,7 +470,7 @@ void readMissingState() { // Given a State with the fruit merkle map, which somehow has // lost the merkle node (this should NEVER HAPPEN in real life!) stateRoot.putServiceStateIfAbsent(fruitMetadata, () -> fruitMerkleMap); - stateRoot.setChild(0, null); + stateRoot.setChild(1, null); // When we get the ReadableStates final var states = stateRoot.getReadableStates(FIRST_SERVICE); @@ -663,7 +654,7 @@ void readMissingState() { // Given a State with the fruit virtual map, which somehow has // lost the merkle node (this should NEVER HAPPEN in real life!) stateRoot.putServiceStateIfAbsent(fruitMetadata, () -> fruitMerkleMap); - stateRoot.setChild(0, null); + stateRoot.setChild(1, null); // When we get the WritableStates final var states = stateRoot.getWritableStates(FIRST_SERVICE); @@ -988,85 +979,6 @@ void migrate_currentVersion() { verifyNoMoreInteractions(node1, node2); } - @Test - @DisplayName("Migrate from the previous version (the platform state is the zeroth child)") - void migrate_platform_state_zeroth_child() { - com.swirlds.platform.state.PlatformState platformState = (com.swirlds.platform.state.PlatformState) - randomPlatformState(new com.swirlds.platform.state.PlatformState()); - stateRoot.setChild(0, platformState); - assertNull(stateRoot.getPreV054PlatformState()); - - var node1 = mock(MerkleNode.class); - var node1Copy = mock(MerkleNode.class); - when(node1.copy()).thenReturn(node1Copy); - stateRoot.setChild(1, node1); - - var node2 = mock(MerkleNode.class); - var node2Copy = mock(MerkleNode.class); - when(node2.copy()).thenReturn(node2Copy); - stateRoot.setChild(2, node2); - - assertSame(stateRoot, stateRoot.migrate(CURRENT_VERSION - 1)); - - // Platform state is not a part of the tree temporarily - assertEquals(2, stateRoot.getNumberOfChildren()); - verify(node1).release(); - verify(node2).release(); - - assertEquals(node1Copy, stateRoot.getChild(0)); - assertEquals(node2Copy, stateRoot.getChild(1)); - - // Platform state is temporarily stored in a separate field - assertEquals(platformState, stateRoot.getPreV054PlatformState()); - - assertFalse(stateRoot.isImmutable()); - - // MerkleStateRoot registers the platform state as a singleton upon the first request to it - assertInstanceOf(WritablePlatformStateStore.class, stateRoot.getWritablePlatformState()); - assertInstanceOf(ReadablePlatformStateStore.class, stateRoot.getReadablePlatformState()); - assertEquals(toPbjPlatformState(platformState), toPbjPlatformState(stateRoot.getWritablePlatformState())); - assertEquals(toPbjPlatformState(platformState), toPbjPlatformState(stateRoot.getReadablePlatformState())); - } - - @Test - @DisplayName("Migrate from the previous version (platform state is the last child)") - void migrate_platform_state_last_child() { - com.swirlds.platform.state.PlatformState platformState = (com.swirlds.platform.state.PlatformState) - randomPlatformState(new com.swirlds.platform.state.PlatformState()); - - var node1 = mock(MerkleNode.class); - var node1Copy = mock(MerkleNode.class); - when(node1.copy()).thenReturn(node1Copy); - stateRoot.setChild(0, node1); - - var node2 = mock(MerkleNode.class); - var node2Copy = mock(MerkleNode.class); - when(node2.copy()).thenReturn(node2Copy); - stateRoot.setChild(1, node2); - - stateRoot.setChild(2, platformState); - assertNull(stateRoot.getPreV054PlatformState()); - - assertSame(stateRoot, stateRoot.migrate(CURRENT_VERSION - 1)); - - // Platform state is not a part of the tree temporarily - assertEquals(2, stateRoot.getNumberOfChildren()); - verify(node1).release(); - verify(node2).release(); - - assertEquals(node1Copy, stateRoot.getChild(0)); - assertEquals(node2Copy, stateRoot.getChild(1)); - - // Platform state is temporarily stored in a separate field - assertEquals(platformState, stateRoot.getPreV054PlatformState()); - - assertFalse(stateRoot.isImmutable()); - - // MerkleStateRoot registers the platform state as a singleton upon the first request to it - assertInstanceOf(WritablePlatformStateStore.class, stateRoot.getWritablePlatformState()); - assertEquals(toPbjPlatformState(platformState), toPbjPlatformState(stateRoot.getWritablePlatformState())); - } - @Test @DisplayName("Migrate fails if the platform state is absent") void migrate_platform_absent() { diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/SignedStateTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/SignedStateTests.java index 47b3146121b7..6c40ae523e32 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/SignedStateTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/SignedStateTests.java @@ -61,8 +61,10 @@ private SignedState generateSignedState(final Random random, final MerkleStateRo * @param releaseCallback this method is called when the State is released */ private MerkleStateRoot buildMockState(final Runnable reserveCallback, final Runnable releaseCallback) { - final MerkleStateRoot state = spy(new MerkleStateRoot( - FAKE_MERKLE_STATE_LIFECYCLES, version -> new BasicSoftwareVersion(version.major()))); + final var real = + new MerkleStateRoot(FAKE_MERKLE_STATE_LIFECYCLES, version -> new BasicSoftwareVersion(version.major())); + FAKE_MERKLE_STATE_LIFECYCLES.initPlatformState(real); + final MerkleStateRoot state = spy(real); final PlatformStateModifier platformState = new PlatformState(); platformState.setAddressBook(mock(AddressBook.class)); @@ -206,14 +208,14 @@ void noGarbageCollectorTest() { @Test @DisplayName("Alternate Constructor Reservations Test") void alternateConstructorReservationsTest() { - final MerkleRoot state = spy(new MerkleStateRoot( + final MerkleStateRoot state = spy(new MerkleStateRoot( FAKE_MERKLE_STATE_LIFECYCLES, version -> new BasicSoftwareVersion(version.major()))); final PlatformStateModifier platformState = mock(PlatformStateModifier.class); - state.initPlatformState(); + FAKE_MERKLE_STATE_LIFECYCLES.initPlatformState(state); when(state.getReadablePlatformState()).thenReturn(platformState); when(platformState.getRound()).thenReturn(0L); final SignedState signedState = new SignedState( - TestPlatformContextBuilder.create().build(), + TestPlatformContextBuilder.create().build().getConfiguration(), mock(SignatureVerifier.class), state, "test", diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateRegistryTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateRegistryTests.java index 666e81660311..84be53777929 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateRegistryTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/StateRegistryTests.java @@ -108,6 +108,7 @@ void activeStateCountTest() throws IOException { // Deserialize a state final MerkleStateRoot stateToSerialize = new MerkleStateRoot(FAKE_MERKLE_STATE_LIFECYCLES, softwareVersionSupplier); + FAKE_MERKLE_STATE_LIFECYCLES.initPlatformState(stateToSerialize); final var platformState = stateToSerialize.getWritablePlatformState(); platformState.bulkUpdate(v -> { v.setCreationSoftwareVersion(new BasicSoftwareVersion(version.minor())); diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/SwirldStateManagerTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/SwirldStateManagerTests.java index d12ba13d50f9..ac53e553d415 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/SwirldStateManagerTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/SwirldStateManagerTests.java @@ -122,6 +122,7 @@ void loadFromSignedStateRefCount() { private static MerkleRoot newState() { final MerkleStateRoot state = new MerkleStateRoot(FAKE_MERKLE_STATE_LIFECYCLES, version -> new BasicSoftwareVersion(version.major())); + FAKE_MERKLE_STATE_LIFECYCLES.initPlatformState(state); final PlatformStateModifier platformState = mock(PlatformStateModifier.class); when(platformState.getCreationSoftwareVersion()).thenReturn(new BasicSoftwareVersion(nextInt(1, 100))); diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/SwirldStateManagerUtilsTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/SwirldStateManagerUtilsTests.java index 90214c40db7a..25c941eaa90c 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/SwirldStateManagerUtilsTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/state/SwirldStateManagerUtilsTests.java @@ -36,6 +36,7 @@ void testFastCopyIsMutable() { final MerkleStateRoot state = new MerkleStateRoot(FAKE_MERKLE_STATE_LIFECYCLES, version -> new BasicSoftwareVersion(version.major())); + FAKE_MERKLE_STATE_LIFECYCLES.initPlatformState(state); state.reserve(); final SwirldStateMetrics stats = mock(SwirldStateMetrics.class); final MerkleRoot result = SwirldStateManagerUtils.fastCopy(state, stats, new BasicSoftwareVersion(1)); 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 74e03b151b26..fb941596ad26 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 @@ -17,6 +17,7 @@ package com.swirlds.platform.state.signed; import static com.swirlds.common.test.fixtures.RandomUtils.getRandomPrintSeed; +import static com.swirlds.common.threading.manager.AdHocThreadManager.getStaticThreadManager; import static com.swirlds.platform.state.snapshot.SignedStateFileWriter.writeSignedStateToDisk; import static com.swirlds.platform.test.fixtures.state.FakeMerkleStateLifecycles.FAKE_MERKLE_STATE_LIFECYCLES; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -26,6 +27,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.spy; +import com.swirlds.base.time.Time; import com.swirlds.common.config.StateCommonConfig; import com.swirlds.common.config.StateCommonConfig_; import com.swirlds.common.constructable.ClassConstructorPair; @@ -33,8 +35,10 @@ import com.swirlds.common.constructable.ConstructableRegistryException; import com.swirlds.common.context.PlatformContext; import com.swirlds.common.crypto.Hash; +import com.swirlds.common.io.filesystem.FileSystemManager; import com.swirlds.common.io.utility.FileUtils; import com.swirlds.common.io.utility.RecycleBin; +import com.swirlds.common.metrics.noop.NoOpMetrics; import com.swirlds.common.platform.NodeId; import com.swirlds.common.test.fixtures.TestRecycleBin; import com.swirlds.common.test.fixtures.platform.TestPlatformContextBuilder; @@ -161,8 +165,10 @@ private SignedState writeState( void genesisTest() throws SignedStateLoadingException { final PlatformContext platformContext = buildContext(false, TestRecycleBin.getInstance()); + final RecycleBin recycleBin = initializeRecycleBin(platformContext, selfId); final SignedState loadedState = StartupStateUtils.loadStateFile( - platformContext, + platformContext.getConfiguration(), + recycleBin, selfId, mainClassName, swirldName, @@ -188,8 +194,10 @@ void normalRestartTest() throws IOException, SignedStateLoadingException { latestState = writeState(random, platformContext, latestRound, null, false); } + final RecycleBin recycleBin = initializeRecycleBin(platformContext, selfId); final SignedState loadedState = StartupStateUtils.loadStateFile( - platformContext, + platformContext.getConfiguration(), + recycleBin, selfId, mainClassName, swirldName, @@ -218,9 +226,11 @@ void corruptedStateNoRecyclingTest() throws IOException { final boolean corrupted = i == stateCount - 1; writeState(random, platformContext, latestRound, null, corrupted); } + final RecycleBin recycleBin = initializeRecycleBin(platformContext, selfId); assertThrows(SignedStateLoadingException.class, () -> StartupStateUtils.loadStateFile( - platformContext, + platformContext.getConfiguration(), + recycleBin, selfId, mainClassName, swirldName, @@ -263,7 +273,8 @@ void corruptedStateRecyclingPermittedTest(final int invalidStateCount) } final SignedState loadedState = StartupStateUtils.loadStateFile( - platformContext, + platformContext.getConfiguration(), + recycleBin, selfId, mainClassName, swirldName, @@ -290,4 +301,12 @@ void corruptedStateRecyclingPermittedTest(final int invalidStateCount) assertEquals(5 - invalidStateCount, Files.list(savedStateDirectory).count()); assertEquals(invalidStateCount, recycleCount.get()); } + + private RecycleBin initializeRecycleBin(PlatformContext platformContext, NodeId selfId) { + final var metrics = new NoOpMetrics(); + final var configuration = platformContext.getConfiguration(); + final var fileSystemManager = FileSystemManager.create(configuration); + final var time = Time.getCurrent(); + return RecycleBin.create(metrics, configuration, getStaticThreadManager(), time, fileSystemManager, selfId); + } } diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/turtle/runner/TurtleNode.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/turtle/runner/TurtleNode.java index 2f44d980b110..061548c6468c 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/turtle/runner/TurtleNode.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/turtle/runner/TurtleNode.java @@ -16,11 +16,15 @@ package com.swirlds.platform.turtle.runner; +import static com.swirlds.common.threading.manager.AdHocThreadManager.getStaticThreadManager; +import static com.swirlds.platform.builder.internal.StaticPlatformBuilder.getMetricsProvider; import static com.swirlds.platform.state.signed.StartupStateUtils.getInitialState; import static com.swirlds.platform.system.address.AddressBookUtils.createRoster; import com.swirlds.base.time.Time; import com.swirlds.common.context.PlatformContext; +import com.swirlds.common.io.filesystem.FileSystemManager; +import com.swirlds.common.io.utility.RecycleBin; import com.swirlds.common.platform.NodeId; import com.swirlds.common.test.fixtures.Randotron; import com.swirlds.common.test.fixtures.platform.TestPlatformContextBuilder; @@ -97,8 +101,16 @@ public class TurtleNode { .build(); final Supplier genesisStateSupplier = TurtleTestingToolState::getStateRootNode; final var version = new BasicSoftwareVersion(1); + + final NodeId selfId = null; + final var metrics = getMetricsProvider().createPlatformMetrics(selfId); + final var fileSystemManager = FileSystemManager.create(configuration); + final var recycleBin = + RecycleBin.create(metrics, configuration, getStaticThreadManager(), time, fileSystemManager, selfId); + final var reservedState = getInitialState( - platformContext, + configuration, + recycleBin, version, genesisStateSupplier, SignedStateFileUtils::readState, diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/wiring/SignedStateReserverTest.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/wiring/SignedStateReserverTest.java index 908cb6787a6a..2ac17245facf 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/wiring/SignedStateReserverTest.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/wiring/SignedStateReserverTest.java @@ -49,7 +49,7 @@ void basicTest() { TestPlatformContextBuilder.create().build(); final SignedState signedState = new SignedState( - platformContext, + platformContext.getConfiguration(), Mockito.mock(SignatureVerifier.class), Mockito.mock(MerkleRoot.class), "create", diff --git a/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/state/FakeMerkleStateLifecycles.java b/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/state/FakeMerkleStateLifecycles.java index ae9eba87a888..32ed944f8acf 100644 --- a/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/state/FakeMerkleStateLifecycles.java +++ b/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/state/FakeMerkleStateLifecycles.java @@ -88,7 +88,6 @@ public static void registerMerkleStateRootClassIds() { } } - @Override public List initPlatformState(@NonNull final State state) { if (!(state instanceof MerkleStateRoot merkleStateRoot)) { throw new IllegalArgumentException("Can only be used with MerkleStateRoot instances"); diff --git a/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/state/RandomSignedStateGenerator.java b/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/state/RandomSignedStateGenerator.java index 24583d91829c..b627967d6207 100644 --- a/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/state/RandomSignedStateGenerator.java +++ b/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/state/RandomSignedStateGenerator.java @@ -187,7 +187,7 @@ public SignedState build() { } else { consensusSnapshotInstance = consensusSnapshot; } - + FAKE_MERKLE_STATE_LIFECYCLES.initPlatformState((MerkleStateRoot) stateInstance); final PlatformStateModifier platformState = stateInstance.getWritablePlatformState(); platformState.bulkUpdate(v -> { @@ -212,7 +212,7 @@ public SignedState build() { .build(); final SignedState signedState = new SignedState( - platformContext, + configuration, signatureVerifier, stateInstance, "RandomSignedStateGenerator.build()", diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/com/swirlds/platform/test/SignedStateUtils.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/com/swirlds/platform/test/SignedStateUtils.java index cc6cd99e73b0..3186bcab161c 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/com/swirlds/platform/test/SignedStateUtils.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/com/swirlds/platform/test/SignedStateUtils.java @@ -39,7 +39,7 @@ public static SignedState randomSignedState(Random random) { randomPlatformState(random, root.getWritablePlatformState()); boolean shouldSaveToDisk = random.nextBoolean(); SignedState signedState = new SignedState( - TestPlatformContextBuilder.create().build(), + TestPlatformContextBuilder.create().build().getConfiguration(), CryptoStatic::verifySignature, root, "test", diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/StateTest.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/StateTest.java index b10df3e51d89..beb1563b8d32 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/StateTest.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/StateTest.java @@ -91,7 +91,7 @@ private static SignedState randomSignedState() { FAKE_MERKLE_STATE_LIFECYCLES, version -> new BasicSoftwareVersion(version.major()))); boolean shouldSaveToDisk = random.nextBoolean(); SignedState signedState = new SignedState( - TestPlatformContextBuilder.create().build(), + TestPlatformContextBuilder.create().build().getConfiguration(), CryptoStatic::verifySignature, root, "test",