From a331906ec80e93690bd291ecbf4305c6f2772c89 Mon Sep 17 00:00:00 2001 From: Lazar Petrovic Date: Tue, 28 Mar 2023 15:20:45 +0200 Subject: [PATCH 01/13] fallen behind change (#5848) Signed-off-by: Lazar Petrovic --- .../platform/sync/SyncFallenBehindStatus.java | 4 ++-- .../platform/test/sync/SyncTestExecutor.java | 20 ++----------------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/sync/SyncFallenBehindStatus.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/sync/SyncFallenBehindStatus.java index b89b9afb756d..021957500666 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/sync/SyncFallenBehindStatus.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/sync/SyncFallenBehindStatus.java @@ -24,10 +24,10 @@ public enum SyncFallenBehindStatus { OTHER_FALLEN_BEHIND; public static SyncFallenBehindStatus getStatus(final GraphGenerations self, final GraphGenerations other) { - if (other.getMaxRoundGeneration() < self.getMinRoundGeneration()) { + if (other.getMaxRoundGeneration() < self.getMinGenerationNonAncient()) { return OTHER_FALLEN_BEHIND; } - if (self.getMaxRoundGeneration() < other.getMinRoundGeneration()) { + if (self.getMaxRoundGeneration() < other.getMinGenerationNonAncient()) { return SELF_FALLEN_BEHIND; } return NONE_FALLEN_BEHIND; diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/sync/SyncTestExecutor.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/sync/SyncTestExecutor.java index 56163b32b6bd..3b8bb51f3af3 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/sync/SyncTestExecutor.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/sync/SyncTestExecutor.java @@ -180,29 +180,13 @@ private BiConsumer getDefaultGenerationDefinitions() { long callerMinGen = SyncUtils.getMinGen(caller.getShadowGraph().findAncestors(callerTips, (e) -> true)); long callerMaxGen = SyncUtils.getMaxGen(callerTips); - long listenerMinNonAncientGen = listenerMinGen; - double listenerDif = listenerMaxGen - listenerMinGen; - if (listenerDif >= 3) { - listenerMinNonAncientGen += Math.floor(listenerDif / 3); - } else if (listenerDif == 2) { - listenerMinNonAncientGen++; - } - - long callerMinNonAncientGen = callerMinGen; - double callerDif = callerMaxGen - callerMinGen; - if (callerDif >= 3) { - callerMinNonAncientGen += Math.floor(callerDif / 3); - } else if (callerDif == 2) { - callerMinNonAncientGen++; - } - when(caller.getConsensus().getMaxRoundGeneration()).thenReturn(callerMaxGen); when(caller.getConsensus().getMinRoundGeneration()).thenReturn(callerMinGen); - when(caller.getConsensus().getMinGenerationNonAncient()).thenReturn(callerMinNonAncientGen); + when(caller.getConsensus().getMinGenerationNonAncient()).thenReturn(callerMinGen); when(listener.getConsensus().getMaxRoundGeneration()).thenReturn(listenerMaxGen); when(listener.getConsensus().getMinRoundGeneration()).thenReturn(listenerMinGen); - when(listener.getConsensus().getMinGenerationNonAncient()).thenReturn(listenerMinNonAncientGen); + when(listener.getConsensus().getMinGenerationNonAncient()).thenReturn(listenerMinGen); }; } From cd30f400e69f01a3c37bbd055f594f2414605630 Mon Sep 17 00:00:00 2001 From: artemananiev <33361937+artemananiev@users.noreply.github.com> Date: Tue, 28 Mar 2023 10:28:04 -0700 Subject: [PATCH 02/13] 5314: JasperDB to MerkleDb: logs and settings (#5744) Fixes: https://github.com/hashgraph/hedera-services/issues/5314 Signed-off-by: Artem Ananev --- hedera-node/configuration/dev/bootstrap.properties | 1 + hedera-node/configuration/dev/log4j2.xml | 9 ++++++--- hedera-node/configuration/dev/settings.txt | 1 + hedera-node/configuration/mainnet/log4j2.xml | 9 ++++++--- hedera-node/configuration/mainnet/settings.txt | 2 +- hedera-node/configuration/preprod/log4j2.xml | 9 ++++++--- hedera-node/configuration/preprod/settings.txt | 2 +- hedera-node/configuration/previewnet/log4j2.xml | 9 ++++++--- hedera-node/configuration/previewnet/settings.txt | 2 +- hedera-node/configuration/testnet/log4j2.xml | 9 ++++++--- hedera-node/configuration/testnet/settings.txt | 2 +- .../hedera-app/src/test/resources/bootstrap.properties | 2 +- .../service/mono/contracts/SizeLimitedStorageBench.java | 2 +- .../node/app/service/mono/setup/Constructables.java | 6 +++++- .../mono/context/properties/BootstrapPropertiesTest.java | 2 +- .../src/test/resources/bootstrap.properties | 2 +- .../src/test/resources/bootstrap/standard.properties | 2 +- 17 files changed, 46 insertions(+), 25 deletions(-) diff --git a/hedera-node/configuration/dev/bootstrap.properties b/hedera-node/configuration/dev/bootstrap.properties index 34525d2b7a8e..eeb1787fc55b 100644 --- a/hedera-node/configuration/dev/bootstrap.properties +++ b/hedera-node/configuration/dev/bootstrap.properties @@ -1 +1,2 @@ bootstrap.throttleDefsJson.resource=throttles-dev.json +virtualdatasource.jasperdbToMerkledb=true diff --git a/hedera-node/configuration/dev/log4j2.xml b/hedera-node/configuration/dev/log4j2.xml index acbfe5d1d54c..d91738da6be3 100644 --- a/hedera-node/configuration/dev/log4j2.xml +++ b/hedera-node/configuration/dev/log4j2.xml @@ -73,8 +73,9 @@ - + + @@ -82,8 +83,9 @@ - + + @@ -183,8 +185,9 @@ - + + diff --git a/hedera-node/configuration/dev/settings.txt b/hedera-node/configuration/dev/settings.txt index 1d6811b0e1a5..108a14cb28fd 100644 --- a/hedera-node/configuration/dev/settings.txt +++ b/hedera-node/configuration/dev/settings.txt @@ -16,5 +16,6 @@ state.signedStateKeep, 10 useLoopbackIp, false waitAtStartup, false jasperDb.iteratorInputBufferBytes, 16777216 +merkleDb.iteratorInputBufferBytes, 16777216 virtualMap.preferredFlushQueueSize, 10000 transThrottle, false \ No newline at end of file diff --git a/hedera-node/configuration/mainnet/log4j2.xml b/hedera-node/configuration/mainnet/log4j2.xml index 47011e950a43..0a7f268154e7 100644 --- a/hedera-node/configuration/mainnet/log4j2.xml +++ b/hedera-node/configuration/mainnet/log4j2.xml @@ -72,8 +72,9 @@ - + + @@ -81,8 +82,9 @@ - + + @@ -181,8 +183,9 @@ - + + diff --git a/hedera-node/configuration/mainnet/settings.txt b/hedera-node/configuration/mainnet/settings.txt index 32eb1e96669c..94a518e7073c 100644 --- a/hedera-node/configuration/mainnet/settings.txt +++ b/hedera-node/configuration/mainnet/settings.txt @@ -23,8 +23,8 @@ state.signedStateKeep, 10 throttle7extra, 0.5 useLoopbackIp, false waitAtStartup, false -jasperDb.storagePath, /opt/hgcapp/services-hedera/HapiApp2.0/data/saved jasperDb.iteratorInputBufferBytes, 16777216 +merkleDb.iteratorInputBufferBytes, 16777216 virtualMap.preferredFlushQueueSize, 10000 state.mainClassNameOverride, com.hedera.services.ServicesMain prometheusEndpointEnabled, true diff --git a/hedera-node/configuration/preprod/log4j2.xml b/hedera-node/configuration/preprod/log4j2.xml index 8f75276a0043..48fd7e55cdb3 100644 --- a/hedera-node/configuration/preprod/log4j2.xml +++ b/hedera-node/configuration/preprod/log4j2.xml @@ -133,8 +133,9 @@ - + + @@ -142,8 +143,9 @@ - + + @@ -243,8 +245,9 @@ - + + diff --git a/hedera-node/configuration/preprod/settings.txt b/hedera-node/configuration/preprod/settings.txt index 2dfcc2f97d13..49d3a05d973f 100644 --- a/hedera-node/configuration/preprod/settings.txt +++ b/hedera-node/configuration/preprod/settings.txt @@ -23,8 +23,8 @@ state.signedStateKeep, 10 throttle7extra, 0.5 useLoopbackIp, false waitAtStartup, false -jasperDb.storagePath, /opt/hgcapp/services-hedera/HapiApp2.0/data/saved jasperDb.iteratorInputBufferBytes, 16777216 +merkleDb.iteratorInputBufferBytes, 16777216 virtualMap.preferredFlushQueueSize, 10000 prometheusEndpointEnabled, true state.mainClassNameOverride, com.hedera.services.ServicesMain diff --git a/hedera-node/configuration/previewnet/log4j2.xml b/hedera-node/configuration/previewnet/log4j2.xml index 72d2ce969f2e..81ce6230aaad 100644 --- a/hedera-node/configuration/previewnet/log4j2.xml +++ b/hedera-node/configuration/previewnet/log4j2.xml @@ -73,8 +73,9 @@ - + + @@ -82,8 +83,9 @@ - + + @@ -183,8 +185,9 @@ - + + diff --git a/hedera-node/configuration/previewnet/settings.txt b/hedera-node/configuration/previewnet/settings.txt index 689997563c47..1cc4f71484fd 100644 --- a/hedera-node/configuration/previewnet/settings.txt +++ b/hedera-node/configuration/previewnet/settings.txt @@ -23,8 +23,8 @@ state.signedStateKeep, 10 throttle7extra, 0.5 useLoopbackIp, false waitAtStartup, false -jasperDb.storagePath, /opt/hgcapp/services-hedera/HapiApp2.0/data/saved jasperDb.iteratorInputBufferBytes, 16777216 +merkleDb.iteratorInputBufferBytes, 16777216 virtualMap.preferredFlushQueueSize, 10000 state.mainClassNameOverride, com.hedera.services.ServicesMain prometheusEndpointEnabled, true diff --git a/hedera-node/configuration/testnet/log4j2.xml b/hedera-node/configuration/testnet/log4j2.xml index 72d2ce969f2e..81ce6230aaad 100644 --- a/hedera-node/configuration/testnet/log4j2.xml +++ b/hedera-node/configuration/testnet/log4j2.xml @@ -73,8 +73,9 @@ - + + @@ -82,8 +83,9 @@ - + + @@ -183,8 +185,9 @@ - + + diff --git a/hedera-node/configuration/testnet/settings.txt b/hedera-node/configuration/testnet/settings.txt index 9a7d2352c369..053a0220483c 100644 --- a/hedera-node/configuration/testnet/settings.txt +++ b/hedera-node/configuration/testnet/settings.txt @@ -24,8 +24,8 @@ state.signedStateKeep, 10 throttle7extra, 0.5 useLoopbackIp, false waitAtStartup, false -jasperDb.storagePath, /opt/hgcapp/services-hedera/HapiApp2.0/data/saved jasperDb.iteratorInputBufferBytes, 16777216 +merkleDb.iteratorInputBufferBytes, 16777216 virtualMap.preferredFlushQueueSize, 10000 state.mainClassNameOverride, com.hedera.services.ServicesMain chatter.useChatter, false diff --git a/hedera-node/hedera-app/src/test/resources/bootstrap.properties b/hedera-node/hedera-app/src/test/resources/bootstrap.properties index 01348f2507a8..3ff4cdb38f10 100644 --- a/hedera-node/hedera-app/src/test/resources/bootstrap.properties +++ b/hedera-node/hedera-app/src/test/resources/bootstrap.properties @@ -211,4 +211,4 @@ hedera.prefetch.threadPoolSize=4 hedera.prefetch.codeCacheTtlSecs=600 utilPrng.isEnabled=true tokens.autoCreations.isEnabled=true -virtualdatasource.jasperdbToMerkledb=false +virtualdatasource.jasperdbToMerkledb=true diff --git a/hedera-node/hedera-mono-service/src/jmh/java/com/hedera/node/app/service/mono/contracts/SizeLimitedStorageBench.java b/hedera-node/hedera-mono-service/src/jmh/java/com/hedera/node/app/service/mono/contracts/SizeLimitedStorageBench.java index 4df222d9e1cf..6ec091da2b5e 100644 --- a/hedera-node/hedera-mono-service/src/jmh/java/com/hedera/node/app/service/mono/contracts/SizeLimitedStorageBench.java +++ b/hedera-node/hedera-mono-service/src/jmh/java/com/hedera/node/app/service/mono/contracts/SizeLimitedStorageBench.java @@ -130,9 +130,9 @@ public void simulateContractTransaction() { private void registerConstructables() { try { Constructables.registerForAccounts(); - Constructables.registerForJasperDb(); Constructables.registerForMerkleMap(); Constructables.registerForVirtualMap(); + Constructables.registerForVirtualDataSource(); Constructables.registerForContractStorage(); } catch (final ConstructableRegistryException e) { throw new IllegalStateException(e); diff --git a/hedera-node/hedera-mono-service/src/jmh/java/com/hedera/node/app/service/mono/setup/Constructables.java b/hedera-node/hedera-mono-service/src/jmh/java/com/hedera/node/app/service/mono/setup/Constructables.java index ff3a7741822c..fd289c219c3d 100644 --- a/hedera-node/hedera-mono-service/src/jmh/java/com/hedera/node/app/service/mono/setup/Constructables.java +++ b/hedera-node/hedera-mono-service/src/jmh/java/com/hedera/node/app/service/mono/setup/Constructables.java @@ -37,6 +37,7 @@ import com.swirlds.merkle.map.MerkleMap; import com.swirlds.merkle.tree.MerkleBinaryTree; import com.swirlds.merkle.tree.MerkleTreeInternalNode; +import com.swirlds.merkledb.MerkleDbDataSourceBuilder; import com.swirlds.virtualmap.VirtualMap; import com.swirlds.virtualmap.internal.cache.VirtualNodeCache; import com.swirlds.virtualmap.internal.merkle.VirtualMapState; @@ -129,8 +130,11 @@ public static void registerForVirtualMap() throws ConstructableRegistryException .registerConstructable(new ClassConstructorPair(VirtualNodeCache.class, VirtualNodeCache::new)); } - public static void registerForJasperDb() throws ConstructableRegistryException { + public static void registerForVirtualDataSource() throws ConstructableRegistryException { ConstructableRegistry.getInstance() .registerConstructable(new ClassConstructorPair(JasperDbBuilder.class, JasperDbBuilder::new)); + ConstructableRegistry.getInstance() + .registerConstructable( + new ClassConstructorPair(MerkleDbDataSourceBuilder.class, MerkleDbDataSourceBuilder::new)); } } diff --git a/hedera-node/hedera-mono-service/src/test/java/com/hedera/node/app/service/mono/context/properties/BootstrapPropertiesTest.java b/hedera-node/hedera-mono-service/src/test/java/com/hedera/node/app/service/mono/context/properties/BootstrapPropertiesTest.java index 4fea65652e34..eea425a7d45a 100644 --- a/hedera-node/hedera-mono-service/src/test/java/com/hedera/node/app/service/mono/context/properties/BootstrapPropertiesTest.java +++ b/hedera-node/hedera-mono-service/src/test/java/com/hedera/node/app/service/mono/context/properties/BootstrapPropertiesTest.java @@ -504,7 +504,7 @@ class BootstrapPropertiesTest { entry(HEDERA_RECORD_STREAM_COMPRESS_FILES_ON_CREATION, true), entry(TOKENS_AUTO_CREATIONS_ENABLED, true), entry(WORKFLOWS_ENABLED, Set.of()), - entry(VIRTUALDATASOURCE_JASPERDB_TO_MERKLEDB, false)); + entry(VIRTUALDATASOURCE_JASPERDB_TO_MERKLEDB, true)); @Test void containsProperty() { diff --git a/hedera-node/hedera-mono-service/src/test/resources/bootstrap.properties b/hedera-node/hedera-mono-service/src/test/resources/bootstrap.properties index 8573b333106d..64edbe7114d0 100644 --- a/hedera-node/hedera-mono-service/src/test/resources/bootstrap.properties +++ b/hedera-node/hedera-mono-service/src/test/resources/bootstrap.properties @@ -211,4 +211,4 @@ hedera.prefetch.threadPoolSize=4 hedera.prefetch.codeCacheTtlSecs=600 utilPrng.isEnabled=true tokens.autoCreations.isEnabled=true -virtualdatasource.jasperdbToMerkledb=false +virtualdatasource.jasperdbToMerkledb=true diff --git a/hedera-node/hedera-mono-service/src/test/resources/bootstrap/standard.properties b/hedera-node/hedera-mono-service/src/test/resources/bootstrap/standard.properties index 7f0308bfb7ad..11a8cb181e05 100644 --- a/hedera-node/hedera-mono-service/src/test/resources/bootstrap/standard.properties +++ b/hedera-node/hedera-mono-service/src/test/resources/bootstrap/standard.properties @@ -209,4 +209,4 @@ stats.runningAvgHalfLifeSecs=10.0 stats.speedometerHalfLifeSecs=10.0 utilPrng.isEnabled=true tokens.autoCreations.isEnabled=true -virtualdatasource.jasperdbToMerkledb=false +virtualdatasource.jasperdbToMerkledb=true From 696b07029ed689685f48450e6ab5df903f66d4c5 Mon Sep 17 00:00:00 2001 From: Edward Wertz <123979964+edward-swirldslabs@users.noreply.github.com> Date: Tue, 28 Mar 2023 15:27:06 -0500 Subject: [PATCH 03/13] 05738 - deletes old saved address books (#5791) Signed-off-by: Edward Wertz --- .../swirlds/common/config/StateConfig.java | 6 +-- .../platform/AddressBookInitializer.java | 43 +++++++++++----- .../java/com/swirlds/platform/Browser.java | 11 ++-- .../platform/config/AddressBookConfig.java | 16 +++++- .../platform/AddressBookInitializerTest.java | 50 +++++++++++-------- 5 files changed, 80 insertions(+), 46 deletions(-) diff --git a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/config/StateConfig.java b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/config/StateConfig.java index f038c45d39f4..cb37486475e0 100644 --- a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/config/StateConfig.java +++ b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/config/StateConfig.java @@ -91,9 +91,6 @@ * If true, then enable extra debug code that tracks signed states. Very useful for debugging state leaks. * This debug code is relatively expensive (it takes and stores stack traces when operations are * performed on signed state objects). - * @param forceUseOfConfigAddressBook - * If true, then the address book from the config file will be used instead of the address book from the - * signed state and the swirld state will not be queried for any address book updates. */ @ConfigData("state") public record StateConfig( @@ -116,8 +113,7 @@ public record StateConfig( @ConfigProperty(defaultValue = "5") int debugHashDepth, @ConfigProperty(defaultValue = "1000") int maxAgeOfFutureStateSignatures, @ConfigProperty(defaultValue = "26") int roundsToKeepForSigning, - @ConfigProperty(defaultValue = "false") boolean signedStateSentinelEnabled, - @ConfigProperty(defaultValue = "true") boolean forceUseOfConfigAddressBook) { + @ConfigProperty(defaultValue = "false") boolean signedStateSentinelEnabled) { /** * Get the main class name that should be used for signed states. diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/AddressBookInitializer.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/AddressBookInitializer.java index 60ee9a8430ed..0bc0f51365c5 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/AddressBookInitializer.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/AddressBookInitializer.java @@ -23,6 +23,7 @@ import com.swirlds.common.system.SwirldState; import com.swirlds.common.system.address.AddressBook; import com.swirlds.common.system.address.AddressBookValidator; +import com.swirlds.platform.config.AddressBookConfig; import com.swirlds.platform.state.signed.SignedState; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -34,8 +35,10 @@ import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; +import java.util.List; import java.util.Objects; import java.util.function.Supplier; +import java.util.stream.Stream; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -56,8 +59,6 @@ public class AddressBookInitializer { public static final String STATE_ADDRESS_BOOK_USED = "The State Saved Address Book Was Used."; /** The text indicating the state address book was null in the usedAddressBook file. */ public static final String STATE_ADDRESS_BOOK_NULL = "The State Saved Address Book Was NULL."; - /** The name of the address book directory to write address books to. */ - private static final String ADDRESS_BOOK_DIRECTORY_NAME = "address_book"; /** The file name prefix to use when creating address book files. */ private static final String ADDRESS_BOOK_FILE_PREFIX = "usedAddressBook"; /** The format of date and time to use when creating address book files. */ @@ -86,6 +87,8 @@ public class AddressBookInitializer { /** The path to the directory for writing address books. */ @NonNull private final Path pathToAddressBookDirectory; + /** The maximum number of address book files to keep in the address book directory. */ + private final int maxNumFiles; /** Indicate that the unmodified config address book must be used. */ private final boolean useConfigAddressBook; @@ -97,31 +100,30 @@ public class AddressBookInitializer { * @param signedState The signed state loaded from disk. May be null. * @param genesisSupplier The swirld application state in genesis start. Must not be null. * @param configAddressBook The address book derived from config.txt. Must not be null. - * @param parentDirectory The parent directory of the address book directory. Must not be null. - * @param useConfigAddressBook Indicates if the unmodified config address book should be used. + * @param addressBookConfig The configuration settings for AddressBooks. */ public AddressBookInitializer( @NonNull final SoftwareVersion currentVersion, @Nullable final SignedState signedState, @NonNull final Supplier genesisSupplier, @NonNull final AddressBook configAddressBook, - @NonNull final String parentDirectory, - final boolean useConfigAddressBook) { + @NonNull final AddressBookConfig addressBookConfig) { this.currentVersion = Objects.requireNonNull(currentVersion, "The currentVersion must not be null."); this.genesisSupplier = Objects.requireNonNull(genesisSupplier, "The genesis swirldState supplier must not be null."); this.configAddressBook = Objects.requireNonNull(configAddressBook, "The configAddressBook must not be null."); - Objects.requireNonNull(parentDirectory, "The parentDirectory must not be null."); - this.pathToAddressBookDirectory = Path.of(parentDirectory, ADDRESS_BOOK_DIRECTORY_NAME); + Objects.requireNonNull(addressBookConfig, "The addressBookConfig must not be null."); + this.loadedSignedState = signedState; + this.loadedAddressBook = loadedSignedState == null ? null : loadedSignedState.getAddressBook(); + this.pathToAddressBookDirectory = Path.of(addressBookConfig.addressBookDirectory()); try { Files.createDirectories(pathToAddressBookDirectory); } catch (final IOException e) { logger.error(EXCEPTION.getMarker(), "Not able to create directory: {}", pathToAddressBookDirectory, e); throw new IllegalStateException("Not able to create directory: " + pathToAddressBookDirectory, e); } - this.loadedSignedState = signedState; - this.loadedAddressBook = loadedSignedState == null ? null : loadedSignedState.getAddressBook(); - this.useConfigAddressBook = useConfigAddressBook; + this.useConfigAddressBook = addressBookConfig.forceUseOfConfigAddressBook(); + this.maxNumFiles = addressBookConfig.maxRecordedAddressBookFiles(); initialAddressBook = initialize(); } @@ -231,7 +233,7 @@ private AddressBook checkCandidateAddressBookValidity(@Nullable final AddressBoo * * @param usedAddressBook the address book to be returned from the AddressBookInitializer. */ - private void recordAddressBooks(@NonNull final AddressBook usedAddressBook) { + private synchronized void recordAddressBooks(@NonNull final AddressBook usedAddressBook) { final String date = DATE_TIME_FORMAT.format(Instant.now()); final String addressBookFileName = ADDRESS_BOOK_FILE_PREFIX + "_v" + currentVersion + "_" + date + ".txt"; final String addressBookDebugFileName = addressBookFileName + ".debug"; @@ -263,6 +265,23 @@ private void recordAddressBooks(@NonNull final AddressBook usedAddressBook) { } catch (final IOException e) { logger.error(EXCEPTION.getMarker(), "Not able to write address book to file. ", e); } + cleanAddressBookDirectory(); + } + + /** + * Deletes the oldest address book files if there are more than the maximum number of address book files. + */ + private synchronized void cleanAddressBookDirectory() { + try (final Stream filesStream = Files.list(pathToAddressBookDirectory)) { + final List files = filesStream.sorted().toList(); + if (files.size() > maxNumFiles) { + for (int i = 0; i < files.size() - maxNumFiles; i++) { + Files.delete(files.get(i)); + } + } + } catch (final IOException e) { + logger.info(EXCEPTION.getMarker(), "Unable to list files in address book directory. ", e); + } } /** 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 7d28166f4b4a..eedf09c5c0aa 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 @@ -545,17 +545,12 @@ private void createLocalPlatforms( final SignedState loadedSignedState = getUnmodifiedSignedStateFromDisk( mainClassName, swirldName, nodeId, appVersion, addressBook.copy(), emergencyRecoveryManager); - final StateConfig stateConfig = - platformContext.getConfiguration().getConfigData(StateConfig.class); + final AddressBookConfig addressBookConfig = + platformContext.getConfiguration().getConfigData(AddressBookConfig.class); // Initialize the address book from the configuration and platform saved state. final AddressBookInitializer addressBookInitializer = new AddressBookInitializer( - appVersion, - loadedSignedState, - appMain::newState, - addressBook.copy(), - stateConfig.savedStateDirectory(), - stateConfig.forceUseOfConfigAddressBook()); + appVersion, loadedSignedState, appMain::newState, addressBook.copy(), addressBookConfig); // set here, then given to the state in run(). A copy of it is given to hashgraph. final AddressBook initialAddressBook = addressBookInitializer.getInitialAddressBook(); diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/config/AddressBookConfig.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/config/AddressBookConfig.java index 2adbd94bcf97..6800f9e39980 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/config/AddressBookConfig.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/config/AddressBookConfig.java @@ -27,6 +27,20 @@ * Intermediate workaround until the platform is capable of handling an address book that changes at runtime. This * feature will be removed in a future version. Check if the address book override is enabled. If enabled, then * the address book will only change when a node starts up. This feature will be removed in a future version. + * @param forceUseOfConfigAddressBook + * If true, then the address book from the config file will be used instead of the address book from the + * signed state and the swirld state will not be queried for any address book updates. + * @param addressBookDirectory + * The directory where address book files are saved. + * @param maxRecordedAddressBookFiles + * The maximum number of address book files to keep in the address book directory. On startup the + * AddressBookInitializer will create two new files in the `/data/saved/address_book` directory. The + * AddressBookInitializer will delete files by age, oldest first, if the number of files in the directory + * exceeds the maximum indicated by this setting. */ @ConfigData("addressBook") -public record AddressBookConfig(@ConfigProperty(defaultValue = "true") boolean updateAddressBookOnlyAtUpgrade) {} +public record AddressBookConfig( + @ConfigProperty(defaultValue = "true") boolean updateAddressBookOnlyAtUpgrade, + @ConfigProperty(defaultValue = "true") boolean forceUseOfConfigAddressBook, + @ConfigProperty(defaultValue = "data/saved/address_book") String addressBookDirectory, + @ConfigProperty(defaultValue = "50") int maxRecordedAddressBookFiles) {} diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/AddressBookInitializerTest.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/AddressBookInitializerTest.java index 31b6bdfe40b0..94d008160182 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/AddressBookInitializerTest.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/AddressBookInitializerTest.java @@ -36,10 +36,13 @@ import com.swirlds.common.system.address.Address; import com.swirlds.common.system.address.AddressBook; import com.swirlds.common.test.RandomAddressBookGenerator; +import com.swirlds.config.api.Configuration; +import com.swirlds.platform.config.AddressBookConfig; import com.swirlds.platform.state.PlatformData; import com.swirlds.platform.state.PlatformState; import com.swirlds.platform.state.State; import com.swirlds.platform.state.signed.SignedState; +import com.swirlds.test.framework.config.TestConfigBuilder; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -70,8 +73,7 @@ void forceUseOfConfigAddressBookNullSignedState() throws IOException { getMockSignedState(0), getMockSwirldStateSupplier(1), configAddressBook, - testDirectory.toString(), - true); + getAddressBookConfig(true)); final AddressBook inititializedAddressBook = initializer.getInitialAddressBook(); assertEquals( configAddressBook, @@ -91,8 +93,7 @@ void forceUseOfConfigAddressBookNonNullSignedState() throws IOException { signedState, getMockSwirldStateSupplier(1), configAddressBook, - testDirectory.toString(), - true); + getAddressBookConfig(true)); final AddressBook inititializedAddressBook = initializer.getInitialAddressBook(); assertEquals( configAddressBook, @@ -113,8 +114,7 @@ void noStateLoadedFromDisk() throws IOException { getMockSignedState(0), getMockSwirldStateSupplier(10), configAddressBook, - testDirectory.toString(), - false); + getAddressBookConfig(false)); final AddressBook inititializedAddressBook = initializer.getInitialAddressBook(); assertEquals( expectedAddressBook, @@ -133,8 +133,7 @@ void noStateLoadedFromDiskGenesisStateSetZeroStake() throws IOException { getMockSignedState(0), getMockSwirldStateSupplier(0), configAddressBook, - testDirectory.toString(), - false); + getAddressBookConfig(false)); final AddressBook inititializedAddressBook = initializer.getInitialAddressBook(); assertEquals( configAddressBook, @@ -153,8 +152,7 @@ void noStateLoadedFromDiskGenesisStateChangedAddressBook() throws IOException { getMockSignedState(0), getMockSwirldStateSupplier(2), configAddressBook, - testDirectory.toString(), - false); + getAddressBookConfig(false)); final AddressBook inititializedAddressBook = initializer.getInitialAddressBook(); assertEquals( configAddressBook, @@ -171,12 +169,12 @@ void currentVersionLessThanStateVersion() throws IOException { final AddressBook configAddressBook = getRandomAddressBook(); final SoftwareVersion configSoftwareVersion = getMockSoftwareVersion(1); final Supplier genesisSwirldState = getMockSwirldStateSupplier(1); - final String directory = testDirectory.toString(); + final AddressBookConfig addressBookConfig = getAddressBookConfig(false); assertThrows( IllegalStateException.class, () -> new AddressBookInitializer( - configSoftwareVersion, signedState, genesisSwirldState, configAddressBook, directory, false), + configSoftwareVersion, signedState, genesisSwirldState, configAddressBook, addressBookConfig), "IllegalStateException was not thrown."); } @@ -191,8 +189,7 @@ void currentVersionEqualsStateVersion() throws IOException { signedState, getMockSwirldStateSupplier(1), configAddressBook, - testDirectory.toString(), - false); + getAddressBookConfig(false)); final AddressBook inititializedAddressBook = initializer.getInitialAddressBook(); assertEquals( signedState.getAddressBook(), @@ -213,8 +210,7 @@ void versionUpgradeSwirldStateZeroStake() throws IOException { signedState, getMockSwirldStateSupplier(0), configAddressBook, - testDirectory.toString(), - false); + getAddressBookConfig(false)); final AddressBook inititializedAddressBook = initializer.getInitialAddressBook(); assertEquals( configAddressBook, @@ -235,8 +231,7 @@ void versionUpgradeSwirldStateModifiedAddressBook() throws IOException { signedState, getMockSwirldStateSupplier(2), configAddressBook, - testDirectory.toString(), - false); + getAddressBookConfig(false)); final AddressBook inititializedAddressBook = initializer.getInitialAddressBook(); assertEquals( configAddressBook, @@ -257,8 +252,7 @@ void versionUpgradeSwirldStateStakeUpdateWorks() throws IOException { signedState, getMockSwirldStateSupplier(10), configAddressBook, - testDirectory.toString(), - false); + getAddressBookConfig(false)); final AddressBook inititializedAddressBook = initializer.getInitialAddressBook(); assertNotEquals( configAddressBook, @@ -496,4 +490,20 @@ private void assertAddressBookFileContent( debugFileContent.contains(usedText), "The usedAddressBook content is not:\n" + usedText + "\n\n debugFileContent:\n" + debugFileContent); } + + /** + * Creates an AddressBookConfig object with the given forceUseOfConfigAddressBook value. + * + * @param forceUseOfConfigAddressBook the value to use for the forceUseOfConfigAddressBook property. + * @return the AddressBookConfig object. + */ + private AddressBookConfig getAddressBookConfig(boolean forceUseOfConfigAddressBook) { + final Configuration configuration = new TestConfigBuilder() + .withValue("addressBook.forceUseOfConfigAddressBook", forceUseOfConfigAddressBook) + .withValue("addressBook.addressBookDirectory", testDirectory.toString()) + .withValue("addressBook.maxRecordedAddressBookFiles", 50) + .getOrCreateConfig(); + final AddressBookConfig addressBookConfig = configuration.getConfigData(AddressBookConfig.class); + return addressBookConfig; + } } From 4e2340f648443e74a58f9660fe941add4a611f85 Mon Sep 17 00:00:00 2001 From: Neeha <52669918+Neeharika-Sompalli@users.noreply.github.com> Date: Wed, 29 Mar 2023 10:42:20 -0500 Subject: [PATCH 04/13] Update protobufs to v0.36.0 tag (#5877) Signed-off-by: Neeharika-Sompalli --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 0dba39663a9c..a1da4fcdc37d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -99,7 +99,7 @@ gitRepositories { uri.set("https://github.com/hashgraph/hedera-protobufs.git") // choose tag or branch of HAPI you would like to test with // This version needs to match tha HAPI version below in versionCatalogs - tag.set("05111-use-pbj-for-hcs-state") + tag.set("v0.36.0") // do not load project from repo autoInclude.set(false) } From eaedcc4e5039931b6664addaf812e85a37ffd961 Mon Sep 17 00:00:00 2001 From: Neeha <52669918+Neeharika-Sompalli@users.noreply.github.com> Date: Wed, 29 Mar 2023 14:46:56 -0500 Subject: [PATCH 05/13] Update protobufs to v0.36.1 tag (#5885) Signed-off-by: Neeharika-Sompalli --- settings.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index a1da4fcdc37d..c7588dfd94c4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -99,7 +99,7 @@ gitRepositories { uri.set("https://github.com/hashgraph/hedera-protobufs.git") // choose tag or branch of HAPI you would like to test with // This version needs to match tha HAPI version below in versionCatalogs - tag.set("v0.36.0") + tag.set("v0.36.1") // do not load project from repo autoInclude.set(false) } @@ -114,7 +114,7 @@ dependencyResolutionManagement { // runtime. create("libs") { // The HAPI API version to use, this need to match the tag set on gitRepositories above - version("hapi-version", "0.36.0-alpha.1-SNAPSHOT") + version("hapi-version", "0.36.1-another-SNAPSHOT") // Definition of version numbers for all libraries version("pbj-version", "0.3.0") From 512fb58a808841985d1d1674b7c970eff72d634b Mon Sep 17 00:00:00 2001 From: artemananiev <33361937+artemananiev@users.noreply.github.com> Date: Wed, 29 Mar 2023 13:58:46 -0700 Subject: [PATCH 06/13] 5869: Faster bucket index updates in HalfDiskHashMap (#5870) Fixes: https://github.com/hashgraph/hedera-services/issues/5869 Reviewed-by: Ivan Malygin , Oleg Mazurov Signed-off-by: Artem Ananev --- .../merkledb/files/hashmap/Bucket.java | 3 +- .../files/hashmap/HalfDiskHashMap.java | 29 +++++++------------ .../merkledb/files/hashmap/BucketTest.java | 28 +++++++++--------- 3 files changed, 27 insertions(+), 33 deletions(-) diff --git a/platform-sdk/swirlds-jasperdb/src/main/java/com/swirlds/merkledb/files/hashmap/Bucket.java b/platform-sdk/swirlds-jasperdb/src/main/java/com/swirlds/merkledb/files/hashmap/Bucket.java index d12cbfa52253..96b58fa6e1d6 100644 --- a/platform-sdk/swirlds-jasperdb/src/main/java/com/swirlds/merkledb/files/hashmap/Bucket.java +++ b/platform-sdk/swirlds-jasperdb/src/main/java/com/swirlds/merkledb/files/hashmap/Bucket.java @@ -213,7 +213,8 @@ public long findValue(final int keyHashCode, final K key, final long notFoundVal * @param value the entry value, this can also be special * HalfDiskHashMap.SPECIAL_DELETE_ME_VALUE to mean delete */ - public void putValue(final int keyHashCode, final K key, final long value) { + public void putValue(final K key, final long value) { + final int keyHashCode = key.hashCode(); try { // scan over all existing key/value entries and see if there is already one for this // key. If there is then update it, otherwise we have at least worked out the entryOffset diff --git a/platform-sdk/swirlds-jasperdb/src/main/java/com/swirlds/merkledb/files/hashmap/HalfDiskHashMap.java b/platform-sdk/swirlds-jasperdb/src/main/java/com/swirlds/merkledb/files/hashmap/HalfDiskHashMap.java index c121e0cfef10..26b7f78617bc 100644 --- a/platform-sdk/swirlds-jasperdb/src/main/java/com/swirlds/merkledb/files/hashmap/HalfDiskHashMap.java +++ b/platform-sdk/swirlds-jasperdb/src/main/java/com/swirlds/merkledb/files/hashmap/HalfDiskHashMap.java @@ -18,6 +18,7 @@ import static com.swirlds.logging.LogMarker.EXCEPTION; import static com.swirlds.logging.LogMarker.MERKLE_DB; +import static com.swirlds.merkledb.files.DataFileCommon.NON_EXISTENT_DATA_LOCATION; import static com.swirlds.merkledb.files.DataFileCommon.formatSizeBytes; import static com.swirlds.merkledb.files.DataFileCommon.getSizeOfFiles; import static com.swirlds.merkledb.files.DataFileCommon.logMergeStats; @@ -45,7 +46,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.eclipse.collections.api.tuple.primitive.IntObjectPair; -import org.eclipse.collections.impl.list.mutable.primitive.LongArrayList; import org.eclipse.collections.impl.map.mutable.primitive.IntObjectHashMap; /** @@ -69,8 +69,6 @@ public class HalfDiskHashMap> implements AutoClo private static final String METADATA_FILENAME_SUFFIX = "_metadata.hdhm"; /** Bucket index file name suffix with extension */ private static final String BUCKET_INDEX_FILENAME_SUFFIX = "_bucket_index.ll"; - /** Each index change includes both a bucket index and bucket location. */ - private static final int INDEX_CHANGE_COMPONENTS = 2; /** Nominal value for value to say please delete from map. */ protected static final long SPECIAL_DELETE_ME_VALUE = Long.MIN_VALUE; /** The amount of data used for storing key hash code */ @@ -416,7 +414,6 @@ public void endWriting() throws IOException { // write to files fileCollection.startWriting(); // for each changed bucket, write the new buckets to file but do not update index yet - final LongArrayList indexChanges = new LongArrayList(); int oldBucketIndex = -1; for (IntObjectPair> keyValue : oneTransactionsData.keyValuesView().toList().sortThis()) { @@ -438,13 +435,16 @@ public void endWriting() throws IOException { } final Bucket finalBucket = bucket; // for each changed key in bucket, update bucket - bucketMap.forEachKeyValue((k, v) -> finalBucket.putValue(k.hashCode(), k, v)); - // save bucket - final long bucketLocation = fileCollection.storeDataItem(bucket); - - // stash update bucketIndexToBucketLocation - indexChanges.add(bucketIndex); - indexChanges.add(bucketLocation); + bucketMap.forEachKeyValue(finalBucket::putValue); + if (finalBucket.getSize() == 0) { + // bucket is missing or empty, remove it from the index + bucketIndexToBucketLocation.put(bucketIndex, NON_EXISTENT_DATA_LOCATION); + } else { + // save bucket + final long bucketLocation = fileCollection.storeDataItem(bucket); + // update bucketIndexToBucketLocation + bucketIndexToBucketLocation.put(bucketIndex, bucketLocation); + } } catch (IllegalStateException e) { printStats(); debugDumpTransactionCacheCondensed(); @@ -454,13 +454,6 @@ public void endWriting() throws IOException { } // close files session final DataFileReader> dataFileReader = fileCollection.endWriting(0, numOfBuckets); - // for each changed bucket update index - for (int i = 0; i < indexChanges.size(); i += INDEX_CHANGE_COMPONENTS) { - final long bucketIndex = indexChanges.get(i); - final long bucketLocation = indexChanges.get(i + 1); - // update bucketIndexToBucketLocation - bucketIndexToBucketLocation.put(bucketIndex, bucketLocation); - } // we have updated all indexes so the data file can now be included in merges dataFileReader.setFileCompleted(); } diff --git a/platform-sdk/swirlds-jasperdb/src/test/java/com/swirlds/merkledb/files/hashmap/BucketTest.java b/platform-sdk/swirlds-jasperdb/src/test/java/com/swirlds/merkledb/files/hashmap/BucketTest.java index 30bc091d74b3..495585d0cffc 100644 --- a/platform-sdk/swirlds-jasperdb/src/test/java/com/swirlds/merkledb/files/hashmap/BucketTest.java +++ b/platform-sdk/swirlds-jasperdb/src/test/java/com/swirlds/merkledb/files/hashmap/BucketTest.java @@ -86,7 +86,7 @@ void testBucketAddAndDelete(KeyType keyType) throws IOException { // insert keys and check for (int i = 0; i < 10; i++) { final VirtualLongKey key = testKeys[i]; - bucket.putValue(key.hashCode(), key, key.getKeyAsLong() + 100); + bucket.putValue(key, key.getKeyAsLong() + 100); assertEquals(i + 1, bucket.getBucketEntryCount(), "Check we have correct count"); // check that all keys added so far are there for (int j = 0; j <= i; j++) { @@ -95,15 +95,15 @@ void testBucketAddAndDelete(KeyType keyType) throws IOException { } assertEquals(10, bucket.getBucketEntryCount(), "Check we have correct count"); // now update, check and put back - bucket.putValue(testKeys[5].hashCode(), testKeys[5], 1234); + bucket.putValue(testKeys[5], 1234); assertEquals( 1234, bucket.findValue(testKeys[5].hashCode(), testKeys[5], -1), "Should get expected value of 1234"); - bucket.putValue(testKeys[5].hashCode(), testKeys[5], 115); + bucket.putValue(testKeys[5], 115); for (int j = 0; j < 10; j++) { checkKey(bucket, testKeys[j]); } // now delete last key and check - bucket.putValue(testKeys[9].hashCode(), testKeys[9], SPECIAL_DELETE_ME_VALUE); + bucket.putValue(testKeys[9], SPECIAL_DELETE_ME_VALUE); assertEquals(9, bucket.getBucketEntryCount(), "Check we have correct count"); for (int j = 0; j < 9; j++) { checkKey(bucket, testKeys[j]); @@ -113,7 +113,7 @@ void testBucketAddAndDelete(KeyType keyType) throws IOException { bucket.findValue(testKeys[9].hashCode(), testKeys[9], -1), "Should not find entry 10 any more we deleted it"); // now delete a middle, index 5 - bucket.putValue(testKeys[5].hashCode(), testKeys[5], SPECIAL_DELETE_ME_VALUE); + bucket.putValue(testKeys[5], SPECIAL_DELETE_ME_VALUE); assertEquals(8, bucket.getBucketEntryCount(), "Check we have correct count"); for (int j = 0; j < 5; j++) { checkKey(bucket, testKeys[j]); @@ -126,7 +126,7 @@ void testBucketAddAndDelete(KeyType keyType) throws IOException { bucket.findValue(testKeys[5].hashCode(), testKeys[5], -1), "Should not find entry 5 any more we deleted it"); // now delete first, index 0 - bucket.putValue(testKeys[0].hashCode(), testKeys[0], SPECIAL_DELETE_ME_VALUE); + bucket.putValue(testKeys[0], SPECIAL_DELETE_ME_VALUE); assertEquals(7, bucket.getBucketEntryCount(), "Check we have correct count"); for (int j = 1; j < 5; j++) { checkKey(bucket, testKeys[j]); @@ -139,8 +139,8 @@ void testBucketAddAndDelete(KeyType keyType) throws IOException { bucket.findValue(testKeys[0].hashCode(), testKeys[0], -1), "Should not find entry 0 any more we deleted it"); // add two more entries and check - bucket.putValue(testKeys[10].hashCode(), testKeys[10], 120); - bucket.putValue(testKeys[11].hashCode(), testKeys[11], 121); + bucket.putValue(testKeys[10], 120); + bucket.putValue(testKeys[11], 121); assertEquals(9, bucket.getBucketEntryCount(), "Check we have correct count"); for (int j = 1; j < 5; j++) { checkKey(bucket, testKeys[j]); @@ -152,9 +152,9 @@ void testBucketAddAndDelete(KeyType keyType) throws IOException { checkKey(bucket, testKeys[j]); } // put 0, 5 and 9 back in, and check we have full range - bucket.putValue(testKeys[0].hashCode(), testKeys[0], 110); - bucket.putValue(testKeys[5].hashCode(), testKeys[5], 115); - bucket.putValue(testKeys[9].hashCode(), testKeys[9], 119); + bucket.putValue(testKeys[0], 110); + bucket.putValue(testKeys[5], 115); + bucket.putValue(testKeys[9], 119); assertEquals(12, bucket.getBucketEntryCount(), "Check we have correct count"); for (int j = 0; j < 12; j++) { checkKey(bucket, testKeys[j]); @@ -177,7 +177,7 @@ void testBucketGrowing(KeyType keyType) { // insert keys and check for (int i = 0; i < testKeys.length; i++) { final VirtualLongKey key = testKeys[i]; - bucket.putValue(key.hashCode(), key, key.getKeyAsLong() + 100); + bucket.putValue(key, key.getKeyAsLong() + 100); assertEquals(i + 1, bucket.getBucketEntryCount(), "Check we have correct count"); // check that all keys added so far are there for (int j = 0; j <= i; j++) { @@ -203,7 +203,7 @@ void testBucketImportExportClear(KeyType keyType) throws IOException { // insert keys and check for (int i = 0; i < testKeys.length; i++) { final VirtualLongKey key = testKeys[i]; - bucket.putValue(key.hashCode(), key, key.getKeyAsLong() + 100); + bucket.putValue(key, key.getKeyAsLong() + 100); assertEquals(i + 1, bucket.getBucketEntryCount(), "Check we have correct count"); // check that all keys added so far are there for (int j = 0; j <= i; j++) { @@ -246,7 +246,7 @@ void toStringAsExpectedForBucket() { assertEquals(emptyBucketRepr, bucket.toString(), "Empty bucket should represent as expected"); final ExampleLongKeyFixedSize key = new ExampleLongKeyFixedSize(2056); - bucket.putValue(key.hashCode(), key, 5124); + bucket.putValue(key, 5124); bucket.setBucketIndex(0); final String nonEmptyBucketRepr = "Bucket{bucketIndex=0, entryCount=1, size=32\n" + " ENTRY[0] value= 5124 keyHashCode=2056 keyVer=3054" From 2228a6ee0f0edecd18aba27e5a389bb3380894a6 Mon Sep 17 00:00:00 2001 From: Matt Hess Date: Wed, 29 Mar 2023 16:58:54 -0600 Subject: [PATCH 07/13] Update hedera protobuf java api to v0.36.1 (#5886) Signed-off-by: Matt Hess --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index c7588dfd94c4..5ea440ad0ea7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -114,7 +114,7 @@ dependencyResolutionManagement { // runtime. create("libs") { // The HAPI API version to use, this need to match the tag set on gitRepositories above - version("hapi-version", "0.36.1-another-SNAPSHOT") + version("hapi-version", "0.36.1") // Definition of version numbers for all libraries version("pbj-version", "0.3.0") From b54c98ee76102ec5fb57e1db270908e6ae8efeeb Mon Sep 17 00:00:00 2001 From: Edward Wertz <123979964+edward-swirldslabs@users.noreply.github.com> Date: Thu, 30 Mar 2023 09:00:51 -0500 Subject: [PATCH 08/13] 05803 - SwirldMain load configuration at startup (#5810) Signed-off-by: Edward Wertz --- .../com/swirlds/common/system/SwirldMain.java | 16 +- .../java/com/swirlds/platform/Browser.java | 159 ++++++++++++------ 2 files changed, 117 insertions(+), 58 deletions(-) diff --git a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/system/SwirldMain.java b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/system/SwirldMain.java index 0adc4b915049..a556b2faf53e 100644 --- a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/system/SwirldMain.java +++ b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/system/SwirldMain.java @@ -17,6 +17,8 @@ package com.swirlds.common.system; import com.swirlds.config.api.Configuration; +import com.swirlds.config.api.ConfigurationBuilder; +import edu.umd.cs.findbugs.annotations.NonNull; /** * To implement a swirld, create a class that implements SwirldMain. Its constructor should have no @@ -30,7 +32,17 @@ public interface SwirldMain extends Runnable { * @param configuration * configuration for this node */ - default void setConfiguration(final Configuration configuration) { + default void setConfiguration(@NonNull final Configuration configuration) { + // override if needed + } + + /** + * Update the configuration builder with any additional configuration that is required by this swirld. + * + * @param configurationBuilder + * the configuration builder to update + */ + default void updateConfigurationBuilder(@NonNull final ConfigurationBuilder configurationBuilder) { // override if needed } @@ -50,7 +62,7 @@ default void setConfiguration(final Configuration configuration) { * @param selfId * the ID number for this member (myself) */ - void init(Platform platform, NodeId selfId); + void init(@NonNull final Platform platform, @NonNull final NodeId selfId); /** * This is where the app manages the screen and I/O, and creates transactions as needed. It should 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 eedf09c5c0aa..82996b6aed31 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 @@ -50,7 +50,6 @@ import com.swirlds.common.crypto.CryptographyHolder; import com.swirlds.common.crypto.config.CryptoConfig; import com.swirlds.common.internal.ApplicationDefinition; -import com.swirlds.common.internal.ConfigurationException; import com.swirlds.common.io.config.TemporaryFileConfig; import com.swirlds.common.merkle.synchronization.config.ReconnectConfig; import com.swirlds.common.metrics.Metrics; @@ -114,9 +113,12 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Set; import javax.swing.JFrame; import javax.swing.UIManager; @@ -180,7 +182,15 @@ private Browser(final Set localNodesToStart) throws IOException { final ConfigSource configPropertiesConfigSource = new ConfigPropertiesSource(configurationProperties); final ConfigSource configPropertiesAliasConfigSource = new AliasConfigSource(configPropertiesConfigSource); - this.configuration = ConfigurationBuilder.create() + // Load config.txt file, parse application jar file name, main class name, address book, and parameters + final ApplicationDefinition appDefinition = + ApplicationDefinitionLoader.load(configurationProperties, localNodesToStart); + + // Load all SwirldMain instances for locally run nodes. + final Map appMains = loadSwirldMains(appDefinition, localNodesToStart); + + // Load Configuration Definitions + final ConfigurationBuilder configurationBuilder = ConfigurationBuilder.create() .withSource(settingsAliasConfigSource) .withSource(configPropertiesAliasConfigSource) .withConfigDataType(BasicConfig.class) @@ -199,8 +209,17 @@ private Browser(final Set localNodesToStart) throws IOException { .withConfigDataType(MetricsConfig.class) .withConfigDataType(PrometheusConfig.class) .withConfigDataType(OSHealthCheckConfig.class) - .withConfigDataType(WiringConfig.class) - .build(); + .withConfigDataType(WiringConfig.class); + + // Assume all locally run instances provide the same configuration definitions to the configuration builder. + if (appMains.size() > 0) { + appMains.get(0L).updateConfigurationBuilder(configurationBuilder); + } + + this.configuration = configurationBuilder.build(); + + // Set the configuration on all SwirldMain instances. + appMains.values().forEach(swirldMain -> swirldMain.setConfiguration(configuration)); ConfigurationHolder.getInstance().setConfiguration(configuration); CryptographyHolder.reset(); @@ -263,7 +282,7 @@ private Browser(final Set localNodesToStart) throws IOException { } // instantiate all Platform objects, which each instantiates a Statistics object logger.debug(STARTUP.getMarker(), "About to run startPlatforms()"); - startPlatforms(configuration, configurationProperties, localNodesToStart); + startPlatforms(configuration, appDefinition, appMains); // create the browser window, which uses those Statistics objects showBrowserWindow(); @@ -314,6 +333,59 @@ private Browser(final Set localNodesToStart) throws IOException { logger.debug(STARTUP.getMarker(), "main() finished"); } + /** + * Load all {@link SwirldMain} instances for locally run nodes. Locally run nodes are indicated in two possible + * ways. One is through the set of local nodes to start. The other is through {@link Address::isOwnHost} being + * true. + * + * @param appDefinition the application definition + * @param localNodesToStart the locally run nodeIds + * @return a map from nodeIds to {@link SwirldMain} instances + * @throws AppLoaderException if there are issues loading the user app + * @throws ConstructableRegistryException if there are issues registering + * {@link com.swirlds.common.constructable.RuntimeConstructable} classes + */ + @NonNull + private Map loadSwirldMains( + @NonNull final ApplicationDefinition appDefinition, @NonNull final Set localNodesToStart) { + Objects.requireNonNull(appDefinition, "appDefinition must not be null"); + Objects.requireNonNull(localNodesToStart, "localNodesToStart must not be null"); + try { + // Create the SwirldAppLoader + final SwirldAppLoader appLoader; + try { + appLoader = + SwirldAppLoader.loadSwirldApp(appDefinition.getMainClassName(), appDefinition.getAppJarPath()); + } catch (final AppLoaderException e) { + CommonUtils.tellUserConsolePopup("ERROR", e.getMessage()); + throw e; + } + + // Register all RuntimeConstructable classes + logger.debug(STARTUP.getMarker(), "Scanning the classpath for RuntimeConstructable classes"); + final long start = System.currentTimeMillis(); + ConstructableRegistry.getInstance().registerConstructables("", appLoader.getClassLoader()); + logger.debug( + STARTUP.getMarker(), + "Done with registerConstructables, time taken {}ms", + System.currentTimeMillis() - start); + + // Create the SwirldMain instances + final Map appMains = new HashMap<>(); + final AddressBook addressBook = appDefinition.getAddressBook(); + for (int i = 0; i < addressBook.getSize(); i++) { + final long id = addressBook.getId(i); + final Address address = addressBook.getAddress(id); + if (localNodesToStart.contains((int) id) || address.isOwnHost()) { + appMains.put(id, buildAppMain(appDefinition, appLoader)); + } + } + return appMains; + } catch (final Exception ex) { + throw new RuntimeException("Error loading SwirldMains", ex); + } + } + /** * Writes all settings and config values to settingsUsed.txt * @@ -504,12 +576,18 @@ private static SwirldMain buildAppMain(final ApplicationDefinition appDefinition } private void createLocalPlatforms( - final ApplicationDefinition appDefinition, - final Crypto[] crypto, - final InfoSwirld infoSwirld, - final SwirldAppLoader appLoader, - final Configuration configuration, - final MetricsProvider metricsProvider) { + @NonNull final ApplicationDefinition appDefinition, + @NonNull final Crypto[] crypto, + @NonNull final InfoSwirld infoSwirld, + @NonNull final Map appMains, + @NonNull final Configuration configuration, + @NonNull final MetricsProvider metricsProvider) { + Objects.requireNonNull(appDefinition, "the app definition must not be null"); + Objects.requireNonNull(crypto, "the crypto array must not be null"); + Objects.requireNonNull(infoSwirld, "the infoSwirld must not be null"); + Objects.requireNonNull(appMains, "the appMains map must not be null"); + Objects.requireNonNull(configuration, "the configuration must not be null"); + Objects.requireNonNull(metricsProvider, "the metricsProvider must not be null"); final AddressBook addressBook = appDefinition.getAddressBook(); @@ -529,8 +607,7 @@ private void createLocalPlatforms( final PlatformContext platformContext = new DefaultPlatformContext(nodeId, metricsProvider, configuration); - final SwirldMain appMain = buildAppMain(appDefinition, appLoader); - appMain.setConfiguration(configuration); + SwirldMain appMain = appMains.get(address.getId()); // name of the app's SwirldMain class final String mainClassName = appDefinition.getMainClassName(); @@ -613,13 +690,13 @@ private void createLocalPlatforms( /** * Load the signed state from the disk if it is present. * - * @param mainClassName the name of the app's SwirldMain class. - * @param swirldName the name of the swirld to load the state for. - * @param selfId the ID of the node to load the state for. - * @param appVersion the version of the app to use for emergency recovery. - * @param configAddressBook the address book to use for emergency recovery. + * @param mainClassName the name of the app's SwirldMain class. + * @param swirldName the name of the swirld to load the state for. + * @param selfId the ID of the node to load the state for. + * @param appVersion the version of the app to use for emergency recovery. + * @param configAddressBook the address book to use for emergency recovery. * @param emergencyRecoveryManager the emergency recovery manager to use for emergency recovery. - * @return the signed state loaded from disk. + * @return the signed state loaded from disk. */ private SignedState getUnmodifiedSignedStateFromDisk( @NonNull final String mainClassName, @@ -653,27 +730,15 @@ private SignedState getUnmodifiedSignedStateFromDisk( * Instantiate and run all the local platforms specified in the given config.txt file. This method reads in and * parses the config.txt file. * - * @throws UnknownHostException problems getting an IP address for another user - * @throws SocketException problems getting the IP address for self - * @throws AppLoaderException if there are issues loading the user app - * @throws ConstructableRegistryException if there are issues registering - * {@link com.swirlds.common.constructable.RuntimeConstructable} classes + * @throws UnknownHostException problems getting an IP address for another user + * @throws SocketException problems getting the IP address for self */ private void startPlatforms( - final Configuration configuration, - final LegacyConfigProperties configurationProperties, - final Set localNodesToStart) - throws AppLoaderException, ConstructableRegistryException { + @NonNull final Configuration configuration, + @NonNull final ApplicationDefinition appDefinition, + @NonNull final Map appMains) { - // Load config.txt file, parse application jar file name, main class name, address book, and parameters - final ApplicationDefinition appDefinition; - final AddressBook addressBook; - try { - appDefinition = ApplicationDefinitionLoader.load(configurationProperties, localNodesToStart); - addressBook = appDefinition.getAddressBook(); - } catch (final ConfigurationException ex) { - return; - } + final AddressBook addressBook = appDefinition.getAddressBook(); // If enabled, clean out the signed state directory. Needs to be done before the platform/state is started up, // as we don't want to delete the temporary file directory if it ends up being put in the saved state directory. @@ -721,31 +786,13 @@ private void startPlatforms( logger.debug(STARTUP.getMarker(), "Starting platforms"); - // Try to load the app - final SwirldAppLoader appLoader; - try { - appLoader = SwirldAppLoader.loadSwirldApp(appDefinition.getMainClassName(), appDefinition.getAppJarPath()); - } catch (final AppLoaderException e) { - CommonUtils.tellUserConsolePopup("ERROR", e.getMessage()); - throw e; - } - - // Register all RuntimeConstructable classes - logger.debug(STARTUP.getMarker(), "Scanning the classpath for RuntimeConstructable classes"); - final long start = System.currentTimeMillis(); - ConstructableRegistry.getInstance().registerConstructables("", appLoader.getClassLoader()); - logger.debug( - STARTUP.getMarker(), - "Done with registerConstructables, time taken {}ms", - System.currentTimeMillis() - start); - // Setup metrics system final DefaultMetricsProvider metricsProvider = new DefaultMetricsProvider(configuration); final Metrics globalMetrics = metricsProvider.createGlobalMetrics(); CryptoMetrics.registerMetrics(globalMetrics); // Create all instances for all nodes that should run locally - createLocalPlatforms(appDefinition, crypto, infoSwirld, appLoader, configuration, metricsProvider); + createLocalPlatforms(appDefinition, crypto, infoSwirld, appMains, configuration, metricsProvider); // Write all metrics information to file MetricsDocUtils.writeMetricsDocumentToFile(globalMetrics, getPlatforms(), configuration); From 44f2da393b8bb34c5263b1bc1979102a2b0bac44 Mon Sep 17 00:00:00 2001 From: Edward Wertz <123979964+edward-swirldslabs@users.noreply.github.com> Date: Thu, 30 Mar 2023 09:01:14 -0500 Subject: [PATCH 09/13] 05584 - AddressBookTestingTool app created (#5785) Signed-off-by: Edward Wertz Signed-off-by: Edward Wertz <123979964+edward-swirldslabs@users.noreply.github.com> Co-authored-by: Hendrik Ebbers Co-authored-by: Cody Littley <56973212+cody-littley@users.noreply.github.com> --- .../tests/AddressBookTestingTool/README.md | 179 +++++++++++++ .../AddressBookTestingTool/build.gradle.kts | 27 ++ .../AddressBookTestingTool/gradle.properties | 18 ++ .../AddressBookTestingToolMain.java | 116 +++++++++ .../AddressBookTestingToolState.java | 241 ++++++++++++++++++ .../addressbook/TransactionGenerator.java | 76 ++++++ .../src/main/java/module-info.java | 10 + platform-sdk/sdk/config.txt | 3 +- platform-sdk/settings.gradle.kts | 2 + .../common/system/address/Address.java | 12 +- .../platform/AddressBookInitializer.java | 3 + 11 files changed, 683 insertions(+), 4 deletions(-) create mode 100644 platform-sdk/platform-apps/tests/AddressBookTestingTool/README.md create mode 100644 platform-sdk/platform-apps/tests/AddressBookTestingTool/build.gradle.kts create mode 100644 platform-sdk/platform-apps/tests/AddressBookTestingTool/gradle.properties create mode 100644 platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/com/swirlds/demo/addressbook/AddressBookTestingToolMain.java create mode 100644 platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/com/swirlds/demo/addressbook/AddressBookTestingToolState.java create mode 100644 platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/com/swirlds/demo/addressbook/TransactionGenerator.java create mode 100644 platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/module-info.java diff --git a/platform-sdk/platform-apps/tests/AddressBookTestingTool/README.md b/platform-sdk/platform-apps/tests/AddressBookTestingTool/README.md new file mode 100644 index 000000000000..134577ec7777 --- /dev/null +++ b/platform-sdk/platform-apps/tests/AddressBookTestingTool/README.md @@ -0,0 +1,179 @@ +# AddressBookTestingTool Instructions + +This document describes various manual tests that were performed using this testing app. This playbook should be used when designing automated tests using this app. + +## Global Modifications and Starting Conditions + +Be sure to clean and assemble the whole project if the java files have been modified. + +### config.txt +comment out the current app +``` +# app, HashgraphDemo.jar, 1,0,0,0,0,0,0,0,0,0, all +``` +uncomment the AddressBookTestingTool.jar +``` + app, AddressBookTestingTool.jar, +``` + +### AddressTestingToolMain.java + +If following these instructions sequentially, ensure the software version is 1. + +```java +private static BasicSoftwareVersion softwareVersion = new BasicSoftwareVersion(1); +``` + +### AddressTestingToolState.java + +If following these instructions sequentially, ensure the staking profile is 1. + +```java +private int stakingProfile = 1; +``` + + + +## Testing Genesis Behavior + +### Force Use of Config Address Book on Genesis +#### Instructions +1. Delete `sdk/data/saved` directory if it exists +2. Ensure settings.txt has `state.saveStatePeriod, 0` +3. Add to settings.txt the value `state.forceUseOfConfigAddressBook, true` +4. Run the app for a short time. + +#### Validation + +* check the swirlds.log for the text + +``` +AddressBookInitializer: A setting has forced the use of the configuration address. +``` + +* check the directory `sdk/data/saved/address_book` for files + * usedAddressBook_v1_.txt + * matches the addresses in the config.txt, including stake value. + * usedAddressBook_v1_.txt.debug + * The configuration address book is the same as what is in config.txt + * the state saved address book was null + * the used address book text says `The Configuration Address Book Was Used.` + + +### Call to SwirldState.updateStake() on Genesis +#### Instructions +1. Delete `sdk/data/saved` directory if it exists +2. **Ensure settings.txt has `state.saveStatePeriod, 10` (differs from previous section)** +3. **Ensure settings.txt has the value `state.forceUseOfConfigAddressBook, false` (differs from previous section)** +4. **Run the app for 20 seconds. (differs from previous section)** + +#### Validation + +* check the swirlds.log for the text + +``` +AddressBookInitializer: The loaded signed state is null. The candidateAddressBook is set to genesisSwirldState.updateStake(configAddressBook, null). +``` + +* check the directory `sdk/data/saved/address_book` for files + * usedAddressBook_v1_.txt + * **contains the addresses in the config.txt, all with stake 10. (differs from previous section)** + * usedAddressBook_v1_.txt.debug + * The configuration address book is the same as what is in config.txt + * the state saved address book was null + * **the used address book matches the content of the non-debug .txt file. (differs from previous section)** + +## Testing Non-Genesis Behavior, No Software Upgrade + +### No Software Upgrade, Use Saved State Address Book +#### Instructions +1. Ensure settings.txt has `state.saveStatePeriod, 10` +2. Ensure settings.txt has the value `state.forceUseOfConfigAddressBook, false` +3. Run the app for 20 seconds. + +#### Validation + +* check the swirlds.log for the text + +``` +AddressBookInitializer: No Software Upgrade. Continuing with software version 1 and using the loaded signed state's address book and stake values. +``` + +* check the directory `sdk/data/saved/address_book` for the latest files + * usedAddressBook_v1_.txt + * contains the addresses in the config.txt, all with stake 10. + * usedAddressBook_v1_.txt.debug + * The configuration address book is the same as what is in config.txt + * **the state saved address book matches the content of the non-debug .txt file. (differs from previous section)** + * **the used address book has the text `The State Saved Address Book Was Used.` (differs from previous section)** + +### No Software Upgrade, Force Use of Config Address Book +#### Instructions +1. Ensure settings.txt has `state.saveStatePeriod, 10` +2. **Ensure settings.txt has the value `state.forceUseOfConfigAddressBook, true` (differs from previous section)** +3. Run the app for 20 seconds. + +#### Validation + +* check the swirlds.log for the text + +``` +AddressBookInitializer: A setting has forced the use of the configuration address. +``` + +* check the directory `sdk/data/saved/address_book` for the latest files + * usedAddressBook_v1_.txt + * **matches the addresses in the config.txt, including stake value. (differs from previous section)** + * usedAddressBook_v1_.txt.debug + * The configuration address book is the same as what is in config.txt + * **the state saved address book contains the addresses in the config.txt, all with stake 10. (differs from previous section)** + * **the used address book has the text `The Configuration Address Book Was Used.` (differs from previous section)** + +### No Software Upgrade, Use Saved State Address Book, Matches Config +#### Instructions +1. Ensure settings.txt has `state.saveStatePeriod, 10` +2. **Ensure settings.txt has the value `state.forceUseOfConfigAddressBook, false` (differs from previous section)** +3. Run the app for 20 seconds. + +#### Validation + +* check the swirlds.log for the text + +``` +AddressBookInitializer: No Software Upgrade. Continuing with software version 1 and using the loaded signed state's address book and stake values. +``` + +* check the directory `sdk/data/saved/address_book` for the latest files + * usedAddressBook_v1_.txt + * matches the addresses in the config.txt, including stake value. + * usedAddressBook_v1_.txt.debug + * The configuration address book is the same as what is in config.txt + * **The state saved address book is the same as what is in the config.txt (differs from previous section)** + * **the used address book has the text `The State Saved Address Book Was Used.` (differs from previous section)** + +## Testing Non-Genesis Behavior, Software Upgrade + +### Software Upgrade, Force Use of Config Address Book +#### Instructions +1. Ensure settings.txt has `state.saveStatePeriod, 10` +2. Ensure settings.txt has the value `state.forceUseOfConfigAddressBook, false` +3. **Increase the softwareVersion in AddressBookTestingToolMain.java to 2. (differs from previous section)** +4. **Change the stakingProfile in AddressBookTestingToolState.java to 2. (differs from previous section)** +5. **recompile the application: assemble ONLY!!!!.(differs from previous section)** +6. Run the app for 20 seconds. + +#### Validation + +* check the swirlds.log for the text + +``` +AddressBookInitializer: Software Upgrade from version 1 to 2. The address book stake will be updated by the saved state's SwirldState. +``` + +* check the directory `sdk/data/saved/address_book` for the latest files + * usedAddressBook_v1_.txt + * **matches the addresses in the config.txt, but the stake values incrementally increase starting from 0. (differs from previous section)** + * usedAddressBook_v1_.txt.debug + * The configuration address book is the same as what is in config.txt + * The state saved address book is the same as what is in config.txt + * **the used address book matches the content of the non-debug .txt file. (differs from previous section)** diff --git a/platform-sdk/platform-apps/tests/AddressBookTestingTool/build.gradle.kts b/platform-sdk/platform-apps/tests/AddressBookTestingTool/build.gradle.kts new file mode 100644 index 000000000000..42b6aec135bd --- /dev/null +++ b/platform-sdk/platform-apps/tests/AddressBookTestingTool/build.gradle.kts @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2020-2023 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. + */ + +plugins { + id("com.swirlds.platform.conventions") + id("com.swirlds.platform.application") +} + +dependencies { + // Individual Dependencies + implementation(project(":swirlds-platform-core")) + implementation(libs.bundles.logging.impl) + compileOnly(libs.spotbugs.annotations) +} diff --git a/platform-sdk/platform-apps/tests/AddressBookTestingTool/gradle.properties b/platform-sdk/platform-apps/tests/AddressBookTestingTool/gradle.properties new file mode 100644 index 000000000000..9b4e94c7647a --- /dev/null +++ b/platform-sdk/platform-apps/tests/AddressBookTestingTool/gradle.properties @@ -0,0 +1,18 @@ +# +# Copyright 2016-2022 Hedera Hashgraph, LLC +# +# This software is the confidential and proprietary information of +# Hedera Hashgraph, LLC. ("Confidential Information"). You shall not +# disclose such Confidential Information and shall use it only in +# accordance with the terms of the license agreement you entered into +# with Hedera Hashgraph. +# +# HEDERA HASHGRAPH MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF +# THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +# TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE, OR NON-INFRINGEMENT. HEDERA HASHGRAPH SHALL NOT BE LIABLE FOR +# ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR +# DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. +# + +mainClass=com.swirlds.demo.addressbook.AddressBookTestingToolMain diff --git a/platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/com/swirlds/demo/addressbook/AddressBookTestingToolMain.java b/platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/com/swirlds/demo/addressbook/AddressBookTestingToolMain.java new file mode 100644 index 000000000000..27dc439664e0 --- /dev/null +++ b/platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/com/swirlds/demo/addressbook/AddressBookTestingToolMain.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2022-2023 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.demo.addressbook; + +import static com.swirlds.logging.LogMarker.STARTUP; + +import com.swirlds.common.system.BasicSoftwareVersion; +import com.swirlds.common.system.NodeId; +import com.swirlds.common.system.Platform; +import com.swirlds.common.system.PlatformWithDeprecatedMethods; +import com.swirlds.common.system.SwirldMain; +import com.swirlds.common.system.SwirldState; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.security.SecureRandom; +import java.util.Objects; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + *

+ * An application that updates address book stake weights on version upgrade. + *

+ * + *

+ * Arguments: + *

    + *
  1. + * No arguments parsed at this time. The software version must be updated through setting the static value in + * this main class and recompiling. The behavior of staking is updated in the State class and recompiling. + *
  2. + *
+ */ +public class AddressBookTestingToolMain implements SwirldMain { + /** The logger for this class. */ + private static final Logger logger = LogManager.getLogger(AddressBookTestingToolMain.class); + + /** The default software version of this application. */ + private static final BasicSoftwareVersion softwareVersion = new BasicSoftwareVersion(1); + + /** The platform. */ + private Platform platform; + + /** The number of transactions to generate per second. */ + private static final int TRANSACTIONS_PER_SECOND = 100; + + public AddressBookTestingToolMain() { + logger.info(STARTUP.getMarker(), "constructor called in Main."); + } + + /** + * {@inheritDoc} + */ + @Override + public void init(@NonNull final Platform platform, @NonNull final NodeId id) { + Objects.requireNonNull(platform, "The platform must not be null."); + Objects.requireNonNull(id, "The node id must not be null."); + + logger.info(STARTUP.getMarker(), "init called in Main for node {}.", id); + this.platform = platform; + + parseArguments(((PlatformWithDeprecatedMethods) platform).getParameters()); + } + + /** + * Parses the arguments. Currently, no arguments are expected. + * + * @param args the arguments + * @throws IllegalArgumentException if the arguments array has length other than 0. + */ + private void parseArguments(@NonNull final String[] args) { + Objects.requireNonNull(args, "The arguments must not be null."); + if (args.length != 0) { + throw new IllegalArgumentException("Expected no arguments. See javadocs for details."); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void run() { + logger.info(STARTUP.getMarker(), "run called in Main."); + new TransactionGenerator(new SecureRandom(), platform, TRANSACTIONS_PER_SECOND).start(); + } + + /** + * {@inheritDoc} + */ + @Override + public SwirldState newState() { + return new AddressBookTestingToolState(); + } + + /** + * {@inheritDoc} + */ + @Override + public BasicSoftwareVersion getSoftwareVersion() { + logger.info(STARTUP.getMarker(), "returning software version {}", softwareVersion); + return softwareVersion; + } +} diff --git a/platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/com/swirlds/demo/addressbook/AddressBookTestingToolState.java b/platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/com/swirlds/demo/addressbook/AddressBookTestingToolState.java new file mode 100644 index 000000000000..db18294f0141 --- /dev/null +++ b/platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/com/swirlds/demo/addressbook/AddressBookTestingToolState.java @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2022-2023 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.demo.addressbook; +/* + * This file is public domain. + * + * SWIRLDS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF + * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SWIRLDS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. + */ + +import static com.swirlds.logging.LogMarker.STARTUP; + +import com.swirlds.common.io.streams.SerializableDataInputStream; +import com.swirlds.common.io.streams.SerializableDataOutputStream; +import com.swirlds.common.merkle.MerkleLeaf; +import com.swirlds.common.merkle.impl.PartialMerkleLeaf; +import com.swirlds.common.system.InitTrigger; +import com.swirlds.common.system.Platform; +import com.swirlds.common.system.PlatformWithDeprecatedMethods; +import com.swirlds.common.system.Round; +import com.swirlds.common.system.SoftwareVersion; +import com.swirlds.common.system.SwirldDualState; +import com.swirlds.common.system.SwirldState; +import com.swirlds.common.system.address.AddressBook; +import com.swirlds.common.system.events.ConsensusEvent; +import com.swirlds.common.system.transaction.ConsensusTransaction; +import com.swirlds.common.utility.ByteUtils; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.io.IOException; +import java.util.Iterator; +import java.util.Objects; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * State for the AddressBookTestingTool. + */ +public class AddressBookTestingToolState extends PartialMerkleLeaf implements SwirldState, MerkleLeaf { + + private static final Logger logger = LogManager.getLogger(AddressBookTestingToolState.class); + + /** modify this value to change how updateStake behaves. */ + private static final int STAKING_PROFILE = 1; + + private static class ClassVersion { + public static final int ORIGINAL = 1; + } + + private static final long CLASS_ID = 0xf052378c7364ef47L; + + private long selfId; + + /** + * The true "state" of this app. Each transaction is just an integer that gets added to this value. + */ + private long runningSum = 0; + + public AddressBookTestingToolState() { + logger.info(STARTUP.getMarker(), "New State Constructed."); + } + + /** + * Copy constructor. + */ + private AddressBookTestingToolState(@NonNull final AddressBookTestingToolState that) { + super(that); + Objects.requireNonNull(that, "the address book testing tool state to copy cannot be null"); + this.runningSum = that.runningSum; + this.selfId = that.selfId; + } + + /** + * {@inheritDoc} + */ + @Override + public synchronized AddressBookTestingToolState copy() { + throwIfImmutable(); + return new AddressBookTestingToolState(this); + } + + /** + * {@inheritDoc} + */ + @Override + public void init( + @NonNull final Platform platform, + @NonNull final SwirldDualState swirldDualState, + @NonNull final InitTrigger trigger, + @Nullable final SoftwareVersion previousSoftwareVersion) { + Objects.requireNonNull(platform, "the platform cannot be null"); + Objects.requireNonNull(swirldDualState, "the swirld dual state cannot be null"); + Objects.requireNonNull(trigger, "the init trigger cannot be null"); + + logger.info(STARTUP.getMarker(), "init called in State."); + throwIfImmutable(); + + if (trigger == InitTrigger.GENESIS) { + parseArguments(((PlatformWithDeprecatedMethods) platform).getParameters()); + } + + this.selfId = platform.getSelfId().getId(); + } + + /** + * {@inheritDoc} + */ + @Override + public void handleConsensusRound(@NonNull final Round round, @NonNull final SwirldDualState swirldDualState) { + Objects.requireNonNull(round, "the round cannot be null"); + Objects.requireNonNull(swirldDualState, "the swirld dual state cannot be null"); + throwIfImmutable(); + + final Iterator eventIterator = round.iterator(); + + while (eventIterator.hasNext()) { + final ConsensusEvent event = eventIterator.next(); + event.consensusTransactionIterator().forEachRemaining(this::handleTransaction); + } + } + + /** + * Apply a transaction to the state. + * + * @param transaction the transaction to apply + */ + private void handleTransaction(@NonNull final ConsensusTransaction transaction) { + final int delta = ByteUtils.byteArrayToInt(transaction.getContents(), 0); + runningSum += delta; + } + + /** + * {@inheritDoc} + */ + @Override + public void serialize(@NonNull final SerializableDataOutputStream out) throws IOException { + Objects.requireNonNull(out, "the serializable data output stream cannot be null"); + out.writeLong(runningSum); + } + + /** + * {@inheritDoc} + */ + @Override + public void deserialize(@NonNull final SerializableDataInputStream in, final int version) throws IOException { + Objects.requireNonNull(in, "the serializable data input stream cannot be null"); + runningSum = in.readLong(); + } + + /** + * {@inheritDoc} + */ + private void parseArguments(@NonNull final String[] args) { + if (args.length != 0) { + throw new IllegalArgumentException("Expected no arguments. See javadocs for details."); + } + } + + /** + * {@inheritDoc} + */ + @Override + public long getClassId() { + return CLASS_ID; + } + + /** + * {@inheritDoc} + */ + @Override + public int getVersion() { + return ClassVersion.ORIGINAL; + } + + /** + * {@inheritDoc} + */ + @Override + @NonNull + public AddressBook updateStake(@NonNull final AddressBook addressBook) { + Objects.requireNonNull(addressBook, "the address book cannot be null"); + logger.info("updateStake called in State. Staking Profile: {}", STAKING_PROFILE); + switch (STAKING_PROFILE) { + case 1: + return stakingProfile1(addressBook); + case 2: + return stakingProfile2(addressBook); + default: + logger.info(STARTUP.getMarker(), "Staking Profile {}: no change to address book.", STAKING_PROFILE); + return addressBook; + } + } + + /** + * All nodes received 10 stake. + * + * @param addressBook the address book to update. + * @return the updated address book. + */ + @NonNull + private AddressBook stakingProfile1(@NonNull final AddressBook addressBook) { + logger.info(STARTUP.getMarker(), "Staking Profile 1: updating all nodes to have 10 stake."); + for (int i = 0; i < addressBook.getSize(); i++) { + addressBook.updateStake(i, 10); + } + return addressBook; + } + + /** + * All nodes received stake equal to their nodeId. + * + * @param addressBook the address book to update. + * @return the updated address book. + */ + @NonNull + private AddressBook stakingProfile2(@NonNull final AddressBook addressBook) { + logger.info(STARTUP.getMarker(), "Staking Profile 2: updating all nodes to have stake equal to their nodeId."); + for (int i = 0; i < addressBook.getSize(); i++) { + addressBook.updateStake(i, i); + } + return addressBook; + } +} diff --git a/platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/com/swirlds/demo/addressbook/TransactionGenerator.java b/platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/com/swirlds/demo/addressbook/TransactionGenerator.java new file mode 100644 index 000000000000..a87b6a62bc49 --- /dev/null +++ b/platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/com/swirlds/demo/addressbook/TransactionGenerator.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2022-2023 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.demo.addressbook; + +import static com.swirlds.common.threading.manager.AdHocThreadManager.getStaticThreadManager; +import static com.swirlds.common.utility.ByteUtils.intToByteArray; + +import com.swirlds.common.system.Platform; +import com.swirlds.common.threading.framework.StoppableThread; +import com.swirlds.common.threading.framework.config.StoppableThreadConfiguration; +import com.swirlds.common.utility.Startable; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Objects; +import java.util.Random; + +/** + * Generates and submits transactional workload. + */ +public class TransactionGenerator implements Startable { + + private final Random random; + private final Platform platform; + + private final StoppableThread thread; + + public TransactionGenerator( + @NonNull final Random random, + @NonNull final Platform platform, + final int networkWideTransactionsPerSecond) { + Objects.requireNonNull(random, "The random number generator must not be null"); + Objects.requireNonNull(platform, "The platform must not be null"); + this.random = random; + this.platform = platform; + + // Each node in an N node network should create 1/N transactions per second. + final int tps = + networkWideTransactionsPerSecond / platform.getAddressBook().getSize(); + + thread = new StoppableThreadConfiguration<>(getStaticThreadManager()) + .setComponent("addressbook-testing-tool") + .setThreadName("transaction-generator") + .setMaximumRate(tps) + .setWork(this::generateTransaction) + .build(); + } + + /** + * {@inheritDoc} + */ + @Override + public void start() { + thread.start(); + } + + /** + * Generate and submit a single transaction. + */ + private void generateTransaction() { + // Transactions are simple: take an integer, and add it into the running sum. + platform.createTransaction(intToByteArray(random.nextInt())); + } +} diff --git a/platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/module-info.java b/platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/module-info.java new file mode 100644 index 000000000000..0945fbfdff87 --- /dev/null +++ b/platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/module-info.java @@ -0,0 +1,10 @@ +module com.swirlds.demo.addressbook { + requires com.swirlds.platform; + requires com.swirlds.common; + requires com.swirlds.logging; + requires lazysodium.java; + requires org.bouncycastle.provider; + requires org.bouncycastle.pkix; + requires org.apache.logging.log4j; + requires static com.github.spotbugs.annotations; +} diff --git a/platform-sdk/sdk/config.txt b/platform-sdk/sdk/config.txt index 32096939b920..fe105c9bc569 100644 --- a/platform-sdk/sdk/config.txt +++ b/platform-sdk/sdk/config.txt @@ -17,7 +17,8 @@ swirld, 123 # app, FCMStatsTestingTool.jar, 0, 0, -1, 250 # app, PlatformTestingTool.jar, FCMFCQ-Basic-1k-10m.json # app, MigrationTestingTool.jar, 4685095852903873060, 5000, 4 -# app, ISSTestingTool.jar, 10000, 60:0-1+2+3, 120:0-1-2+3 +# app, ISSTestingTool.jar, 10000, 60:0-1+2+3, 120:0-1-2+3 +# app, AddressBookTestingTool.jar, # ** END REMOVE FROM SDK RELEASES ** address, A, Alice, 1, 127.0.0.1, 15301, 127.0.0.1, 15301 diff --git a/platform-sdk/settings.gradle.kts b/platform-sdk/settings.gradle.kts index b41b70b9ac8e..778b31d92917 100644 --- a/platform-sdk/settings.gradle.kts +++ b/platform-sdk/settings.gradle.kts @@ -58,6 +58,8 @@ include(":swirlds-platform-apps:demos:HelloSwirldDemo") include(":swirlds-platform-apps:demos:StatsDemo") +include(":swirlds-platform-apps:tests:AddressBookTestingTool") + include(":swirlds-platform-apps:tests:ISSTestingTool") include(":swirlds-platform-apps:tests:MigrationTestingTool") diff --git a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/system/address/Address.java b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/system/address/Address.java index 7cd9bfb895ee..66acdac3e9fc 100644 --- a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/system/address/Address.java +++ b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/system/address/Address.java @@ -842,9 +842,15 @@ public boolean equalsWithoutStake(@NonNull final Address address) { && Arrays.equals(addressExternalIpv4, address.addressExternalIpv4) && Arrays.equals(addressInternalIpv6, address.addressInternalIpv6) && Arrays.equals(addressExternalIpv6, address.addressExternalIpv6) - && Objects.equals(sigPublicKey, address.sigPublicKey) - && Objects.equals(encPublicKey, address.encPublicKey) - && Objects.equals(agreePublicKey, address.agreePublicKey) + && Arrays.equals( + sigPublicKey.getPublicKey().getEncoded(), + address.sigPublicKey.getPublicKey().getEncoded()) + && Arrays.equals( + encPublicKey.getPublicKey().getEncoded(), + address.encPublicKey.getPublicKey().getEncoded()) + && Arrays.equals( + agreePublicKey.getPublicKey().getEncoded(), + address.agreePublicKey.getPublicKey().getEncoded()) && Objects.equals(memo, address.memo); } diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/AddressBookInitializer.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/AddressBookInitializer.java index 0bc0f51365c5..bb2bcda85197 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/AddressBookInitializer.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/AddressBookInitializer.java @@ -153,6 +153,9 @@ public AddressBook getInitialAddressBook() { private AddressBook initialize() { AddressBook candidateAddressBook; if (useConfigAddressBook) { + logger.info( + STARTUP.getMarker(), + "Overriding the address book in the state with the address book from config.txt"); // configuration is overriding to force use of configuration address book. candidateAddressBook = configAddressBook; } else if (loadedSignedState == null || loadedAddressBook == null) { From 26fd0efde85c6740d477e196bc0efbe38e6fcb16 Mon Sep 17 00:00:00 2001 From: Austin Littley <102969658+alittley@users.noreply.github.com> Date: Thu, 30 Mar 2023 10:43:28 -0400 Subject: [PATCH 10/13] 05757 event stream signing PCLI (#5858) Signed-off-by: Austin Littley --- .../platform/cli/EventStreamSignCommand.java | 61 ++++++ .../com/swirlds/platform/cli/SignCommand.java | 196 ++++++++++++++++++ .../util/EventStreamSigningUtils.java | 56 ++--- .../util/EventStreamSigningUtilsTests.java | 44 +--- 4 files changed, 274 insertions(+), 83 deletions(-) create mode 100644 platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/EventStreamSignCommand.java create mode 100644 platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/SignCommand.java diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/EventStreamSignCommand.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/EventStreamSignCommand.java new file mode 100644 index 000000000000..aa9819b3a5b7 --- /dev/null +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/EventStreamSignCommand.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 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.cli; + +import com.swirlds.cli.commands.EventStreamCommand; +import com.swirlds.cli.utility.SubcommandOf; +import com.swirlds.common.stream.EventStreamType; +import com.swirlds.platform.util.EventStreamSigningUtils; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.nio.file.Path; +import java.security.KeyPair; +import picocli.CommandLine; + +/** + * A subcommand of the {@link SignCommand}, for signing event stream files + */ +@CommandLine.Command(name = "sign", mixinStandardHelpOptions = true, description = "Sign event stream files") +@SubcommandOf(EventStreamCommand.class) +public final class EventStreamSignCommand extends SignCommand { + /** + * {@inheritDoc} + */ + @Override + public boolean generateSignatureFile( + @NonNull Path signatureFileDestination, @NonNull Path fileToSign, @NonNull KeyPair keyPair) { + + return EventStreamSigningUtils.signEventStreamFile(signatureFileDestination, fileToSign, keyPair); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isFileSupported(@NonNull final Path path) { + return EventStreamType.getInstance().isStreamFile(path.toFile()); + } + + /** + * {@inheritDoc} + */ + @Override + public Integer call() { + EventStreamSigningUtils.initializeSystem(); + + return super.call(); + } +} diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/SignCommand.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/SignCommand.java new file mode 100644 index 000000000000..91553bb0715e --- /dev/null +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/cli/SignCommand.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2023 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.cli; + +import com.swirlds.cli.utility.AbstractCommand; +import com.swirlds.common.io.utility.FileUtils; +import com.swirlds.platform.util.FileSigningUtils; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.KeyPair; +import java.util.List; +import java.util.stream.Stream; +import picocli.CommandLine; + +/** + * An abstract command type for generating signature files + */ +public abstract class SignCommand extends AbstractCommand { + /** + * The paths to the sources that signature files will be generated for. Can contain individual files, as well as + * directories + *

+ * If an element of this list is a directory, then all files in that directory will be signed, recursively + *

+ * Defaults to the current working directory + */ + private List pathsToSign = List.of(FileUtils.getAbsolutePath()); + + /** + * The directory where signature files will be generated. Defaults to in-place signatures (null) + */ + @Nullable + private Path destinationDirectory = null; + + /** + * The path to the key file to use to generate signatures + */ + private Path keyFilePath; + + /** + * The password to the key file + */ + private String keyFilePassword; + + /** + * The alias of the key in the key file + */ + private String keyAlias; + + /** + * The key pair to use to generate signatures + */ + private KeyPair keyPair; + + @CommandLine.Parameters(description = "The path to the key file to use to generate signatures", index = "0") + private void setKeyFile(@NonNull final Path keyFilePath) { + this.keyFilePath = pathMustExist(keyFilePath.toAbsolutePath().normalize()); + } + + @CommandLine.Parameters(description = "The password to the key file", index = "1") + private void setKeyFilePassword(@NonNull final String keyFilePassword) { + this.keyFilePassword = keyFilePassword; + } + + @CommandLine.Parameters(description = "The alias of the key in the key file", index = "2") + private void setKeyAlias(@NonNull final String keyAlias) { + this.keyAlias = keyAlias; + } + + @CommandLine.Option( + names = {"-p", "--paths-to-sign"}, + description = "The paths to what will be signed. Can contain single files, as well as directories." + + "Defaults to the current working directory") + private void setPathsToSign(@NonNull final List pathsToSign) { + this.pathsToSign = pathsToSign.stream() + .map((path -> pathMustExist(path.toAbsolutePath().normalize()))) + .toList(); + } + + @CommandLine.Option( + names = {"-d", "--destination-directory"}, + description = "Specify the destination directory where signature files will be generated. If specified," + + "source files will be copied to the destination directory. If not specified," + + "the signature file will simply be generated in the same directory as the source file") + private void setDestinationDirectory(@NonNull final Path destinationDirectory) { + this.destinationDirectory = destinationDirectory.toAbsolutePath().normalize(); + } + + /** + * {@inheritDoc} + */ + @Override + public Integer call() { + keyPair = FileSigningUtils.loadPfxKey(keyFilePath, keyFilePassword, keyAlias); + + pathsToSign.forEach(this::signFilesAtPath); + + return 0; + } + + /** + * Generate a signature file for a single source file + * + * @param signatureFileDestination the full path where the signature file will be generated + * @param fileToSign the file to generate a signature file for + * @param keyPair the key pair to use to generate the signature + * @return true if the signature file was generated successfully, false otherwise + */ + public abstract boolean generateSignatureFile( + @NonNull final Path signatureFileDestination, + @NonNull final Path fileToSign, + @NonNull final KeyPair keyPair); + + /** + * Check if a file is supported by this command + * + * @param path the path to the file to check + * @return true if the file is supported, false otherwise + */ + public abstract boolean isFileSupported(@NonNull final Path path); + + /** + * Perform necessary tasks to sign a single file. Generates a signature file via {@link #generateSignatureFile} if + * one doesn't already exist + *

+ * If a destinationDirectory has been specified, the source file will additionally be copied to the destination + * directory + * + * @param fileToSign the file to generate a signature file for + */ + private void signFile(@NonNull final Path fileToSign) { + final Path signatureFileDestinationPath; + if (destinationDirectory == null) { + // if destinationDirectory is null, then we are generating in-place signatures + signatureFileDestinationPath = FileSigningUtils.buildSignatureFilePath(fileToSign.getParent(), fileToSign); + } else { + signatureFileDestinationPath = FileSigningUtils.buildSignatureFilePath(destinationDirectory, fileToSign); + } + + // if signature file already exists, don't continue + if (Files.exists(signatureFileDestinationPath)) { + System.out.println( + "Signature file " + signatureFileDestinationPath + " already exists. Skipping file " + fileToSign); + return; + } + + // if signature generation fails, don't continue + if (!generateSignatureFile(signatureFileDestinationPath, fileToSign, keyPair)) { + // don't print anything here, as a specific error message should be printed by the signing implementation + return; + } + + // if input destinationDirectory was null, then we generated in-place signatures. No need to copy source files + if (destinationDirectory == null) { + return; + } + + try { + Files.copy(fileToSign, destinationDirectory.resolve(fileToSign.getFileName())); + } catch (final IOException e) { + System.err.println("Failed to copy source file " + fileToSign.getFileName() + " to destination directory " + + destinationDirectory + ". Exception: " + e); + } + } + + /** + * Sign file(s) at a path. Individual files will simply be signed, and directories will have all supported files + * signed, recursively + * + * @param path the path to sign files at + */ + private void signFilesAtPath(@NonNull final Path path) { + try (final Stream stream = Files.walk(path)) { + stream.filter(this::isFileSupported).forEach(this::signFile); + } catch (final IOException e) { + throw new RuntimeException("Failed to list files: " + path); + } + } +} diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/util/EventStreamSigningUtils.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/util/EventStreamSigningUtils.java index 3948fba3a127..92d5a662beb0 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/util/EventStreamSigningUtils.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/util/EventStreamSigningUtils.java @@ -20,7 +20,6 @@ import static com.swirlds.common.stream.LinkedObjectStreamUtilities.computeMetaHash; import static com.swirlds.common.stream.LinkedObjectStreamUtilities.readFirstIntFromFile; import static com.swirlds.common.stream.internal.TimestampStreamFileWriter.writeSignatureFile; -import static com.swirlds.platform.util.FileSigningUtils.buildSignatureFilePath; import static com.swirlds.platform.util.FileSigningUtils.signData; import com.swirlds.common.crypto.Hash; @@ -30,7 +29,6 @@ import com.swirlds.common.stream.internal.InvalidStreamFileException; import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.security.InvalidKeyException; import java.security.KeyPair; @@ -38,7 +36,6 @@ import java.security.NoSuchProviderException; import java.security.SignatureException; import java.util.Objects; -import java.util.stream.Stream; /** * Utility class for signing event stream files @@ -74,16 +71,17 @@ public static void initializeSystem() { /** * Generates a signature file for the given event stream file * - * @param destinationDirectory the directory where the signature file will be saved - * @param streamFileToSign the stream file to be signed - * @param keyPair the keyPair used for signing + * @param signatureFileDestination the full path where the signature file will be generated + * @param streamFileToSign the stream file to be signed + * @param keyPair the keyPair used for signing + * @return true if the signature file was generated successfully, false otherwise */ - public static void signEventStreamFile( - @NonNull final Path destinationDirectory, + public static boolean signEventStreamFile( + @NonNull final Path signatureFileDestination, @NonNull final Path streamFileToSign, @NonNull final KeyPair keyPair) { - Objects.requireNonNull(destinationDirectory, "destinationDirectory must not be null"); + Objects.requireNonNull(signatureFileDestination, "signatureFileDestination must not be null"); Objects.requireNonNull(streamFileToSign, "streamFileToSign must not be null"); Objects.requireNonNull(keyPair, "keyPair must not be null"); @@ -93,7 +91,7 @@ public static void signEventStreamFile( System.err.printf( "Failed to sign file [%s] with unsupported version [%s]%n", streamFileToSign.getFileName(), version); - return; + return false; } final Hash entireHash = computeEntireHash(streamFileToSign.toFile()); @@ -106,50 +104,22 @@ public static void signEventStreamFile( final com.swirlds.common.crypto.Signature metaHashSignature = new com.swirlds.common.crypto.Signature(SignatureType.RSA, signData(metaHash.getValue(), keyPair)); - final Path signatureFilePath = buildSignatureFilePath(destinationDirectory, streamFileToSign); - writeSignatureFile( entireHash, entireHashSignature, metaHash, metaHashSignature, - signatureFilePath.toString(), + signatureFileDestination.toString(), streamType); - System.out.println("Generated signature file: " + signatureFilePath); + System.out.println("Generated signature file: " + signatureFileDestination); + + return true; } catch (final SignatureException | InvalidStreamFileException | IOException e) { System.err.println("Failed to sign file " + streamFileToSign.getFileName() + ". Exception: " + e); + return false; } catch (final InvalidKeyException | NoSuchAlgorithmException | NoSuchProviderException e) { throw new RuntimeException("Irrecoverable error encountered", e); } } - - /** - * Signs all event stream files in a directory - *

- * If a recoverable error is encountered while signing an individual file in the directory, an error will be logged, - * and signing of remaining files will continue - * - * @param sourceDirectory the source directory - * @param destinationDirectory the destination directory - * @param keyPair the key pair to sign with - */ - public static void signEventStreamFilesInDirectory( - @NonNull final Path sourceDirectory, - @NonNull final Path destinationDirectory, - @NonNull final KeyPair keyPair) { - - Objects.requireNonNull(sourceDirectory, "sourceDirectory must not be null"); - Objects.requireNonNull(destinationDirectory, "destinationDirectory must not be null"); - Objects.requireNonNull(keyPair, "keyPair must not be null"); - - final EventStreamType streamType = EventStreamType.getInstance(); - - try (final Stream stream = Files.walk(sourceDirectory)) { - stream.filter(filePath -> streamType.isStreamFile(filePath.toString())) - .forEach(path -> signEventStreamFile(destinationDirectory, path, keyPair)); - } catch (final IOException e) { - throw new RuntimeException("Failed to list files in directory: " + sourceDirectory); - } - } } diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/util/EventStreamSigningUtilsTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/util/EventStreamSigningUtilsTests.java index 0e9ffb197e04..078dd6a00d7f 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/util/EventStreamSigningUtilsTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/util/EventStreamSigningUtilsTests.java @@ -22,7 +22,6 @@ import static com.swirlds.platform.recovery.RecoveryTestUtils.writeRandomEventStream; import static com.swirlds.platform.util.EventStreamSigningUtils.initializeSystem; import static com.swirlds.platform.util.EventStreamSigningUtils.signEventStreamFile; -import static com.swirlds.platform.util.EventStreamSigningUtils.signEventStreamFilesInDirectory; import static com.swirlds.platform.util.FileSigningUtils.SIGNATURE_FILE_NAME_SUFFIX; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -144,7 +143,10 @@ void signSingleStreamFile() { throw new RuntimeException(e); } - signEventStreamFile(destinationDirectory, fileToSign, keyPair); + assertTrue( + signEventStreamFile( + FileSigningUtils.buildSignatureFilePath(destinationDirectory, fileToSign), fileToSign, keyPair), + "Signing failed"); final List destinationDirectoryFiles = getDestinationDirectoryFiles(); @@ -162,42 +164,4 @@ void signSingleStreamFile() { validateFileAndSignature( fileToSign.toFile(), signatureFile.toFile(), keyPair.getPublic(), EventStreamType.getInstance()); } - - @Test - @DisplayName("Sign stream files in directory") - void signStreamFiles() { - createStreamFiles(); - - final List filesToSign; - try (final Stream stream = Files.walk(toSignDirectory)) { - filesToSign = stream.filter( - filePath -> EventStreamType.getInstance().isStreamFile(filePath.toString())) - .toList(); - } catch (final IOException e) { - throw new RuntimeException("Failed to list files in directory: " + toSignDirectory, e); - } - - signEventStreamFilesInDirectory(toSignDirectory, destinationDirectory, keyPair); - - final List destinationDirectoryFiles = getDestinationDirectoryFiles(); - - assertNotNull(destinationDirectoryFiles, "Expected signature file to be created"); - assertEquals( - filesToSign.size(), - destinationDirectoryFiles.size(), - "Expected correct number of signature files to be created"); - - for (final Path originalFile : filesToSign) { - final Path expectedFile = - destinationDirectory.resolve(originalFile.getFileName() + SIGNATURE_FILE_NAME_SUFFIX); - - assertTrue( - destinationDirectoryFiles.contains(expectedFile), - "Expected signature file to be created for " + originalFile.getFileName()); - - // validate the stream file and sig file via the standard method - validateFileAndSignature( - originalFile.toFile(), expectedFile.toFile(), keyPair.getPublic(), EventStreamType.getInstance()); - } - } } From 14ab637918c414e8344770a5c615f5cb7dd2dddf Mon Sep 17 00:00:00 2001 From: David Bakin <117694041+david-bakin-sl@users.noreply.github.com> Date: Thu, 30 Mar 2023 11:13:31 -0700 Subject: [PATCH 11/13] Copy latest `bootstrap.properties` from main `hedera-node/hedera-mono-service` (#5705) Signed-off-by: David Bakin <117694041+david-bakin-sl@users.noreply.github.com> --- .../src/main/resource/bootstrap.properties | 71 ++++++++++++++----- 1 file changed, 52 insertions(+), 19 deletions(-) diff --git a/hedera-node/test-clients/src/main/resource/bootstrap.properties b/hedera-node/test-clients/src/main/resource/bootstrap.properties index c1b1f1d87b70..acca391fd54e 100644 --- a/hedera-node/test-clients/src/main/resource/bootstrap.properties +++ b/hedera-node/test-clients/src/main/resource/bootstrap.properties @@ -16,13 +16,17 @@ accounts.addressBookAdmin=55 accounts.exchangeRatesAdmin=57 accounts.feeSchedulesAdmin=56 accounts.freezeAdmin=58 +accounts.lastThrottleExempt=100 accounts.nodeRewardAccount=801 accounts.stakingRewardAccount=800 +accounts.storeOnDisk=false accounts.systemAdmin=50 accounts.systemDeleteAdmin=59 accounts.systemUndeleteAdmin=60 accounts.treasury=2 +autorenew.grantFreeRenewals=false entities.maxLifetime=3153600000 +entities.systemDeletable=FILE files.addressBook=101 files.networkProperties=121 files.exchangeRates=112 @@ -31,6 +35,7 @@ files.hapiPermissions=122 files.nodeDetails=102 files.softwareUpdateRange=150-159 files.throttleDefinitions=123 +hedera.firstUserEntity=1001 hedera.realm=0 hedera.shard=0 ledger.id=0x03 @@ -38,47 +43,64 @@ ledger.numSystemAccounts=100 ledger.totalTinyBarFloat=5000000000000000000 staking.periodMins=1 staking.rewardHistory.numStoredPeriods=3 +staking.startupHelper.recompute=NODE_STAKES,PENDING_REWARDS +tokens.storeRelsOnDisk=false hedera.workflows.enabled= # Dynamic properties accounts.maxNumber=5_000_000 autoCreation.enabled=true lazyCreation.enabled=true -cryptoCreateWithAlias.enabled=true +cryptoCreateWithAlias.enabled=false entities.limitTokenAssociations=false balances.exportDir.path=/opt/hgcapp/accountBalances/ balances.exportEnabled=true balances.exportPeriodSecs=900 balances.exportTokenBalances=true balances.nodeBalanceWarningThreshold=0 +balances.compressOnCreation=false cache.records.ttl=180 contracts.allowAutoAssociations=true contracts.allowCreate2=true +contracts.chainId=295 contracts.defaultLifetime=7890000 +contracts.enforceCreationThrottle=false contracts.evm.version=v0.34 contracts.evm.version.dynamic=false +contracts.permittedDelegateCallers=1062787,1461860 contracts.freeStorageTierLimit=100 contracts.itemizeStorageFees=true +contracts.keys.legacyActivations=1058134by[1062784] +contracts.knownBlockHash= contracts.localCall.estRetBytes=32 contracts.maxGasPerSec=15000000 contracts.maxKvPairs.aggregate=500_000_000 contracts.maxKvPairs.individual=163_840 contracts.maxNumber=5_000_000 -contracts.chainId=295 -contracts.throttle.throttleByGas=false contracts.maxRefundPercentOfGasLimit=20 -contracts.scheduleThrottleMaxGasLimit=5000000 -contracts.sidecars= -contract.storageSlotPriceTiers=0til100M,2000til450M -contracts.redirectTokenCalls=true -contracts.precompile.htsDefaultGasCost=10000 +contracts.precompile.exchangeRateGasCost=100 contracts.precompile.exportRecordResults=true +contracts.precompile.htsDefaultGasCost=10000 contracts.precompile.htsEnableTokenCreate=true +# Current implementation requires two storage get()'s to remove a slot +expiry.minCycleEntryCapacity=ACCOUNTS_GET,ACCOUNTS_GET_FOR_MODIFY,STORAGE_GET,STORAGE_GET,STORAGE_REMOVE,STORAGE_PUT +expiry.throttleResource=expiry-throttle.json +contracts.redirectTokenCalls=true +contracts.referenceSlotLifetime=31536000 +contracts.scheduleThrottleMaxGasLimit=5000000 +contracts.sidecars=CONTRACT_STATE_CHANGE,CONTRACT_BYTECODE,CONTRACT_ACTION +contracts.sidecarValidationEnabled=false +contract.storageSlotPriceTiers=0til100M,2000til450M +contracts.throttle.throttleByGas=false contracts.precompile.atomicCryptoTransfer.enabled=true fees.minCongestionPeriod=60 fees.percentCongestionMultipliers=90,10x,95,25x,99,100x +# Disable entity utilization congestion pricing by default +fees.percentUtilizationScaleFactors=DEFAULT(0,1:1) fees.tokenTransferUsageMultiplier=380 files.maxNumber=1_000_000 files.maxSizeKb=1024 +hedera.recordStream.enableTraceabilityMigration=true +hedera.recordStream.sidecarMaxSizeMb=256 hedera.transaction.maxMemoUtf8Bytes=100 hedera.transaction.maxValidDuration=180 hedera.transaction.minValidDuration=15 @@ -98,7 +120,6 @@ ledger.autoRenewPeriod.minDuration=6999999 ledger.changeHistorian.memorySecs=20 ledger.xferBalanceChanges.maxLen=20 ledger.fundingAccount=98 -ledger.maxAccountNum=100000000 ledger.transfers.maxLen=10 ledger.tokenTransfers.maxLen=10 ledger.nftTransfers.maxLen=10 @@ -111,11 +132,22 @@ scheduling.longTermEnabled=false scheduling.maxNumber=10_000_000 scheduling.maxTxnPerSecond=100 scheduling.maxExpirationFutureSeconds=5356800 -sigs.expandFromLastSignedState=true +sigs.expandFromImmutableState=true +staking.fees.nodeRewardPercentage=10 +staking.fees.stakingRewardPercentage=10 +staking.maxDailyStakeRewardThPerH=100 +traceability.maxExportsPerConsSec=10 +traceability.minFreeToUsedGasThrottleRatio=9 +# A possibly empty comma-separated : list; default ratio is 4 +staking.nodeMaxToMinStakeRatios= +staking.isEnabled=true +staking.rewardRate=100_000_000_000 +staking.requireMinStakeToReward=false +staking.startThreshold=100_000_000 tokens.maxAggregateRels=10_000_000 tokens.maxNumber=1_000_000 -tokens.maxRelsPerInfoQuery=1000 tokens.maxPerAccount=1000 +tokens.maxRelsPerInfoQuery=1000 tokens.maxSymbolUtf8Bytes=100 tokens.maxTokenNameUtf8Bytes=100 tokens.maxCustomFeesAllowed=10 @@ -129,6 +161,7 @@ tokens.nfts.maxAllowedMints=5000000 tokens.nfts.maxQueryRange=100 tokens.nfts.mintThrottleScaleFactor=5:2 tokens.nfts.useTreasuryWildcards=true +tokens.nfts.useVirtualMerkle=false topics.maxNumber=1_000_000 upgrade.artifacts.path=/opt/hgcapp/services-hedera/HapiApp2.0/data/upgrade/current # Node properties (can be overridden via data/config/node.properties) @@ -143,9 +176,13 @@ hedera.exportAccountsOnStartup=false hedera.profiles.active=PROD hedera.recordStream.isEnabled=true hedera.recordStream.logDir=/opt/hgcapp/recordStreams -hedera.recordStream.sidecarDir= +# hedera.recordStream.sidecarDir is specified relative to hedera.recordStream.logDir; blank==same dir +hedera.recordStream.sidecarDir=sidecar hedera.recordStream.logPeriod=2 hedera.recordStream.queueCapacity=5000 +hedera.recordStream.recordFileVersion=6 +hedera.recordStream.signatureFileVersion=6 +hedera.recordStream.logEveryTransaction=false hedera.recordStream.compressFilesOnCreation=false iss.resetPeriod=60 iss.roundsToLog=5000 @@ -162,6 +199,8 @@ netty.startRetryIntervalMs=1000 netty.tlsCrt.path=hedera.crt netty.tlsKey.path=hedera.key queries.blob.lookupRetries=3 +stats.consThrottlesToSample=,ThroughputLimits,CreationLimits +stats.hapiThrottlesToSample=,ThroughputLimits,OffHeapQueryLimits,CreationLimits,FreeQueryLimits stats.executionTimesToTrack=0 stats.entityUtils.gaugeUpdateIntervalMs=3000 stats.hapiOps.speedometerUpdateIntervalMs=3000 @@ -171,12 +210,6 @@ stats.speedometerHalfLifeSecs=10.0 hedera.prefetch.queueCapacity=70000 hedera.prefetch.threadPoolSize=4 hedera.prefetch.codeCacheTtlSecs=600 -staking.fees.nodeRewardPercentage=10 -staking.fees.stakingRewardPercentage=10 -staking.isEnabled=true -staking.maxDailyStakeRewardThPerH=100 -staking.rewardRate=100_000_000_000 -staking.startThreshold=100_000_000 utilPrng.isEnabled=true tokens.autoCreations.isEnabled=true -virtualdatasource.jasperdbToMerkledb=false \ No newline at end of file +virtualdatasource.jasperdbToMerkledb=false From d4fd2926ff95109f22f798fbc96d63a82a47b86f Mon Sep 17 00:00:00 2001 From: Edward Wertz <123979964+edward-swirldslabs@users.noreply.github.com> Date: Thu, 30 Mar 2023 14:24:12 -0500 Subject: [PATCH 12/13] 05895 - fixing startup crash due to new SwirldMain configuration (#5896) Signed-off-by: Edward Wertz --- .../src/main/java/com/swirlds/platform/Browser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 82996b6aed31..f75d30593109 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 @@ -213,7 +213,7 @@ private Browser(final Set localNodesToStart) throws IOException { // Assume all locally run instances provide the same configuration definitions to the configuration builder. if (appMains.size() > 0) { - appMains.get(0L).updateConfigurationBuilder(configurationBuilder); + appMains.values().iterator().next().updateConfigurationBuilder(configurationBuilder); } this.configuration = configurationBuilder.build(); From 6583dc11d3e4f6014270ef650e856929f97a44d4 Mon Sep 17 00:00:00 2001 From: Cody Littley <56973212+cody-littley@users.noreply.github.com> Date: Thu, 30 Mar 2023 15:12:56 -0500 Subject: [PATCH 13/13] Write preconsensus event stream (#5633) Signed-off-by: Cody Littley --- .../docs/threads/eventflow-threads.drawio.svg | 2 +- .../docs/threads/eventflow-threads.html | 2 +- platform-sdk/sdk/settings.txt | 17 +- .../base/functions/ThrowingConsumer.java | 35 ++++ .../src/main/java/module-info.java | 1 + .../swirlds/common/config/StateConfig.java | 3 +- .../common/io/config/TemporaryFileConfig.java | 3 +- .../threading/interrupt/Uninterruptable.java | 123 ++++++++----- .../swirlds-platform-core/build.gradle.kts | 1 + .../java/com/swirlds/platform/Browser.java | 4 +- .../swirlds/platform/PlatformConstructor.java | 25 +-- .../com/swirlds/platform/SwirldsPlatform.java | 66 ++++++- .../platform/components/ConsensusWrapper.java | 2 +- .../platform/components/EventIntake.java | 59 +++---- .../DefaultStateManagementComponent.java | 54 ++++-- ...efaultStateManagementComponentFactory.java | 17 +- .../StateManagementComponentFactory.java | 8 + .../MinimumGenerationNonAncientConsumer.java | 31 ++++ .../components/wiring/ManualWiring.java | 58 +++--- .../AsyncPreConsensusEventWriter.java | 33 ++-- .../NoOpPreConsensusEventWriter.java | 104 +++++++++++ .../PreConsensusEventFileManager.java | 62 ++++--- .../PreConsensusEventStreamConfig.java | 10 +- .../PreconsensusEventMetrics.java | 4 +- .../SyncPreConsensusEventWriter.java | 91 +++++----- .../eventhandling/ConsensusRoundHandler.java | 35 ++-- .../platform/internal/ConsensusRound.java | 30 +++- .../metrics/ConsensusHandlingMetrics.java | 6 +- .../observers/EventObserverDispatcher.java | 17 +- .../state/signed/SignedStateFileManager.java | 40 +++-- .../src/main/java/module-info.java | 2 + .../swirlds/platform/ConsensusRoundTests.java | 4 +- .../platform/SignedStateFileManagerTests.java | 29 ++- .../SignedStateFileReadWriteTest.java | 3 +- .../state/StateManagementComponentTests.java | 4 +- .../ConsensusRoundHandlerTests.java | 2 + .../swirlds-common-test/build.gradle.kts | 1 + .../src/main/java/module-info.java | 1 + .../test/utility/UninterruptableTest.java | 27 +++ .../test/chatter/ChatterNotifierTest.java | 3 +- .../EventObserverDispatcherTests.java | 6 +- .../TransactionHandlingTestUtils.java | 2 +- .../event/intake/OrphanEventsIntakeTest.java | 7 +- .../AsyncPreConsensusEventWriterTests.java | 89 +++++----- .../PreConsensusEventFileManagerTests.java | 167 ++++++++---------- .../SyncPreConsensusEventWriterTests.java | 73 +++++--- .../test/eventflow/EventFlowTests.java | 1 + .../swirlds-virtualmap/build.gradle.kts | 1 + .../src/main/java/module-info.java | 1 + 49 files changed, 926 insertions(+), 440 deletions(-) create mode 100644 platform-sdk/swirlds-base/src/main/java/com/swirlds/base/functions/ThrowingConsumer.java create mode 100644 platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/state/output/MinimumGenerationNonAncientConsumer.java create mode 100644 platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/NoOpPreConsensusEventWriter.java diff --git a/platform-sdk/docs/threads/eventflow-threads.drawio.svg b/platform-sdk/docs/threads/eventflow-threads.drawio.svg index 24e735d2bc8c..743392c6c51c 100644 --- a/platform-sdk/docs/threads/eventflow-threads.drawio.svg +++ b/platform-sdk/docs/threads/eventflow-threads.drawio.svg @@ -1,4 +1,4 @@ -
SwirldsPlatform
SwirldsPlatform
createPrioritySystemTransaction
createPrioritySystemTransaction
SignatureTransmitter
SignatureTransmitter
Template
Template
ClassName
ClassName
ClassName
ClassName
PreConsensusEventHandler
PreConsensusEventHandler
PreConsensusEventObserver
PreConsensusEventObserver
EventObserverDispatcher
EventObserverDispatcher
EventIntake
EventIntake
Consumer<GossipEvent>
Consumer<GossipEvent>
EventValidator
EventValidator
EventTaskDispatcher
EventTaskDispatcher
Queue Thread Name
(thread-name)
Queue Thread Name...
Note: the name in the parenthesis should match the value set with QueueThreadConfiguration::setThreadName
Note: the name in the parenthes...
EventIntakeTask
EventIntakeTask
Event Intake Queue
(event-intake)
Event Intake Queue...
EventCreator
EventCreator
createEvent
createEvent
pathway not used with OOO gossip
pathway not used with OOO gossip
GossipEvent
GossipEvent
EventImpl
EventImpl
addUnlinkedEvent
addUnlinkedEvent
ConsensusRounds
ConsensusRounds
addEvent
addEvent
dispatchTask
dispatchTask
Conditional
Conditional
instanceof CreateEventTask
instanceof CreateEvent...
instanceof ValidEvent
instanceof ValidEvent
instanceof GossipEvent
instanceof GossipEvent
CreateEventTask
CreateEventTask
GossipEvent
GossipEvent
GossipEvent
GossipEvent
validateEvent
validateEvent
PreConsensusEventObserver
PreConsensusEventObserver
preConsensusEvent
preConsensusEvent
EventImpl
EventImpl
EventImpl
EventImpl
preConsensusEvent
preConsensusEvent
PreConsensus Event Queue
(thread-curr)
PreConsensus Event Queue...
SwirldStateManagerImpl
SwirldStateManagerImpl
SwirldStateManager
SwirldStateManager
handlePreConsensusEvent
handlePreConsensusEvent
EventImpl
EventImpl
preHandle
preHandle
EventImpl
EventImpl
DefaultStateManagementComponent
DefaultStateManagementComponent
ConsensusHashManager
ConsensusHashManager
SignedStateManager
SignedStateManager
postConsensusSignatureObserver
postConsensusSignatureObserver
signature info
signature info
preConsensusSignatureObserver
preConsensusSignatureObserver
signature info
signature info
TransactionHandler
TransactionHandler
preHandle
preHandle
EventImpl, state
EventImpl, state
SwirldState2
SwirldState2
preHandle
preHandle
transaction
transaction
ConsensusRound, state
ConsensusRound, state
handleConsensusRound
handleConsensusRound
round, dual state
round, dual state
handleRound
handleRound
handleConsensusRound
handleConsensusRound
ConsensusRoundHandler
ConsensusRoundHandler
Consensus Round Queue
(thread-cons)
Consensus Round Queue...
InterruptableConsumer<ConsensusRound>
InterruptableConsumer<ConsensusRound>
signed state
signed state
applyConsensusRoundToState
applyConsensusRoundToState
ConsensusRound
ConsensusRound
ConsensusRound
ConsensusRound
ConsensusRound
ConsensusRound
addConsensusRound
addConsensusRound
EventStreamManager
EventStreamManager
ConsensusRoundObserver
ConsensusRoundObserver
ConsensusRound
ConsensusRound
consensusRound
consensusRound
EventImpl
EventImpl
addEvents
addEvents
EventImpls
EventImpls
addEvent
addEvent
Multistream
Multistream
LinkedObjectStream
LinkedObjectStream
EventImpl
EventImpl
addObject
addObject
EventImpl
EventImpl
Hash Queue Thread
(hash-queue)
Hash Queue Thread...
Write Queue Thread
(write-queue)
Write Queue Thread...
EventImpl
EventImpl
ConsensusRoundObserver
ConsensusRoundObserver
ConsensusRound
ConsensusRound
consensusRound
consensusRound
ConsensusRound
ConsensusRound
handleConsensus
handleConsensus
OrphanBufferingLinker
OrphanBufferingLinker
linkEvent
linkEvent
GossipEvent
GossipEvent
pollLinkedEvent
pollLinkedEvent
calls
calls
updateGenerations
updateGenerations
EventImpl
EventImpl
TODO there are more observers
TODO there a...
ConsensusWrapper
ConsensusWrapper
addEvent
addEvent
EventImpl
EventImpl
EventAddedObserver
EventAddedObserver
eventAdded
eventAdded
ConsensusRounds
ConsensusRounds
InterfaceName
InterfaceName
interfaceMethod
interfaceMethod
classMethod
classMethod
EventTaskCreator
EventTaskCreator
TODO
TODO
handleMethod
handleMethod
QueueElement
QueueElement
The color of a thread should be reflected in the arrows between methods
The color of a thread...
TODO there are more observers
TODO there a...
PostConsensusSystemTransactionManagerImpl
PostConsensusSystemTransactionManagerImpl
PreConsensusSystemTransactionManagerImpl
PreConsensusSystemTransactionManagerImpl
PostConsensusSystemTransactionManager
PostConsensusSystemTransactionManager
handleRound
handleRound
PreConsensusSystemTransactionManager
PreConsensusSystemTransactionManager
handleEvent
handleEvent
EventImpl
EventImpl
ConsensusRound, state
ConsensusRound, state
handleStateSignatureTransactionPreConsensus
handleStateSignatureTransactionPreConsensus
handleStateSignatureTransactionPostConsensus
handleStateSignatureTransactionPostConsensus
StateSignatureTransactions
StateSignatureTransactions
StateSignatureTransactions
StateSignatureTransactions
Node Mindmap
Node Mindmap
getStateForSigning
getStateForSigning
copy
copy
State Hashing Queue
(state-hash-sign)
State Hashing Queue...
NewSignedStateFromTransactionsConsumer
NewSignedStateFromTransactionsConsumer
newSignedStateFromTransactions
newSignedStateFromTransactions
SignedStateHasher
SignedStateHasher
hashState
hashState
submitTransaction
submitTransaction
EventTransactionPool
EventTransactionPool
submitTransaction
submitTransaction
addUnsignedState
addUnsignedState
transmitSignature
transmitSignature
RoundAppliedToStateConsumer
RoundAppliedToStateConsumer
roundAppliedToState
roundAppliedToState
round number
round number
roundCompleted
roundCompleted
HashLogger
HashLogger
logHashes
logHashes
SignedStateFileManager
SignedStateFileManager
StateHasEnoughSignaturesConsumer
StateHasEnoughSignaturesConsumer
stateHasEnoughSignatures
stateHasEnoughSignatures
saveSignedStateToDisk
saveSignedStateToDisk
FreezeManager
FreezeManager
stateHasEnoughSignatures
stateHasEnoughSignatures
EventCreationRule
EventCreationRule
shouldCreateEvent
shouldCreateEvent
TransactionSupplier
TransactionSupplier
getTransactions
getTransactions
StateLacksSignaturesConsumer
StateLacksSignaturesConsumer
stateLacksSignatures
stateLacksSignatures
purgeOldStates
purgeOldStates
file system
file system
State to disk
(signed-state-file-manager)
State to disk...
(lambda)
(lambda)
ManualWiring
ManualWiring
(lambda)
(lambda)
stateToDisk
stateToDisk
stateHashedObserver
stateHashedObserver
Text is not SVG - cannot display
\ No newline at end of file +
PreConsensensusEventWriter
PreConsensensusEventWriter
SwirldsPlatform
SwirldsPlatform
createPrioritySystemTransaction
createPrioritySystemTransaction
SignatureTransmitter
SignatureTransmitter
Template
Template
ClassName
ClassName
ClassName
ClassName
PreConsensusEventHandler
PreConsensusEventHandler
PreConsensusEventObserver
PreConsensusEventObserver
EventObserverDispatcher
EventObserverDispatcher
EventIntake
EventIntake
Consumer<GossipEvent>
Consumer<GossipEvent>
EventValidator
EventValidator
EventTaskDispatcher
EventTaskDispatcher
Queue Thread Name
(thread-name)
Queue Thread Name...
Note: the name in the parenthesis should match the value set with QueueThreadConfiguration::setThreadName
Note: the name in the parenthes...
EventIntakeTask
EventIntakeTask
Event Intake Queue
(event-intake)
Event Intake Queue...
EventCreator
EventCreator
createEvent
createEvent
pathway not used with OOO gossip
pathway not used with OOO gossip
GossipEvent
GossipEvent
EventImpl
EventImpl
addUnlinkedEvent
addUnlinkedEvent
ConsensusRounds
ConsensusRounds
addEvent
addEvent
dispatchTask
dispatchTask
Conditional
Conditional
instanceof CreateEventTask
instanceof CreateEvent...
instanceof ValidEvent
instanceof ValidEvent
instanceof GossipEvent
instanceof GossipEvent
CreateEventTask
CreateEventTask
GossipEvent
GossipEvent
GossipEvent
GossipEvent
validateEvent
validateEvent
PreConsensusEventObserver
PreConsensusEventObserver
preConsensusEvent
preConsensusEvent
EventImpl
EventImpl
EventImpl
EventImpl
preConsensusEvent
preConsensusEvent
PreConsensus Event Queue
(thread-curr)
PreConsensus Event Queue...
SwirldStateManagerImpl
SwirldStateManagerImpl
SwirldStateManager
SwirldStateManager
handlePreConsensusEvent
handlePreConsensusEvent
EventImpl
EventImpl
preHandle
preHandle
EventImpl
EventImpl
DefaultStateManagementComponent
DefaultStateManagementComponent
ConsensusHashManager
ConsensusHashManager
SignedStateManager
SignedStateManager
postConsensusSignatureObserver
postConsensusSignatureObserver
signature info
signature info
preConsensusSignatureObserver
preConsensusSignatureObserver
signature info
signature info
TransactionHandler
TransactionHandler
preHandle
preHandle
EventImpl, state
EventImpl, state
SwirldState2
SwirldState2
preHandle
preHandle
transaction
transaction
ConsensusRound, state
ConsensusRound, state
handleConsensusRound
handleConsensusRound
round, dual state
round, dual state
handleRound
handleRound
handleConsensusRound
handleConsensusRound
ConsensusRoundHandler
ConsensusRoundHandler
Consensus Round Queue
(thread-cons)
Consensus Round Queue...
InterruptableConsumer<ConsensusRound>
InterruptableConsumer<ConsensusRound>
signed state
signed state
applyConsensusRoundToState
applyConsensusRoundToState
ConsensusRound
ConsensusRound
ConsensusRound
ConsensusRound
ConsensusRound
ConsensusRound
addConsensusRound
addConsensusRound
EventStreamManager
EventStreamManager
ConsensusRoundObserver
ConsensusRoundObserver
ConsensusRound
ConsensusRound
consensusRound
consensusRound
EventImpl
EventImpl
addEvents
addEvents
EventImpls
EventImpls
addEvent
addEvent
Multistream
Multistream
LinkedObjectStream
LinkedObjectStream
EventImpl
EventImpl
addObject
addObject
EventImpl
EventImpl
Hash Queue Thread
(hash-queue)
Hash Queue Thread...
Write Queue Thread
(write-queue)
Write Queue Thread...
EventImpl
EventImpl
ConsensusRoundObserver
ConsensusRoundObserver
consensusRound
consensusRound
ConsensusRound
ConsensusRound
handleConsensus
handleConsensus
OrphanBufferingLinker
OrphanBufferingLinker
linkEvent
linkEvent
GossipEvent
GossipEvent
pollLinkedEvent
pollLinkedEvent
calls
calls
updateGenerations
updateGenerations
EventImpl
EventImpl
TODO there are more observers
TODO there a...
ConsensusWrapper
ConsensusWrapper
addEvent
addEvent
EventImpl
EventImpl
EventAddedObserver
EventAddedObserver
eventAdded
eventAdded
ConsensusRounds
ConsensusRounds
InterfaceName
InterfaceName
interfaceMethod
interfaceMethod
classMethod
classMethod
EventTaskCreator
EventTaskCreator
TODO
TODO
handleMethod
handleMethod
QueueElement
QueueElement
The color of a thread should be reflected in the arrows between methods
The color of a thread...
TODO there are more observers
TODO there a...
PostConsensusSystemTransactionManagerImpl
PostConsensusSystemTransactionManagerImpl
PreConsensusSystemTransactionManagerImpl
PreConsensusSystemTransactionManagerImpl
PostConsensusSystemTransactionManager
PostConsensusSystemTransactionManager
handleRound
handleRound
PreConsensusSystemTransactionManager
PreConsensusSystemTransactionManager
handleEvent
handleEvent
EventImpl
EventImpl
ConsensusRound, state
ConsensusRound, state
handleStateSignatureTransactionPreConsensus
handleStateSignatureTransactionPreConsensus
handleStateSignatureTransactionPostConsensus
handleStateSignatureTransactionPostConsensus
StateSignatureTransactions
StateSignatureTransactions
StateSignatureTransactions
StateSignatureTransactions
Node Mindmap
Node Mindmap
getStateForSigning
getStateForSigning
copy
copy
State Hashing Queue
(state-hash-sign)
State Hashing Queue...
NewSignedStateFromTransactionsConsumer
NewSignedStateFromTransactionsConsumer
newSignedStateFromTransactions
newSignedStateFromTransactions
SignedStateHasher
SignedStateHasher
hashState
hashState
submitTransaction
submitTransaction
EventTransactionPool
EventTransactionPool
submitTransaction
submitTransaction
addUnsignedState
addUnsignedState
transmitSignature
transmitSignature
RoundAppliedToStateConsumer
RoundAppliedToStateConsumer
roundAppliedToState
roundAppliedToState
round number
round number
roundCompleted
roundCompleted
HashLogger
HashLogger
logHashes
logHashes
SignedStateFileManager
SignedStateFileManager
StateHasEnoughSignaturesConsumer
StateHasEnoughSignaturesConsumer
stateHasEnoughSignatures
stateHasEnoughSignatures
saveSignedStateToDisk
saveSignedStateToDisk
FreezeManager
FreezeManager
stateHasEnoughSignatures
stateHasEnoughSignatures
EventCreationRule
EventCreationRule
shouldCreateEvent
shouldCreateEvent
TransactionSupplier
TransactionSupplier
getTransactions
getTransactions
StateLacksSignaturesConsumer
StateLacksSignaturesConsumer
stateLacksSignatures
stateLacksSignatures
purgeOldStates
purgeOldStates
file system
file system
State to disk
(signed-state-file-manager)
State to disk...
(lambda)
(lambda)
ManualWiring
ManualWiring
(lambda)
(lambda)
stateToDisk
stateToDisk
stateHashedObserver
stateHashedObserver
PreConsensus Event Writer Queue
PreConsensus Event Writer Queue
writeEvent
writeEvent
setMinimumGenerationNonAncient
setMinimumGenerationNonAncient
setMinimumGenerationToStore
setMinimumGenerationToStore
requestFlush
requestFlush
waitUntilDurable
waitUntilDurable
write events to disk
write events to di...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/platform-sdk/docs/threads/eventflow-threads.html b/platform-sdk/docs/threads/eventflow-threads.html index e9075931f9cc..e9adbbe8990a 100644 --- a/platform-sdk/docs/threads/eventflow-threads.html +++ b/platform-sdk/docs/threads/eventflow-threads.html @@ -5,7 +5,7 @@ eventflow-threads -

+
\ No newline at end of file diff --git a/platform-sdk/sdk/settings.txt b/platform-sdk/sdk/settings.txt index 0496a5c2ab07..b2b8474dcd2a 100644 --- a/platform-sdk/sdk/settings.txt +++ b/platform-sdk/sdk/settings.txt @@ -6,11 +6,12 @@ # All other values are true. ####################################################################################### -maxOutgoingSyncs, 1 -state.saveStatePeriod, 0 -showInternalStats, 1 -doUpnp, false -useLoopbackIp, false -csvFileName, PlatformTesting -checkSignedStateFromDisk, 1 -loadKeysFromPfxFiles, 0 \ No newline at end of file +maxOutgoingSyncs, 1 +state.saveStatePeriod, 0 +showInternalStats, true +doUpnp, false +useLoopbackIp, false +csvFileName, PlatformTesting +checkSignedStateFromDisk, true +loadKeysFromPfxFiles, false +event.preconsensus.enableStorage, false \ No newline at end of file diff --git a/platform-sdk/swirlds-base/src/main/java/com/swirlds/base/functions/ThrowingConsumer.java b/platform-sdk/swirlds-base/src/main/java/com/swirlds/base/functions/ThrowingConsumer.java new file mode 100644 index 000000000000..cc307dd99b42 --- /dev/null +++ b/platform-sdk/swirlds-base/src/main/java/com/swirlds/base/functions/ThrowingConsumer.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 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.base.functions; + +/** + * Similar to {@link java.util.function.Consumer} but throws an exception. + * + * @param the type accepted by the consumer + * @param the type thrown by the consumer + */ +@FunctionalInterface +public interface ThrowingConsumer { + + /** + * Accept the value. + * + * @param t the value to accept + * @throws E the exception type thrown by the consumer + */ + void accept(T t) throws E; +} diff --git a/platform-sdk/swirlds-base/src/main/java/module-info.java b/platform-sdk/swirlds-base/src/main/java/module-info.java index 16ab7f7af11c..168948845b8d 100644 --- a/platform-sdk/swirlds-base/src/main/java/module-info.java +++ b/platform-sdk/swirlds-base/src/main/java/module-info.java @@ -1,3 +1,4 @@ module com.swirlds.base { exports com.swirlds.base; + exports com.swirlds.base.functions; } diff --git a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/config/StateConfig.java b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/config/StateConfig.java index cb37486475e0..0cbf65391059 100644 --- a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/config/StateConfig.java +++ b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/config/StateConfig.java @@ -18,6 +18,7 @@ import com.swirlds.config.api.ConfigData; import com.swirlds.config.api.ConfigProperty; +import java.nio.file.Path; /** * Config that control the SignedStateManager and SignedStateFileManager behaviors. @@ -94,7 +95,7 @@ */ @ConfigData("state") public record StateConfig( - @ConfigProperty(defaultValue = "data/saved") String savedStateDirectory, + @ConfigProperty(defaultValue = "data/saved") Path savedStateDirectory, @ConfigProperty(defaultValue = "") String mainClassNameOverride, @ConfigProperty(defaultValue = "false") boolean cleanSavedStateDirectory, @ConfigProperty(defaultValue = "20") int stateSavingQueueSize, diff --git a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/io/config/TemporaryFileConfig.java b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/io/config/TemporaryFileConfig.java index 617bd964008b..c9e4789f8181 100644 --- a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/io/config/TemporaryFileConfig.java +++ b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/io/config/TemporaryFileConfig.java @@ -19,7 +19,6 @@ import com.swirlds.common.config.StateConfig; import com.swirlds.config.api.ConfigData; import com.swirlds.config.api.ConfigProperty; -import java.nio.file.Path; /** * Settings for temporary files @@ -39,6 +38,6 @@ public record TemporaryFileConfig(@ConfigProperty(defaultValue = "swirlds-tmp") * @return the location where temporary files are stored */ public String getTemporaryFilePath(final StateConfig stateConfig) { - return Path.of(stateConfig.savedStateDirectory(), temporaryFilePath()).toString(); + return stateConfig.savedStateDirectory().resolve(temporaryFilePath()).toString(); } } diff --git a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/threading/interrupt/Uninterruptable.java b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/threading/interrupt/Uninterruptable.java index 556098543e06..190251980f3c 100644 --- a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/threading/interrupt/Uninterruptable.java +++ b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/threading/interrupt/Uninterruptable.java @@ -16,9 +16,13 @@ package com.swirlds.common.threading.interrupt; +import static com.swirlds.base.ArgumentUtils.throwArgNull; import static com.swirlds.logging.LogMarker.EXCEPTION; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import com.swirlds.base.functions.ThrowingConsumer; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import java.time.Duration; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -29,10 +33,9 @@ *

* *

- * WITH GREAT POWER COMES GREAT RESPONSIBILITY. It's really easy to shoot yourself in the - * foot with these methods. Be EXTRA confident that you understand the big picture on - * any thread where you use one of these methods. Incorrectly handing an interrupt - * can cause a lot of headache. + * WITH GREAT POWER COMES GREAT RESPONSIBILITY. It's really easy to shoot yourself in the foot with these methods. Be + * EXTRA confident that you understand the big picture on any thread where you use one of these methods. Incorrectly + * handing an interrupt can cause a lot of headache. *

*/ public final class Uninterruptable { @@ -43,21 +46,21 @@ private Uninterruptable() {} /** *

- * Perform an action. If that action is interrupted, re-attempt that action. If interrupted again - * then re-attempt again, until the action is eventually successful. Unless this thread is being - * interrupted many times, the action is most likely to be run 1 or 2 times. + * Perform an action. If that action is interrupted, re-attempt that action. If interrupted again then re-attempt + * again, until the action is eventually successful. Unless this thread is being interrupted many times, the action + * is most likely to be run 1 or 2 times. *

* *

* This method is useful when operating in a context where it is inconvenient to throw an - * {@link InterruptedException}, or when performing an action using an interruptable interface - * but where the required operation is needed to always succeed regardless of interrupts. + * {@link InterruptedException}, or when performing an action using an interruptable interface but where the + * required operation is needed to always succeed regardless of interrupts. *

* - * @param action - * the action to perform, may be called multiple times if interrupted + * @param action the action to perform, may be called multiple times if interrupted */ - public static void retryIfInterrupted(final InterruptableRunnable action) { + public static void retryIfInterrupted(@NonNull final InterruptableRunnable action) { + throwArgNull(action, "action"); boolean finished = false; boolean interrupted = false; while (!finished) { @@ -76,21 +79,21 @@ public static void retryIfInterrupted(final InterruptableRunnable action) { /** *

- * Perform an action that returns a value. If that action is interrupted, re-attempt that action. - * If interrupted again then re-attempt again, until the action is eventually successful. - * Unless this thread is being interrupted many times, the action is most likely to be run 1 or 2 times. + * Perform an action that returns a value. If that action is interrupted, re-attempt that action. If interrupted + * again then re-attempt again, until the action is eventually successful. Unless this thread is being interrupted + * many times, the action is most likely to be run 1 or 2 times. *

* *

* This method is useful when operating in a context where it is inconvenient to throw an - * {@link InterruptedException}, or when performing an action using an interruptable interface - * but where the required operation is needed to always succeed regardless of interrupts. + * {@link InterruptedException}, or when performing an action using an interruptable interface but where the + * required operation is needed to always succeed regardless of interrupts. *

* - * @param action - * the action to perform, may be called multiple times if interrupted + * @param action the action to perform, may be called multiple times if interrupted */ - public static T retryIfInterrupted(final InterruptableSupplier action) { + public static @Nullable T retryIfInterrupted(@NonNull final InterruptableSupplier action) { + throwArgNull(action, "action"); boolean finished = false; boolean interrupted = false; T value = null; @@ -111,13 +114,13 @@ public static T retryIfInterrupted(final InterruptableSupplier action) { } /** - * Perform an action. If the thread is interrupted, the action will be aborted and the thread's interrupt - * flag will be reset. + * Perform an action. If the thread is interrupted, the action will be aborted and the thread's interrupt flag will + * be reset. * - * @param action - * the action to perform + * @param action the action to perform */ - public static void abortIfInterrupted(final InterruptableRunnable action) { + public static void abortIfInterrupted(@NonNull InterruptableRunnable action) { + throwArgNull(action, "action"); try { action.run(); } catch (final InterruptedException e) { @@ -127,20 +130,20 @@ public static void abortIfInterrupted(final InterruptableRunnable action) { /** *

- * Perform an action. If the thread is interrupted, the action will be aborted and the thread's interrupt - * flag will be set. Also writes an error message to the log. + * Perform an action. If the thread is interrupted, the action will be aborted and the thread's interrupt flag will + * be set. Also writes an error message to the log. *

* *

* This method is useful for situations where interrupts are only expected if there has been an error condition. *

* - * @param action - * the action to perform - * @param errorMessage - * the error message to write to the log if this thread is inerrupted + * @param action the action to perform + * @param errorMessage the error message to write to the log if this thread is inerrupted */ - public static void abortAndLogIfInterrupted(final InterruptableRunnable action, final String errorMessage) { + public static void abortAndLogIfInterrupted( + @NonNull final InterruptableRunnable action, @NonNull final String errorMessage) { + throwArgNull(action, "action"); try { action.run(); } catch (final InterruptedException e) { @@ -151,23 +154,52 @@ public static void abortAndLogIfInterrupted(final InterruptableRunnable action, /** *

- * Perform an action. If the thread is interrupted, the action will be aborted, the thread's interrupt - * flag will be set, and an exception will be thrown. Also writes an error message to the log. + * Pass an object to a consumer that may throw an {@link InterruptedException}. If the thread is interrupted, the + * action will be aborted and the thread's interrupt flag will be set. Also writes an error message to the log. *

* *

- * This method is useful for situations where interrupts are only expected if there has been an error condition - * and if it is preferred to immediately crash the current thread. + * This method is useful for situations where interrupts are only expected if there has been an error condition. + *

+ * + * @param consumer an object that consumes something and may throw an {@link InterruptedException} + * @param object the object to pass to the consumer + * @param errorMessage the error message to write to the log if this thread is inerrupted + */ + public static void abortAndLogIfInterrupted( + @NonNull final ThrowingConsumer consumer, + @Nullable final T object, + @NonNull final String errorMessage) { + + throwArgNull(consumer, "consumer"); + throwArgNull(errorMessage, "errorMessage"); + + try { + consumer.accept(object); + } catch (final InterruptedException e) { + logger.error(EXCEPTION.getMarker(), errorMessage); + Thread.currentThread().interrupt(); + } + } + + /** + *

+ * Perform an action. If the thread is interrupted, the action will be aborted, the thread's interrupt flag will be + * set, and an exception will be thrown. Also writes an error message to the log. + *

+ * + *

+ * This method is useful for situations where interrupts are only expected if there has been an error condition and + * if it is preferred to immediately crash the current thread. *

* - * @param action - * the action to perform - * @param errorMessage - * the error message to write to the log if this thread is interrupted - * @throws IllegalStateException - * if interrupted + * @param action the action to perform + * @param errorMessage the error message to write to the log if this thread is interrupted + * @throws IllegalStateException if interrupted */ - public static void abortAndThrowIfInterrupted(final InterruptableRunnable action, final String errorMessage) { + public static void abortAndThrowIfInterrupted( + @NonNull InterruptableRunnable action, @NonNull final String errorMessage) { + throwArgNull(action, "action"); try { action.run(); } catch (final InterruptedException e) { @@ -180,10 +212,9 @@ public static void abortAndThrowIfInterrupted(final InterruptableRunnable action /** * Attempt to sleep for a period of time. If interrupted, the sleep may finish early. * - * @param duration - * the amount of time to sleep + * @param duration the amount of time to sleep */ - public static void tryToSleep(final Duration duration) { + public static void tryToSleep(@NonNull Duration duration) { abortIfInterrupted(() -> MILLISECONDS.sleep(duration.toMillis())); } } diff --git a/platform-sdk/swirlds-platform-core/build.gradle.kts b/platform-sdk/swirlds-platform-core/build.gradle.kts index e35f88010c5c..d1b34c6b8f0d 100644 --- a/platform-sdk/swirlds-platform-core/build.gradle.kts +++ b/platform-sdk/swirlds-platform-core/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { api(project(":swirlds-fcqueue")) api(project(":swirlds-jasperdb")) api(project(":swirlds-cli")) + api(project(":swirlds-base")) compileOnly(libs.spotbugs.annotations) runtimeOnly(project(":swirlds-config-impl")) 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 f75d30593109..758a015dadd1 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 @@ -84,6 +84,7 @@ import com.swirlds.platform.config.legacy.LegacyConfigPropertiesLoader; import com.swirlds.platform.crypto.CryptoConstants; import com.swirlds.platform.dispatch.DispatchConfiguration; +import com.swirlds.platform.event.preconsensus.PreConsensusEventStreamConfig; import com.swirlds.platform.gui.internal.InfoApp; import com.swirlds.platform.gui.internal.InfoMember; import com.swirlds.platform.gui.internal.InfoSwirld; @@ -209,7 +210,8 @@ private Browser(final Set localNodesToStart) throws IOException { .withConfigDataType(MetricsConfig.class) .withConfigDataType(PrometheusConfig.class) .withConfigDataType(OSHealthCheckConfig.class) - .withConfigDataType(WiringConfig.class); + .withConfigDataType(WiringConfig.class) + .withConfigDataType(PreConsensusEventStreamConfig.class); // Assume all locally run instances provide the same configuration definitions to the configuration builder. if (appMains.size() > 0) { diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/PlatformConstructor.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/PlatformConstructor.java index 1f2377a6728a..586a95e62eea 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/PlatformConstructor.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/PlatformConstructor.java @@ -18,6 +18,7 @@ import static com.swirlds.platform.SwirldsPlatform.PLATFORM_THREAD_POOL_NAME; +import com.swirlds.base.functions.ThrowingConsumer; import com.swirlds.common.context.PlatformContext; import com.swirlds.common.crypto.config.CryptoConfig; import com.swirlds.common.metrics.Metrics; @@ -49,6 +50,7 @@ import com.swirlds.platform.state.SwirldStateManagerImpl; import com.swirlds.platform.state.signed.SignedState; import com.swirlds.platform.system.PlatformConstructionException; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.security.InvalidKeyException; import java.security.KeyManagementException; @@ -216,6 +218,7 @@ static PreConsensusEventHandler preConsensusEventHandler( * the instance that streams consensus events to disk * @param stateHashSignQueue * the queue for signed states that need signatures collected + * @param waitForEventDurability a method that blocks until an event becomes durable. * @param enterFreezePeriod * a runnable executed when a freeze is entered * @param roundAppliedToStateConsumer @@ -225,17 +228,18 @@ static PreConsensusEventHandler preConsensusEventHandler( * @return the newly constructed instance of {@link ConsensusRoundHandler} */ static ConsensusRoundHandler consensusHandler( - final PlatformContext platformContext, - final ThreadManager threadManager, + @NonNull final PlatformContext platformContext, + @NonNull final ThreadManager threadManager, final long selfId, - final SettingsProvider settingsProvider, - final SwirldStateManager swirldStateManager, - final ConsensusHandlingMetrics consensusHandlingMetrics, - final EventStreamManager eventStreamManager, - final BlockingQueue stateHashSignQueue, - final Runnable enterFreezePeriod, - final RoundAppliedToStateConsumer roundAppliedToStateConsumer, - final SoftwareVersion softwareVersion) { + @NonNull final SettingsProvider settingsProvider, + @NonNull final SwirldStateManager swirldStateManager, + @NonNull final ConsensusHandlingMetrics consensusHandlingMetrics, + @NonNull final EventStreamManager eventStreamManager, + @NonNull final BlockingQueue stateHashSignQueue, + @NonNull final ThrowingConsumer waitForEventDurability, + @NonNull final Runnable enterFreezePeriod, + @NonNull final RoundAppliedToStateConsumer roundAppliedToStateConsumer, + @NonNull final SoftwareVersion softwareVersion) { return new ConsensusRoundHandler( platformContext, @@ -246,6 +250,7 @@ static ConsensusRoundHandler consensusHandler( consensusHandlingMetrics, eventStreamManager, stateHashSignQueue, + waitForEventDurability, enterFreezePeriod, roundAppliedToStateConsumer, softwareVersion); diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java index daf840b0e8cd..f8b8a372368c 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/SwirldsPlatform.java @@ -16,6 +16,7 @@ package com.swirlds.platform; +import static com.swirlds.common.threading.interrupt.Uninterruptable.abortAndLogIfInterrupted; import static com.swirlds.common.threading.manager.AdHocThreadManager.getStaticThreadManager; import static com.swirlds.common.utility.CommonUtils.combineConsumers; import static com.swirlds.logging.LogMarker.EXCEPTION; @@ -126,6 +127,13 @@ import com.swirlds.platform.event.linking.InOrderLinker; import com.swirlds.platform.event.linking.OrphanBufferingLinker; import com.swirlds.platform.event.linking.ParentFinder; +import com.swirlds.platform.event.preconsensus.AsyncPreConsensusEventWriter; +import com.swirlds.platform.event.preconsensus.NoOpPreConsensusEventWriter; +import com.swirlds.platform.event.preconsensus.PreConsensusEventFileManager; +import com.swirlds.platform.event.preconsensus.PreConsensusEventStreamConfig; +import com.swirlds.platform.event.preconsensus.PreConsensusEventWriter; +import com.swirlds.platform.event.preconsensus.PreconsensusEventStreamSequencer; +import com.swirlds.platform.event.preconsensus.SyncPreConsensusEventWriter; import com.swirlds.platform.event.validation.AncientValidator; import com.swirlds.platform.event.validation.EventDeduplication; import com.swirlds.platform.event.validation.EventValidator; @@ -170,7 +178,9 @@ import com.swirlds.platform.network.unidirectional.ProtocolMapping; import com.swirlds.platform.network.unidirectional.SharedConnectionLocks; import com.swirlds.platform.network.unidirectional.UnidirectionalProtocols; +import com.swirlds.platform.observers.ConsensusRoundObserver; import com.swirlds.platform.observers.EventObserverDispatcher; +import com.swirlds.platform.observers.PreConsensusEventObserver; import com.swirlds.platform.reconnect.DefaultSignedStateValidator; import com.swirlds.platform.reconnect.FallenBehindManagerImpl; import com.swirlds.platform.reconnect.ReconnectController; @@ -203,6 +213,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.io.IOException; +import java.io.UncheckedIOException; import java.security.NoSuchAlgorithmException; import java.time.Instant; import java.util.ArrayList; @@ -410,6 +421,11 @@ public class SwirldsPlatform implements Platform, PlatformWithDeprecatedMethods, */ private final PlatformContext platformContext; + /** + * Writes pre-consensus events to disk. + */ + private final PreConsensusEventWriter preConsensusEventWriter; + private final BasicConfig basicConfig; /** @@ -532,6 +548,9 @@ public class SwirldsPlatform implements Platform, PlatformWithDeprecatedMethods, metrics.addUpdater(wiring::updateMetrics); final AppCommunicationComponent appCommunicationComponent = wiring.wireAppCommunicationComponent(notificationEngine); + + preConsensusEventWriter = components.add(buildPreConsensusEventWriter()); + stateManagementComponent = wiring.wireStateManagementComponent( PlatformConstructor.platformSigner(crypto.getKeysAndCerts()), this.mainClassName, @@ -539,7 +558,8 @@ public class SwirldsPlatform implements Platform, PlatformWithDeprecatedMethods, swirldName, this::createPrioritySystemTransaction, this::haltRequested, - appCommunicationComponent); + appCommunicationComponent, + preConsensusEventWriter); wiring.registerComponents(components); final NetworkStatsTransmitter networkStatsTransmitter = @@ -996,11 +1016,36 @@ private void haltRequested(final String reason) { } } + /** + * Build the pre-consensus event writer. + */ + private PreConsensusEventWriter buildPreConsensusEventWriter() { + final PreConsensusEventStreamConfig preConsensusEventStreamConfig = + platformContext.getConfiguration().getConfigData(PreConsensusEventStreamConfig.class); + + if (!preConsensusEventStreamConfig.enableStorage()) { + return new NoOpPreConsensusEventWriter(); + } + + final PreConsensusEventFileManager fileManager; + try { + fileManager = new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), selfId.getId()); + } catch (final IOException e) { + throw new UncheckedIOException("unable load preconsensus files", e); + } + + final PreConsensusEventWriter syncWriter = new SyncPreConsensusEventWriter(platformContext, fileManager); + + return new AsyncPreConsensusEventWriter(platformContext, threadManager, syncWriter); + } + /** * Creates and wires up all the classes responsible for accepting events from gossip, creating new events, and * routing those events throughout the system. */ private void buildEventIntake() { + final PreconsensusEventStreamSequencer sequencer = new PreconsensusEventStreamSequencer(); + final EventObserverDispatcher dispatcher = new EventObserverDispatcher( new ShadowGraphEventObserver(shadowGraph), consensusRoundHandler, @@ -1008,7 +1053,23 @@ private void buildEventIntake() { eventMapper, addedEventMetrics, criticalQuorum, - eventIntakeMetrics); + eventIntakeMetrics, + (PreConsensusEventObserver) event -> { + sequencer.assignStreamSequenceNumber(event); + abortAndLogIfInterrupted( + preConsensusEventWriter::writeEvent, + event, + "Interrupted while attempting to enqueue preconsensus event for writing"); + }, + (ConsensusRoundObserver) round -> { + abortAndLogIfInterrupted( + preConsensusEventWriter::setMinimumGenerationNonAncient, + round.getGenerations().getMinGenerationNonAncient(), + "Interrupted while attempting to enqueue change in minimum generation non-ancient"); + + final EventImpl keystoneEvent = round.getKeystoneEvent(); + preConsensusEventWriter.requestFlush(keystoneEvent); + }); if (settings.getChatter().isChatterUsed()) { dispatcher.addObserver(new ChatterNotifier(selfId, chatterCore)); dispatcher.addObserver(chatterEventMapper); @@ -1162,6 +1223,7 @@ private void buildEventHandlersFromState( new ConsensusHandlingMetrics(metrics, time), eventStreamManager, stateHashSignQueueThread, + preConsensusEventWriter::waitUntilDurable, freezeManager::freezeStarted, stateManagementComponent::roundAppliedToState, appVersion)); diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/ConsensusWrapper.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/ConsensusWrapper.java index 51b7a6995726..728d8c846d43 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/ConsensusWrapper.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/ConsensusWrapper.java @@ -56,7 +56,7 @@ public List addEvent(final EventImpl event, final AddressBook ad final List rounds = new LinkedList<>(); for (final Map.Entry> entry : roundEvents.entrySet()) { final Generations generations = new Generations(consensusSupplier.get()); - rounds.add(new ConsensusRound(entry.getValue(), generations)); + rounds.add(new ConsensusRound(entry.getValue(), event, generations)); } return rounds; diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/EventIntake.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/EventIntake.java index cd0ef2d5d74c..8e12d4b0961b 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/EventIntake.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/EventIntake.java @@ -16,6 +16,7 @@ package com.swirlds.platform.components; +import static com.swirlds.base.ArgumentUtils.throwArgNull; import static com.swirlds.logging.LogMarker.INTAKE_EVENT; import static com.swirlds.logging.LogMarker.STALE_EVENTS; import static com.swirlds.logging.LogMarker.SYNC; @@ -33,6 +34,7 @@ import com.swirlds.platform.internal.EventImpl; import com.swirlds.platform.observers.EventObserverDispatcher; import com.swirlds.platform.sync.ShadowGraph; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Collection; import java.util.List; import java.util.function.Supplier; @@ -41,8 +43,7 @@ /** * This class is responsible for adding events to {@link Consensus} and notifying event observers, including - * {@link ConsensusRoundHandler} and - * {@link com.swirlds.platform.eventhandling.PreConsensusEventHandler}. + * {@link ConsensusRoundHandler} and {@link com.swirlds.platform.eventhandling.PreConsensusEventHandler}. */ public class EventIntake { private static final Logger logger = LogManager.getLogger(EventIntake.class); @@ -66,39 +67,34 @@ public class EventIntake { /** * Constructor * - * @param selfId - * the ID of this node - * @param consensusSupplier - * a functor which provides access to the {@code Consensus} interface - * @param addressBook - * the current address book - * @param dispatcher - * an event observer dispatcher + * @param selfId the ID of this node + * @param consensusSupplier a functor which provides access to the {@code Consensus} interface + * @param addressBook the current address book + * @param dispatcher an event observer dispatcher */ public EventIntake( - final NodeId selfId, - final EventLinker eventLinker, - final Supplier consensusSupplier, - final AddressBook addressBook, - final EventObserverDispatcher dispatcher, - final IntakeCycleStats stats, - final ShadowGraph shadowGraph) { - this.selfId = selfId; - this.eventLinker = eventLinker; - this.consensusSupplier = consensusSupplier; + @NonNull final NodeId selfId, + @NonNull final EventLinker eventLinker, + @NonNull final Supplier consensusSupplier, + @NonNull final AddressBook addressBook, + @NonNull final EventObserverDispatcher dispatcher, + @NonNull final IntakeCycleStats stats, + @NonNull final ShadowGraph shadowGraph) { + this.selfId = throwArgNull(selfId, "selfId"); + this.eventLinker = throwArgNull(eventLinker, "eventLinker"); + this.consensusSupplier = throwArgNull(consensusSupplier, "consensusSupplier"); this.consensusWrapper = new ConsensusWrapper(consensusSupplier); - this.addressBook = addressBook; - this.dispatcher = dispatcher; - this.stats = stats; - this.shadowGraph = shadowGraph; + this.addressBook = throwArgNull(addressBook, "addressBook"); + this.dispatcher = throwArgNull(dispatcher, "dispatcher"); + this.stats = throwArgNull(stats, "stats"); + this.shadowGraph = throwArgNull(shadowGraph, "shadowGraph"); } /** * Adds an event received from gossip that has been validated without its parents. It must be linked to its parents * before being added to consensus. The linking is done by the {@link EventLinker} provided. * - * @param event - * the event + * @param event the event */ public void addUnlinkedEvent(final GossipEvent event) { stats.receivedUnlinkedEvent(); @@ -114,8 +110,7 @@ public void addUnlinkedEvent(final GossipEvent event) { /** * Add an event to the hashgraph * - * @param event - * an event to be added + * @param event an event to be added */ public void addEvent(final EventImpl event) { // an expired event will cause ShadowGraph to throw an exception, so we just to discard it @@ -127,6 +122,7 @@ public void addEvent(final EventImpl event) { event.clear(); return; } + stats.doneValidation(); logger.debug(SYNC.getMarker(), "{} sees {}", selfId, event); dispatcher.preConsensusEvent(event); @@ -187,11 +183,10 @@ private static boolean isNotConsensus(final EventImpl event) { } /** - * Notify observers that an event has reach consensus. Called on a list of - * events returned from {@code Consensus.addEvent}. + * Notify observers that an event has reach consensus. Called on a list of events returned from + * {@code Consensus.addEvent}. * - * @param consensusRound - * the (new) consensus round to be observed + * @param consensusRound the (new) consensus round to be observed */ private void handleConsensus(final ConsensusRound consensusRound) { if (consensusRound != null) { diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/state/DefaultStateManagementComponent.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/state/DefaultStateManagementComponent.java index f56420b9ca30..aea4364bb1f0 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/state/DefaultStateManagementComponent.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/state/DefaultStateManagementComponent.java @@ -16,6 +16,7 @@ package com.swirlds.platform.components.state; +import static com.swirlds.base.ArgumentUtils.throwArgNull; import static com.swirlds.logging.LogMarker.EXCEPTION; import com.swirlds.common.config.ConsensusConfig; @@ -46,6 +47,7 @@ import com.swirlds.platform.dispatch.triggers.control.HaltRequestedConsumer; import com.swirlds.platform.dispatch.triggers.control.StateDumpRequestedTrigger; import com.swirlds.platform.dispatch.triggers.flow.StateHashedTrigger; +import com.swirlds.platform.event.preconsensus.PreConsensusEventWriter; import com.swirlds.platform.metrics.IssMetrics; import com.swirlds.platform.state.SignatureTransmitter; import com.swirlds.platform.state.State; @@ -61,6 +63,7 @@ import com.swirlds.platform.state.signed.SignedStateSentinel; import com.swirlds.platform.state.signed.SourceOfSignedState; import com.swirlds.platform.util.HashLogger; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -148,21 +151,39 @@ public class DefaultStateManagementComponent implements StateManagementComponent * @param fatalErrorConsumer consumer to invoke when a fatal error has occurred */ public DefaultStateManagementComponent( - final PlatformContext context, - final ThreadManager threadManager, - final AddressBook addressBook, - final PlatformSigner signer, - final String mainClassName, - final NodeId selfId, - final String swirldName, - final PrioritySystemTransactionSubmitter prioritySystemTransactionSubmitter, - final StateToDiskAttemptConsumer stateToDiskEventConsumer, - final NewLatestCompleteStateConsumer newLatestCompleteStateConsumer, - final StateLacksSignaturesConsumer stateLacksSignaturesConsumer, - final StateHasEnoughSignaturesConsumer stateHasEnoughSignaturesConsumer, - final IssConsumer issConsumer, - final HaltRequestedConsumer haltRequestedConsumer, - final FatalErrorConsumer fatalErrorConsumer) { + @NonNull final PlatformContext context, + @NonNull final ThreadManager threadManager, + @NonNull final AddressBook addressBook, + @NonNull final PlatformSigner signer, + @NonNull final String mainClassName, + @NonNull final NodeId selfId, + @NonNull final String swirldName, + @NonNull final PrioritySystemTransactionSubmitter prioritySystemTransactionSubmitter, + @NonNull final StateToDiskAttemptConsumer stateToDiskEventConsumer, + @NonNull final NewLatestCompleteStateConsumer newLatestCompleteStateConsumer, + @NonNull final StateLacksSignaturesConsumer stateLacksSignaturesConsumer, + @NonNull final StateHasEnoughSignaturesConsumer stateHasEnoughSignaturesConsumer, + @NonNull final IssConsumer issConsumer, + @NonNull final HaltRequestedConsumer haltRequestedConsumer, + @NonNull final FatalErrorConsumer fatalErrorConsumer, + @NonNull final PreConsensusEventWriter preConsensusEventWriter) { + + throwArgNull(context, "context"); + throwArgNull(threadManager, "threadManager"); + throwArgNull(addressBook, "addressBook"); + throwArgNull(signer, "signer"); + throwArgNull(mainClassName, "mainClassName"); + throwArgNull(selfId, "selfId"); + throwArgNull(swirldName, "swirldName"); + throwArgNull(prioritySystemTransactionSubmitter, "prioritySystemTransactionSubmitter"); + throwArgNull(stateToDiskEventConsumer, "stateToDiskEventConsumer"); + throwArgNull(newLatestCompleteStateConsumer, "newLatestCompleteStateConsumer"); + throwArgNull(stateLacksSignaturesConsumer, "stateLacksSignaturesConsumer"); + throwArgNull(stateHasEnoughSignaturesConsumer, "stateHasEnoughSignaturesConsumer"); + throwArgNull(issConsumer, "issConsumer"); + throwArgNull(haltRequestedConsumer, "haltRequestedConsumer"); + throwArgNull(fatalErrorConsumer, "fatalErrorConsumer"); + throwArgNull(preConsensusEventWriter, "preConsensusEventWriter"); this.signer = signer; this.signatureTransmitter = new SignatureTransmitter(addressBook, selfId, prioritySystemTransactionSubmitter); @@ -187,7 +208,8 @@ public DefaultStateManagementComponent( mainClassName, selfId, swirldName, - stateToDiskEventConsumer); + stateToDiskEventConsumer, + preConsensusEventWriter::setMinimumGenerationToStore); final StateHasEnoughSignaturesConsumer combinedStateHasEnoughSignaturesConsumer = ssw -> { stateHasEnoughSignatures(ssw.get()); diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/state/DefaultStateManagementComponentFactory.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/state/DefaultStateManagementComponentFactory.java index 88c251a6b2b8..7e3ea546720b 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/state/DefaultStateManagementComponentFactory.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/state/DefaultStateManagementComponentFactory.java @@ -16,6 +16,7 @@ package com.swirlds.platform.components.state; +import static com.swirlds.base.ArgumentUtils.throwArgNull; import static com.swirlds.common.formatting.StringFormattingUtils.addLine; import com.swirlds.common.context.PlatformContext; @@ -31,6 +32,8 @@ import com.swirlds.platform.components.state.output.StateToDiskAttemptConsumer; import com.swirlds.platform.crypto.PlatformSigner; import com.swirlds.platform.dispatch.triggers.control.HaltRequestedConsumer; +import com.swirlds.platform.event.preconsensus.PreConsensusEventWriter; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Creates instances of {@link DefaultStateManagementComponent} @@ -52,6 +55,7 @@ public class DefaultStateManagementComponentFactory implements StateManagementCo private IssConsumer issConsumer; private HaltRequestedConsumer haltRequestedConsumer; private FatalErrorConsumer fatalErrorConsumer; + private PreConsensusEventWriter preConsensusEventWriter; public DefaultStateManagementComponentFactory( final PlatformContext context, @@ -121,6 +125,13 @@ public StateManagementComponentFactory fatalErrorConsumer(final FatalErrorConsum return this; } + @Override + public @NonNull StateManagementComponentFactory setPreConsensusEventWriter( + @NonNull final PreConsensusEventWriter preConsensusEventWriter) { + this.preConsensusEventWriter = throwArgNull(preConsensusEventWriter, "preConsensusEventWriter"); + return this; + } + @Override public StateManagementComponent build() { verifyInputs(); @@ -139,7 +150,8 @@ public StateManagementComponent build() { stateHasEnoughSignaturesConsumer, issConsumer, haltRequestedConsumer, - fatalErrorConsumer); + fatalErrorConsumer, + preConsensusEventWriter); } private void verifyInputs() { @@ -168,6 +180,9 @@ private void verifyInputs() { if (fatalErrorConsumer == null) { addLine(errors, "fatalErrorConsumer must not be null"); } + if (preConsensusEventWriter == null) { + addLine(errors, "preConsensusEventWriter must not be null"); + } if (!errors.isEmpty()) { throw new IllegalStateException("Unable to build StateManagementComponent:\n" + errors); } diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/state/StateManagementComponentFactory.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/state/StateManagementComponentFactory.java index ad08f3ffd4b0..8f1b52ed2547 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/state/StateManagementComponentFactory.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/state/StateManagementComponentFactory.java @@ -24,6 +24,7 @@ import com.swirlds.platform.components.state.output.StateLacksSignaturesConsumer; import com.swirlds.platform.components.state.output.StateToDiskAttemptConsumer; import com.swirlds.platform.dispatch.triggers.control.HaltRequestedConsumer; +import com.swirlds.platform.event.preconsensus.PreConsensusEventWriter; /** * A factory capable of creating instances of {@link StateManagementComponent}. @@ -86,6 +87,13 @@ public interface StateManagementComponentFactory { */ StateManagementComponentFactory fatalErrorConsumer(FatalErrorConsumer consumer); + /** + * Set the preconsensus event writer. + * @param preConsensusEventWriter the preconsensus event writer + * @return this + */ + StateManagementComponentFactory setPreConsensusEventWriter(PreConsensusEventWriter preConsensusEventWriter); + /** * Builds a new {@link StateManagementComponent} with the provided inputs. * diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/state/output/MinimumGenerationNonAncientConsumer.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/state/output/MinimumGenerationNonAncientConsumer.java new file mode 100644 index 000000000000..f9f4ec9a18b7 --- /dev/null +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/state/output/MinimumGenerationNonAncientConsumer.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 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.components.state.output; + +/** + * A method that is called when the minimum generation non-ancient, + * with respect to the oldest state snapshot on disk, is updated. + */ +@FunctionalInterface +public interface MinimumGenerationNonAncientConsumer { + + /** + * Called when the minimum generation non-ancient is updated, with respect to the oldest state snapshot on disk. + * @param minimumGenerationNonAncient the new minimum generation non-ancient + */ + void newMinimumGenerationNonAncient(long minimumGenerationNonAncient); +} diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/wiring/ManualWiring.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/wiring/ManualWiring.java index 585915654f7a..195bb5216a43 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/wiring/ManualWiring.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/wiring/ManualWiring.java @@ -16,6 +16,7 @@ package com.swirlds.platform.components.wiring; +import static com.swirlds.base.ArgumentUtils.throwArgNull; import static com.swirlds.logging.LogMarker.EXCEPTION; import com.swirlds.common.config.WiringConfig; @@ -38,9 +39,11 @@ import com.swirlds.platform.components.state.StateManagementComponentFactory; import com.swirlds.platform.crypto.PlatformSigner; import com.swirlds.platform.dispatch.triggers.control.HaltRequestedConsumer; +import com.swirlds.platform.event.preconsensus.PreConsensusEventWriter; import com.swirlds.platform.metrics.WiringMetrics; import com.swirlds.platform.system.Shutdown; import com.swirlds.platform.util.PlatformComponents; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ArrayList; import java.util.List; import org.apache.logging.log4j.LogManager; @@ -93,8 +96,7 @@ public ManualWiring( /** * Creates and wires the {@link AppCommunicationComponent}. * - * @param notificationEngine - * passes notifications between the platform and the application + * @param notificationEngine passes notifications between the platform and the application * @return a fully wired {@link AppCommunicationComponent} */ public AppCommunicationComponent wireAppCommunicationComponent(final NotificationEngine notificationEngine) { @@ -107,30 +109,34 @@ public AppCommunicationComponent wireAppCommunicationComponent(final Notificatio /** * Creates and wires the {@link StateManagementComponent}. * - * @param platformSigner - * signer capable of signing with this node's private key - * @param mainClassName - * the class that extends {@link com.swirlds.common.system.SwirldMain} - * @param selfId - * this node's id - * @param swirldName - * the name of the swirld this node is in - * @param prioritySystemTransactionSubmitter - * submits priority system transactions - * @param haltRequestedConsumer - * consumer to invoke when a halt is requested - * @param appCommunicationComponent - * the {@link AppCommunicationComponent} + * @param platformSigner signer capable of signing with this node's private key + * @param mainClassName the class that extends {@link com.swirlds.common.system.SwirldMain} + * @param selfId this node's id + * @param swirldName the name of the swirld this node is in + * @param prioritySystemTransactionSubmitter submits priority system transactions + * @param haltRequestedConsumer consumer to invoke when a halt is requested + * @param appCommunicationComponent the {@link AppCommunicationComponent} + * @param preConsensusEventWriter writes preconsensus events to disk * @return a fully wired {@link StateManagementComponent} */ - public StateManagementComponent wireStateManagementComponent( - final PlatformSigner platformSigner, - final String mainClassName, - final NodeId selfId, - final String swirldName, - final PrioritySystemTransactionSubmitter prioritySystemTransactionSubmitter, - final HaltRequestedConsumer haltRequestedConsumer, - final AppCommunicationComponent appCommunicationComponent) { + public @NonNull StateManagementComponent wireStateManagementComponent( + @NonNull final PlatformSigner platformSigner, + @NonNull final String mainClassName, + @NonNull final NodeId selfId, + @NonNull final String swirldName, + @NonNull final PrioritySystemTransactionSubmitter prioritySystemTransactionSubmitter, + @NonNull final HaltRequestedConsumer haltRequestedConsumer, + @NonNull final AppCommunicationComponent appCommunicationComponent, + @NonNull final PreConsensusEventWriter preConsensusEventWriter) { + + throwArgNull(platformSigner, "platformSigner"); + throwArgNull(mainClassName, "mainClassName"); + throwArgNull(selfId, "selfId"); + throwArgNull(swirldName, "swirldName"); + throwArgNull(prioritySystemTransactionSubmitter, "prioritySystemTransactionSubmitter"); + throwArgNull(haltRequestedConsumer, "haltRequestedConsumer"); + throwArgNull(appCommunicationComponent, "appCommunicationComponent"); + throwArgNull(preConsensusEventWriter, "preConsensusEventWriter"); final StateManagementComponentFactory stateManagementComponentFactory = new DefaultStateManagementComponentFactory( @@ -173,6 +179,7 @@ public StateManagementComponent wireStateManagementComponent( // FUTURE WORK: make this asynchronous stateManagementComponentFactory.issConsumer(appCommunicationComponent); stateManagementComponentFactory.fatalErrorConsumer(this::handleFatalError); + stateManagementComponentFactory.setPreConsensusEventWriter(preConsensusEventWriter); final StateManagementComponent stateManagementComponent = stateManagementComponentFactory.build(); platformComponentList.add(stateManagementComponent); @@ -209,8 +216,7 @@ private static void logFatalError(final String msg, final Throwable throwable) { /** * Registers all components created by this class. * - * @param platformComponents - * the class that manages startables and registers dispatch observers. + * @param platformComponents the class that manages startables and registers dispatch observers. */ public void registerComponents(final PlatformComponents platformComponents) { otherComponents.forEach(platformComponents::add); diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/AsyncPreConsensusEventWriter.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/AsyncPreConsensusEventWriter.java index 039b63b02372..9336650d61d8 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/AsyncPreConsensusEventWriter.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/AsyncPreConsensusEventWriter.java @@ -16,13 +16,16 @@ package com.swirlds.platform.event.preconsensus; +import static com.swirlds.base.ArgumentUtils.throwArgNull; import static com.swirlds.logging.LogMarker.EXCEPTION; +import com.swirlds.common.context.PlatformContext; import com.swirlds.common.threading.framework.BlockingQueueInserter; import com.swirlds.common.threading.framework.MultiQueueThread; import com.swirlds.common.threading.framework.config.MultiQueueThreadConfiguration; import com.swirlds.common.threading.manager.ThreadManager; import com.swirlds.platform.internal.EventImpl; +import edu.umd.cs.findbugs.annotations.NonNull; import java.time.Duration; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -57,16 +60,21 @@ public class AsyncPreConsensusEventWriter implements PreConsensusEventWriter { /** * Create a new AsyncPreConsensusEventWriter. * + * @param platformContext the platform context * @param threadManager responsible for creating new threads - * @param config preconsensus event stream configuration * @param writer the writer to which events will be written, wrapped by this class */ public AsyncPreConsensusEventWriter( - final ThreadManager threadManager, - final PreConsensusEventStreamConfig config, - final PreConsensusEventWriter writer) { + @NonNull final PlatformContext platformContext, + @NonNull final ThreadManager threadManager, + @NonNull final PreConsensusEventWriter writer) { - this.writer = writer; + throwArgNull(platformContext, "platformContext"); + throwArgNull(threadManager, "threadManager"); + this.writer = throwArgNull(writer, "writer"); + + final PreConsensusEventStreamConfig config = + platformContext.getConfiguration().getConfigData(PreConsensusEventStreamConfig.class); handleThread = new MultiQueueThreadConfiguration(threadManager) .setComponent("pre-consensus") @@ -102,7 +110,7 @@ public void stop() { * {@inheritDoc} */ @Override - public void writeEvent(final EventImpl event) throws InterruptedException { + public void writeEvent(@NonNull final EventImpl event) throws InterruptedException { if (event.getStreamSequenceNumber() == EventImpl.NO_STREAM_SEQUENCE_NUMBER || event.getStreamSequenceNumber() == EventImpl.STALE_EVENT_STREAM_SEQUENCE_NUMBER) { throw new IllegalStateException("Event must have a valid stream sequence number"); @@ -130,7 +138,7 @@ public void setMinimumGenerationToStore(final long minimumGenerationToStore) { * {@inheritDoc} */ @Override - public boolean isEventDurable(final EventImpl event) { + public boolean isEventDurable(@NonNull final EventImpl event) { return writer.isEventDurable(event); } @@ -138,7 +146,7 @@ public boolean isEventDurable(final EventImpl event) { * {@inheritDoc} */ @Override - public void waitUntilDurable(final EventImpl event) throws InterruptedException { + public void waitUntilDurable(@NonNull final EventImpl event) throws InterruptedException { writer.waitUntilDurable(event); } @@ -146,7 +154,8 @@ public void waitUntilDurable(final EventImpl event) throws InterruptedException * {@inheritDoc} */ @Override - public boolean waitUntilDurable(final EventImpl event, final Duration timeToWait) throws InterruptedException { + public boolean waitUntilDurable(@NonNull final EventImpl event, @NonNull final Duration timeToWait) + throws InterruptedException { return writer.waitUntilDurable(event, timeToWait); } @@ -154,14 +163,14 @@ public boolean waitUntilDurable(final EventImpl event, final Duration timeToWait * {@inheritDoc} */ @Override - public void requestFlush(final EventImpl event) { + public void requestFlush(@NonNull final EventImpl event) { writer.requestFlush(event); } /** * Pass a minimum generation non-ancient to the wrapped writer. */ - private void setMinimumGenerationNonAncientHandler(final Long minimumGenerationNonAncient) { + private void setMinimumGenerationNonAncientHandler(@NonNull final Long minimumGenerationNonAncient) { try { writer.setMinimumGenerationNonAncient(minimumGenerationNonAncient); } catch (final InterruptedException e) { @@ -178,7 +187,7 @@ private void setMinimumGenerationNonAncientHandler(final Long minimumGenerationN /** * Pass an event to the wrapped writer. */ - private void addEventHandler(final EventImpl event) { + private void addEventHandler(@NonNull final EventImpl event) { try { writer.writeEvent(event); } catch (final InterruptedException e) { diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/NoOpPreConsensusEventWriter.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/NoOpPreConsensusEventWriter.java new file mode 100644 index 000000000000..6a9a5628f029 --- /dev/null +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/NoOpPreConsensusEventWriter.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2023 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.event.preconsensus; + +import com.swirlds.platform.internal.EventImpl; +import java.time.Duration; + +/** + * A {@link PreConsensusEventWriter} that does nothing. + * + * @deprecated Once we decide to enable this in production, we will remove this implementation and will not support + * disabling the pre-consensus event writer. + */ +@Deprecated +public class NoOpPreConsensusEventWriter implements PreConsensusEventWriter { + + /** + * {@inheritDoc} + */ + @Override + public void start() { + // no-op + } + + /** + * {@inheritDoc} + */ + @Override + public void stop() { + // no-op + } + + /** + * {@inheritDoc} + */ + @Override + public void writeEvent(final EventImpl event) { + // no-op + } + + /** + * {@inheritDoc} + */ + @Override + public void setMinimumGenerationNonAncient(final long minimumGenerationNonAncient) { + // no-op + } + + /** + * {@inheritDoc} + */ + @Override + public void setMinimumGenerationToStore(final long minimumGenerationToStore) { + // no-op + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEventDurable(final EventImpl event) { + // If we are not writing events, then we should never block on events becoming durable. + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public void waitUntilDurable(final EventImpl event) { + // If we are not writing events, then we should never block on events becoming durable. + } + + /** + * {@inheritDoc} + */ + @Override + public boolean waitUntilDurable(final EventImpl event, final Duration timeToWait) { + // If we are not writing events, then we should never block on events becoming durable. + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public void requestFlush(final EventImpl event) { + // no-op + } +} diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PreConsensusEventFileManager.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PreConsensusEventFileManager.java index 26200e303b7f..a55ad501cd12 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PreConsensusEventFileManager.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PreConsensusEventFileManager.java @@ -16,13 +16,18 @@ package com.swirlds.platform.event.preconsensus; +import static com.swirlds.base.ArgumentUtils.throwArgNull; import static com.swirlds.logging.LogMarker.EXCEPTION; +import com.swirlds.common.config.StateConfig; +import com.swirlds.common.context.PlatformContext; import com.swirlds.common.time.Time; import com.swirlds.common.utility.BinarySearch; import com.swirlds.common.utility.RandomAccessDeque; import com.swirlds.common.utility.Units; import com.swirlds.common.utility.ValueReference; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -90,19 +95,31 @@ public class PreConsensusEventFileManager { /** * Instantiate an event file collection. Loads all event files in the specified directory. * + * @param platformContext the platform context for this node * @param time provides wall clock time - * @param configuration configuration for preconsensus events - * @param metrics encapsulates metrics for the preconsensus event stream + * @param selfId the ID of this node */ public PreConsensusEventFileManager( - final Time time, final PreConsensusEventStreamConfig configuration, final PreconsensusEventMetrics metrics) + @NonNull final PlatformContext platformContext, @NonNull final Time time, final long selfId) throws IOException { + throwArgNull(platformContext, "platformContext"); + throwArgNull(time, "time"); + + final PreConsensusEventStreamConfig preConsensusEventStreamConfig = + platformContext.getConfiguration().getConfigData(PreConsensusEventStreamConfig.class); + final StateConfig stateConfig = platformContext.getConfiguration().getConfigData(StateConfig.class); + this.time = time; - this.metrics = metrics; - minimumRetentionPeriod = configuration.minimumRetentionPeriod(); + this.metrics = new PreconsensusEventMetrics(platformContext.getMetrics()); + + minimumRetentionPeriod = preConsensusEventStreamConfig.minimumRetentionPeriod(); + + this.databaseDirectory = stateConfig + .savedStateDirectory() + .resolve(preConsensusEventStreamConfig.databaseDirectory()) + .resolve(Long.toString(selfId)); - this.databaseDirectory = configuration.databaseDirectory(); if (!Files.exists(databaseDirectory)) { Files.createDirectories(databaseDirectory); } @@ -115,7 +132,7 @@ public PreConsensusEventFileManager( .map(PreConsensusEventFileManager::parseFile) .filter(Objects::nonNull) .sorted() - .forEachOrdered(buildFileHandler(configuration.permitGaps())); + .forEachOrdered(buildFileHandler(preConsensusEventStreamConfig.permitGaps())); } // Measure the size of each file. @@ -128,7 +145,7 @@ public PreConsensusEventFileManager( .set(files.getFirst().minimumGeneration()); metrics.getPreconsensusEventFileYoungestGeneration() .set(files.getLast().maximumGeneration()); - final Duration age = Duration.between(time.now(), files.getFirst().timestamp()); + final Duration age = Duration.between(files.getFirst().timestamp(), time.now()); metrics.getPreconsensusEventFileOldestSeconds().set(age.toSeconds()); } updateFileSizeMetrics(); @@ -140,7 +157,7 @@ public PreConsensusEventFileManager( * @param path the path to the file * @return the wrapper object, or null if the file can't be parsed */ - private static PreConsensusEventFile parseFile(final Path path) { + private static @Nullable PreConsensusEventFile parseFile(@NonNull final Path path) { try { return PreConsensusEventFile.of(path); } catch (final IOException exception) { @@ -157,7 +174,7 @@ private static PreConsensusEventFile parseFile(final Path path) { * @param permitGaps if gaps are permitted in sequence number * @return the handler */ - private Consumer buildFileHandler(final boolean permitGaps) { + private @NonNull Consumer buildFileHandler(final boolean permitGaps) { final ValueReference previousSequenceNumber = new ValueReference<>(-1L); final ValueReference previousMinimumGeneration = new ValueReference<>(-1L); @@ -189,11 +206,11 @@ private Consumer buildFileHandler(final boolean permitGap */ private static void fileSanityChecks( final boolean permitGaps, - final ValueReference previousSequenceNumber, - final ValueReference previousMinimumGeneration, - final ValueReference previousMaximumGeneration, - final ValueReference previousTimestamp, - final PreConsensusEventFile descriptor) { + @NonNull final ValueReference previousSequenceNumber, + @NonNull final ValueReference previousMinimumGeneration, + @NonNull final ValueReference previousMaximumGeneration, + @NonNull final ValueReference previousTimestamp, + @NonNull final PreConsensusEventFile descriptor) { // Sequence number should always monotonically increase if (!permitGaps @@ -247,7 +264,7 @@ private static void fileSanityChecks( * available event files. * @return an iterator that walks over event files in order */ - public Iterator getFileIterator(final long minimumGeneration) { + public @NonNull Iterator getFileIterator(final long minimumGeneration) { return getFileIterator(minimumGeneration, false); } @@ -271,7 +288,7 @@ public Iterator getFileIterator(final long minimumGenerat * generation. * @return an iterator that walks over event files in order */ - public Iterator getFileIterator( + public @NonNull Iterator getFileIterator( final long minimumGeneration, final boolean requireMinimumGeneration) { try { // Returns the index of the last file with a maximum generation that is @@ -321,7 +338,7 @@ public Iterator getFileIterator( * guaranteed to be returned by the iterator. * @return an iterator that walks over events */ - public PreConsensusEventMultiFileIterator getEventIterator(final long minimumGeneration) { + public @NonNull PreConsensusEventMultiFileIterator getEventIterator(final long minimumGeneration) { return new PreConsensusEventMultiFileIterator(minimumGeneration, getFileIterator(minimumGeneration)); } @@ -331,7 +348,7 @@ public PreConsensusEventMultiFileIterator getEventIterator(final long minimumGen * @param minimumGeneration the minimum generation desired by the caller * @return a function for finding a starting file guaranteed for the generation requested by the user */ - private LongToIntFunction buildBinarySearchComparisonLambda(final long minimumGeneration) { + private @NonNull LongToIntFunction buildBinarySearchComparisonLambda(final long minimumGeneration) { return (final long index) -> { final PreConsensusEventFile file = files.get((int) index); final long maxGenerationInFile = file.maximumGeneration(); @@ -357,7 +374,8 @@ private long getNextSequenceNumber() { * * @return a new event file descriptor */ - public PreConsensusEventFile getNextFileDescriptor(final long minimumGeneration, final long maximumGeneration) { + public @NonNull PreConsensusEventFile getNextFileDescriptor( + final long minimumGeneration, final long maximumGeneration) { if (minimumGeneration > maximumGeneration) { throw new IllegalArgumentException("minimum generation must be less than or equal to maximum generation"); @@ -408,7 +426,7 @@ public PreConsensusEventFile getNextFileDescriptor(final long minimumGeneration, * * @param file the file that has been completely written */ - public void finishedWritingFile(final PreConsensusEventMutableFile file) { + public void finishedWritingFile(@NonNull final PreConsensusEventMutableFile file) { totalFileByteCount += file.fileSize(); metrics.getPreconsensusEventFileRate().cycle(); @@ -441,7 +459,7 @@ public void pruneOldFiles(final long minimumGeneration) throws IOException { if (files.size() > 0) { metrics.getPreconsensusEventFileOldestGeneration() .set(files.getFirst().minimumGeneration()); - final Duration age = Duration.between(time.now(), files.getFirst().timestamp()); + final Duration age = Duration.between(files.getFirst().timestamp(), time.now()); metrics.getPreconsensusEventFileOldestSeconds().set(age.toSeconds()); } updateFileSizeMetrics(); diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PreConsensusEventStreamConfig.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PreConsensusEventStreamConfig.java index fbe8a3e7cf12..89daa103c628 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PreConsensusEventStreamConfig.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PreConsensusEventStreamConfig.java @@ -76,11 +76,11 @@ @ConfigData("event.preconsensus") public record PreConsensusEventStreamConfig( @ConfigProperty(defaultValue = "1000") int writeQueueCapacity, - @ConfigProperty(defaultValue = "1d") Duration minimumRetentionPeriod, - @ConfigProperty(defaultValue = "100") int preferredFileSizeMegabytes, - @ConfigProperty(defaultValue = "50") int bootstrapGenerationalSpan, - @ConfigProperty(defaultValue = "30") int generationalUtilizationSpanRunningAverageLength, - @Min(1) @ConfigProperty(defaultValue = "1.2") double generationalSpanOverlapFactor, + @ConfigProperty(defaultValue = "1h") Duration minimumRetentionPeriod, + @ConfigProperty(defaultValue = "10") int preferredFileSizeMegabytes, + @ConfigProperty(defaultValue = "1000") int bootstrapGenerationalSpan, + @ConfigProperty(defaultValue = "5") int generationalUtilizationSpanRunningAverageLength, + @Min(1) @ConfigProperty(defaultValue = "2") double generationalSpanOverlapFactor, @ConfigProperty(defaultValue = "5") int minimumGenerationalCapacity, @ConfigProperty(defaultValue = "false") boolean permitGaps, @ConfigProperty(defaultValue = "preconsensus-events") Path databaseDirectory, diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PreconsensusEventMetrics.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PreconsensusEventMetrics.java index ac52fd72d510..5d8f1be9d659 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PreconsensusEventMetrics.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PreconsensusEventMetrics.java @@ -61,9 +61,9 @@ public class PreconsensusEventMetrics { private final RunningAverageMetric preconsensusEventAverageFileSpan; private static final RunningAverageMetric.Config PRECONSENSUS_EVENT_AVERAGE_UN_UTILIZED_FILE_SPAN_CONFIG = - new RunningAverageMetric.Config(CATEGORY, "preconsensusEventAverageUnInitializedFileSpan") + new RunningAverageMetric.Config(CATEGORY, "preconsensusEventAverageUnutilizedFileSpan") .withUnit("generations") - .withDescription("The average un-utilized generational span of preconsensus event files. " + .withDescription("The average unutilized generational span of preconsensus event files. " + "Only reflects files written since the last restart. Smaller is better."); private final RunningAverageMetric preconsensusEventAverageUnUtilizedFileSpan; diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/SyncPreConsensusEventWriter.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/SyncPreConsensusEventWriter.java index 02fbdb5da902..a59f529bf917 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/SyncPreConsensusEventWriter.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/SyncPreConsensusEventWriter.java @@ -16,12 +16,16 @@ package com.swirlds.platform.event.preconsensus; +import static com.swirlds.base.ArgumentUtils.throwArgNull; + +import com.swirlds.common.context.PlatformContext; import com.swirlds.common.threading.CountUpLatch; import com.swirlds.common.utility.LongRunningAverage; import com.swirlds.common.utility.Startable; import com.swirlds.common.utility.Stoppable; import com.swirlds.common.utility.Units; import com.swirlds.platform.internal.EventImpl; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.io.UncheckedIOException; import java.time.Duration; @@ -109,11 +113,17 @@ public class SyncPreConsensusEventWriter implements PreConsensusEventWriter, Sta /** * Create a new PreConsensusEventWriter. * - * @param config configuration for preconsensus event streams + * @param platformContext the platform context * @param fileManager manages all preconsensus event stream files currently on disk */ public SyncPreConsensusEventWriter( - final PreConsensusEventStreamConfig config, final PreConsensusEventFileManager fileManager) { + @NonNull final PlatformContext platformContext, @NonNull final PreConsensusEventFileManager fileManager) { + + throwArgNull(platformContext, "platformContext"); + throwArgNull(fileManager, "fileManager"); + + final PreConsensusEventStreamConfig config = + platformContext.getConfiguration().getConfigData(PreConsensusEventStreamConfig.class); preferredFileSizeMegabytes = config.preferredFileSizeMegabytes(); @@ -130,7 +140,7 @@ public SyncPreConsensusEventWriter( * {@inheritDoc} */ @Override - public synchronized void writeEvent(final EventImpl event) { + public synchronized void writeEvent(@NonNull final EventImpl event) { validateSequenceNumber(event); if (event.getGeneration() >= minimumGenerationNonAncient) { writeEventToStream(event); @@ -143,7 +153,7 @@ public synchronized void writeEvent(final EventImpl event) { /** * Make sure that the event has a valid stream sequence number. */ - private static void validateSequenceNumber(final EventImpl event) { + private static void validateSequenceNumber(@NonNull final EventImpl event) { if (event.getStreamSequenceNumber() == EventImpl.NO_STREAM_SEQUENCE_NUMBER || event.getStreamSequenceNumber() == EventImpl.STALE_EVENT_STREAM_SEQUENCE_NUMBER) { throw new IllegalStateException("Event must have a valid stream sequence number"); @@ -151,24 +161,30 @@ private static void validateSequenceNumber(final EventImpl event) { } /** - * Flush the stream if needed. Should be called after each event is written. + * Flush the stream if needed. */ - private void flushIfNeeded() { - // Remove all events that have already been flushed. This scenario is relevant after a file is closed. - while (!flushableEvents.isEmpty() && flushableEvents.peek() < lastWrittenEvent) { - flushableEvents.remove(); - } - if (!flushableEvents.isEmpty()) { + private synchronized void flushIfNeeded() { + while (!flushableEvents.isEmpty()) { final long nextFlushableEvent = flushableEvents.peek(); - if (nextFlushableEvent == lastWrittenEvent) { - try { - currentMutableFile.flush(); - } catch (final IOException e) { - throw new UncheckedIOException("unable to flush", e); - } - markEventsAsFlushed(); - flushableEvents.remove(); + + if (nextFlushableEvent > lastWrittenEvent) { + // We can't flush this until it gets written + break; } + + flushableEvents.remove(); + + if (nextFlushableEvent <= lastFlushedEvent.getCount()) { + // This is already flushed + continue; + } + + try { + currentMutableFile.flush(); + } catch (final IOException e) { + throw new UncheckedIOException("unable to flush", e); + } + markEventsAsFlushed(); } } @@ -177,7 +193,8 @@ private void flushIfNeeded() { * * @param event the event to write */ - private void writeEventToStream(final EventImpl event) { + private void writeEventToStream(@NonNull final EventImpl event) { + throwArgNull(event, "event"); try { prepareOutputStream(event); currentMutableFile.writeEvent(event); @@ -213,7 +230,8 @@ public synchronized void setMinimumGenerationToStore(final long minimumGeneratio * {@inheritDoc} */ @Override - public synchronized boolean isEventDurable(final EventImpl event) { + public synchronized boolean isEventDurable(@NonNull final EventImpl event) { + throwArgNull(event, "event"); if (event.getStreamSequenceNumber() == EventImpl.STALE_EVENT_STREAM_SEQUENCE_NUMBER) { // Stale events are not written to disk. return false; @@ -225,11 +243,12 @@ public synchronized boolean isEventDurable(final EventImpl event) { * {@inheritDoc} */ @Override - public void waitUntilDurable(final EventImpl event) throws InterruptedException { + public void waitUntilDurable(@NonNull final EventImpl event) throws InterruptedException { + throwArgNull(event, "event"); if (event.getStreamSequenceNumber() == EventImpl.STALE_EVENT_STREAM_SEQUENCE_NUMBER) { throw new IllegalStateException("Event is stale and will never be durable"); } - + flushIfNeeded(); lastFlushedEvent.await(event.getStreamSequenceNumber()); } @@ -237,11 +256,14 @@ public void waitUntilDurable(final EventImpl event) throws InterruptedException * {@inheritDoc} */ @Override - public boolean waitUntilDurable(final EventImpl event, final Duration timeToWait) throws InterruptedException { + public boolean waitUntilDurable(@NonNull final EventImpl event, @NonNull final Duration timeToWait) + throws InterruptedException { + throwArgNull(event, "event"); + throwArgNull(timeToWait, "timeToWait"); if (event.getStreamSequenceNumber() == EventImpl.STALE_EVENT_STREAM_SEQUENCE_NUMBER) { throw new IllegalStateException("Event is stale and will never be durable"); } - + flushIfNeeded(); return lastFlushedEvent.await(event.getStreamSequenceNumber(), timeToWait); } @@ -267,7 +289,8 @@ private void markEventsAsFlushed() { * {@inheritDoc} */ @Override - public synchronized void requestFlush(final EventImpl event) { + public synchronized void requestFlush(@NonNull final EventImpl event) { + throwArgNull(event, "event"); final long eventSequenceNumber = event.getStreamSequenceNumber(); if (eventSequenceNumber == EventImpl.STALE_EVENT_STREAM_SEQUENCE_NUMBER) { // Stale events are not written to disk. @@ -279,19 +302,7 @@ public synchronized void requestFlush(final EventImpl event) { return; } - if (lastWrittenEvent < eventSequenceNumber) { - // We haven't yet written this event, event will be flushed as soon as it is written. - flushableEvents.add(eventSequenceNumber); - return; - } - - // We have written the event to the stream, flush immediately - try { - currentMutableFile.flush(); - } catch (final IOException e) { - throw new UncheckedIOException(e); - } - markEventsAsFlushed(); + flushableEvents.add(eventSequenceNumber); } /** @@ -337,7 +348,7 @@ private long computeNewFileSpan(final long minimumFileGeneration, final long nex * * @param eventToWrite the event that is about to be written */ - private void prepareOutputStream(final EventImpl eventToWrite) throws IOException { + private void prepareOutputStream(@NonNull final EventImpl eventToWrite) throws IOException { if (currentMutableFile != null && (!currentMutableFile.canContain(eventToWrite) || currentMutableFile.fileSize() * Units.BYTES_TO_MEBIBYTES >= preferredFileSizeMegabytes)) { diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/ConsensusRoundHandler.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/ConsensusRoundHandler.java index 7f5c0935f68b..b7049e0916e5 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/ConsensusRoundHandler.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/eventhandling/ConsensusRoundHandler.java @@ -20,6 +20,7 @@ import static com.swirlds.logging.LogMarker.STARTUP; import static com.swirlds.platform.SwirldsPlatform.PLATFORM_THREAD_POOL_NAME; +import com.swirlds.base.functions.ThrowingConsumer; import com.swirlds.common.config.ConsensusConfig; import com.swirlds.common.config.singleton.ConfigurationHolder; import com.swirlds.common.context.PlatformContext; @@ -47,6 +48,7 @@ import com.swirlds.platform.state.SwirldStateManager; import com.swirlds.platform.state.signed.SignedState; import com.swirlds.platform.stats.CycleTimingStat; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Arrays; import java.util.List; import java.util.concurrent.BlockingQueue; @@ -111,6 +113,11 @@ public class ConsensusRoundHandler implements ConsensusRoundObserver, Clearable, private final RoundAppliedToStateConsumer roundAppliedToStateConsumer; + /** + * A method that blocks until an event becomes durable. + */ + final ThrowingConsumer waitForEventDurability; + /** * The number of non-ancient rounds. */ @@ -129,21 +136,23 @@ public class ConsensusRoundHandler implements ConsensusRoundObserver, Clearable, * @param eventStreamManager the event stream manager to send consensus events to * @param stateHashSignQueue the queue thread that handles hashing and collecting signatures of new * self-signed states + * @param waitForEventDurability a method that blocks until an event becomes durable. * @param enterFreezePeriod puts the system in a freeze state when executed * @param softwareVersion the current version of the software */ public ConsensusRoundHandler( - final PlatformContext platformContext, - final ThreadManager threadManager, + @NonNull final PlatformContext platformContext, + @NonNull final ThreadManager threadManager, final long selfId, - final SettingsProvider settings, - final SwirldStateManager swirldStateManager, - final ConsensusHandlingMetrics consensusHandlingMetrics, - final EventStreamManager eventStreamManager, - final BlockingQueue stateHashSignQueue, - final Runnable enterFreezePeriod, - final RoundAppliedToStateConsumer roundAppliedToStateConsumer, - final SoftwareVersion softwareVersion) { + @NonNull final SettingsProvider settings, + @NonNull final SwirldStateManager swirldStateManager, + @NonNull final ConsensusHandlingMetrics consensusHandlingMetrics, + @NonNull final EventStreamManager eventStreamManager, + @NonNull final BlockingQueue stateHashSignQueue, + @NonNull final ThrowingConsumer waitForEventDurability, + @NonNull final Runnable enterFreezePeriod, + @NonNull final RoundAppliedToStateConsumer roundAppliedToStateConsumer, + @NonNull final SoftwareVersion softwareVersion) { this.roundAppliedToStateConsumer = roundAppliedToStateConsumer; @@ -180,6 +189,8 @@ public ConsensusRoundHandler( .getConfiguration() .getConfigData(ConsensusConfig.class) .roundsNonAncient(); + + this.waitForEventDurability = waitForEventDurability; } /** @@ -317,10 +328,12 @@ private void applyConsensusRoundToState(final ConsensusRound round) throws Inter final CycleTimingStat consensusTimingStat = consensusHandlingMetrics.getConsCycleStat(); consensusTimingStat.startCycle(); - propagateConsensusData(round); + waitForEventDurability.accept(round.getKeystoneEvent()); consensusTimingStat.setTimePoint(1); + propagateConsensusData(round); + if (round.getEventCount() > 0) { consensusHandlingMetrics.recordConsensusTime(round.getLastEvent().getLastTransTime()); } diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/internal/ConsensusRound.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/internal/ConsensusRound.java index 83095ba7d4dc..730a0d6ad09b 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/internal/ConsensusRound.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/internal/ConsensusRound.java @@ -16,6 +16,7 @@ package com.swirlds.platform.internal; +import static com.swirlds.base.ArgumentUtils.throwArgNull; import static org.apache.commons.lang3.builder.ToStringStyle.SHORT_PREFIX_STYLE; import com.swirlds.common.system.Round; @@ -23,6 +24,7 @@ import com.swirlds.platform.consensus.GraphGenerations; import com.swirlds.platform.event.EventUtils; import com.swirlds.platform.util.iterator.TypedIterator; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -50,14 +52,29 @@ public class ConsensusRound implements Round { /** The number of application transactions in this round */ private int numAppTransactions = 0; + /** + * The event that, when added to the hashgraph, caused this round to reach consensus. + */ + private final EventImpl keystoneEvent; + /** * Create a new instance with the provided consensus events. * - * @param consensusEvents the events in the round, in consensus order - * @param generations the consensus generations for this round + * @param consensusEvents the events in the round, in consensus order + * @param keystoneEvent the event that, when added to the hashgraph, caused this round to reach consensus + * @param generations the consensus generations for this round */ - public ConsensusRound(final List consensusEvents, final GraphGenerations generations) { + public ConsensusRound( + @NonNull final List consensusEvents, + @NonNull final EventImpl keystoneEvent, + @NonNull final GraphGenerations generations) { + + throwArgNull(consensusEvents, "consensusEvents"); + throwArgNull(keystoneEvent, "keystoneEvent"); + throwArgNull(generations, "generations"); + this.consensusEvents = Collections.unmodifiableList(consensusEvents); + this.keystoneEvent = keystoneEvent; this.generations = generations; for (final EventImpl e : consensusEvents) { @@ -160,6 +177,13 @@ public boolean equals(final Object o) { .isEquals(); } + /** + * @return the event that, when added to the hashgraph, caused this round to reach consensus + */ + public @NonNull EventImpl getKeystoneEvent() { + return keystoneEvent; + } + @Override public int hashCode() { return new HashCodeBuilder(17, 37).append(consensusEvents).toHashCode(); diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/metrics/ConsensusHandlingMetrics.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/metrics/ConsensusHandlingMetrics.java index fd2cff2ae268..9ca6e60b7fac 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/metrics/ConsensusHandlingMetrics.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/metrics/ConsensusHandlingMetrics.java @@ -80,13 +80,13 @@ public ConsensusHandlingMetrics(final Metrics metrics, final Time time) { INTERNAL_CATEGORY, "consRound", List.of( + Pair.of( + "keystoneFlushMillis/round", + "average time to flush a round's keystone event to disk"), Pair.of( "dataPropMillis/round", "average time to propagate consensus data to transactions"), Pair.of("handleMillis/round", "average time to handle a consensus round"), - Pair.of( - "roundCompletedDispatch/round", - "average time to send round completed dispatch"), Pair.of( "storeMillis/round", "average time to add consensus round events to signed state storage"), diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/observers/EventObserverDispatcher.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/observers/EventObserverDispatcher.java index 5f71058b0a39..77dbaa3e9e5d 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/observers/EventObserverDispatcher.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/observers/EventObserverDispatcher.java @@ -19,12 +19,12 @@ import com.swirlds.platform.event.GossipEvent; import com.swirlds.platform.internal.ConsensusRound; import com.swirlds.platform.internal.EventImpl; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ArrayList; import java.util.List; /** - * A type which accretes observers of several different types. This type facilitates - * the implied observer DAG. + * A type which accretes observers of several different types. This type facilitates the implied observer DAG. */ public class EventObserverDispatcher implements EventReceivedObserver, @@ -47,10 +47,9 @@ public class EventObserverDispatcher /** * Constructor * - * @param observers - * a list of {@link EventObserver} implementors + * @param observers a list of {@link EventObserver} implementors */ - public EventObserverDispatcher(final List observers) { + public EventObserverDispatcher(@NonNull final List observers) { eventReceivedObservers = new ArrayList<>(); preConsensusEventObservers = new ArrayList<>(); eventAddedObservers = new ArrayList<>(); @@ -65,8 +64,7 @@ public EventObserverDispatcher(final List observers) { /** * Constructor * - * @param observers - * a variadic sequence of {@link EventObserver} implementors + * @param observers a variadic sequence of {@link EventObserver} implementors */ public EventObserverDispatcher(final EventObserver... observers) { this(List.of(observers)); @@ -75,10 +73,9 @@ public EventObserverDispatcher(final EventObserver... observers) { /** * Adds an observer * - * @param observer - * the observer to add + * @param observer the observer to add */ - public void addObserver(final EventObserver observer) { + public void addObserver(@NonNull final EventObserver observer) { if (observer instanceof EventReceivedObserver o) { eventReceivedObservers.add(o); } diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/signed/SignedStateFileManager.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/signed/SignedStateFileManager.java index 32caa7657a7a..8742cd76b29e 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/signed/SignedStateFileManager.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/signed/SignedStateFileManager.java @@ -16,8 +16,8 @@ package com.swirlds.platform.state.signed; +import static com.swirlds.base.ArgumentUtils.throwArgNull; import static com.swirlds.common.io.utility.FileUtils.deleteDirectoryAndLog; -import static com.swirlds.common.utility.CommonUtils.throwArgNull; import static com.swirlds.logging.LogMarker.EXCEPTION; import static com.swirlds.logging.LogMarker.STATE_TO_DISK; import static com.swirlds.platform.SwirldsPlatform.PLATFORM_THREAD_POOL_NAME; @@ -36,7 +36,9 @@ import com.swirlds.common.threading.manager.ThreadManager; import com.swirlds.common.time.Time; import com.swirlds.common.utility.Startable; +import com.swirlds.platform.components.state.output.MinimumGenerationNonAncientConsumer; import com.swirlds.platform.components.state.output.StateToDiskAttemptConsumer; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.nio.file.Path; import java.time.Instant; @@ -99,6 +101,11 @@ public class SignedStateFileManager implements Startable { */ private long minimumGenerationNonAncientForOldestState = -1; + /** + * This method must be called when the minimum generation non-ancient of the oldest state snapshot on disk changes. + */ + private final MinimumGenerationNonAncientConsumer minimumGenerationNonAncientConsumer; + /** * Creates a new instance. * @@ -108,14 +115,15 @@ public class SignedStateFileManager implements Startable { * @param swirldName the name of the swirld */ public SignedStateFileManager( - final PlatformContext context, - final ThreadManager threadManager, - final SignedStateMetrics metrics, - final Time time, - final String mainClassName, - final NodeId selfId, - final String swirldName, - final StateToDiskAttemptConsumer stateToDiskAttemptConsumer) { + @NonNull final PlatformContext context, + @NonNull final ThreadManager threadManager, + @NonNull final SignedStateMetrics metrics, + @NonNull final Time time, + @NonNull final String mainClassName, + @NonNull final NodeId selfId, + @NonNull final String swirldName, + @NonNull final StateToDiskAttemptConsumer stateToDiskAttemptConsumer, + @NonNull final MinimumGenerationNonAncientConsumer minimumGenerationNonAncientConsumer) { this.metrics = throwArgNull(metrics, "metrics"); this.time = time; @@ -124,6 +132,8 @@ public SignedStateFileManager( this.swirldName = swirldName; this.stateToDiskAttemptConsumer = stateToDiskAttemptConsumer; this.stateConfig = context.getConfiguration().getConfigData(StateConfig.class); + this.minimumGenerationNonAncientConsumer = + throwArgNull(minimumGenerationNonAncientConsumer, "minimumGenerationNonAncientConsumer"); final BasicConfig basicConfig = context.getConfiguration().getConfigData(BasicConfig.class); @@ -143,6 +153,8 @@ public SignedStateFileManager( savedStates[savedStates.length - 1].getMetadata().minimumGenerationNonAncient(); if (generationNonAncient != null) { minimumGenerationNonAncientForOldestState = generationNonAncient; + minimumGenerationNonAncientConsumer.newMinimumGenerationNonAncient( + minimumGenerationNonAncientForOldestState); } } } @@ -367,11 +379,15 @@ private synchronized void deleteOldStates() { // Keep the minimum generation non-ancient for the oldest state up to date if (index >= 0) { final SavedStateMetadata oldestStateMetadata = savedStates[index].getMetadata(); - final long minimumGeneration = oldestStateMetadata.minimumGenerationNonAncient() == null + + final long oldestStateMinimumGeneration = oldestStateMetadata.minimumGenerationNonAncient() == null ? -1L : oldestStateMetadata.minimumGenerationNonAncient(); - minimumGenerationNonAncientForOldestState = - Math.max(minimumGenerationNonAncientForOldestState, minimumGeneration); + + if (minimumGenerationNonAncientForOldestState < oldestStateMinimumGeneration) { + minimumGenerationNonAncientForOldestState = oldestStateMinimumGeneration; + minimumGenerationNonAncientConsumer.newMinimumGenerationNonAncient(oldestStateMinimumGeneration); + } } } diff --git a/platform-sdk/swirlds-platform-core/src/main/java/module-info.java b/platform-sdk/swirlds-platform-core/src/main/java/module-info.java index 2b20646caf45..a47298d68fd5 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/module-info.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/module-info.java @@ -118,9 +118,11 @@ exports com.swirlds.platform.components.transaction; exports com.swirlds.platform.components.transaction.system.internal; exports com.swirlds.platform.components.transaction.system; + exports com.swirlds.platform.event.preconsensus; /* Swirlds Libraries */ requires transitive com.swirlds.common; + requires com.swirlds.base; requires com.swirlds.common.test; requires com.swirlds.test.framework; requires com.swirlds.logging; diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/ConsensusRoundTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/ConsensusRoundTests.java index 96fb9f462985..ddb0bf7e2c80 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/ConsensusRoundTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/ConsensusRoundTests.java @@ -47,7 +47,7 @@ void testConstructor() { final List events = List.of(e1, e2, e3); - final ConsensusRound round = new ConsensusRound(events, g); + final ConsensusRound round = new ConsensusRound(events, mock(EventImpl.class), g); assertEquals(events, round.getConsensusEvents(), "consensus event list does not match the provided list."); assertEquals(events.size(), round.getNumEvents(), "numEvents does not match the events provided."); @@ -70,7 +70,7 @@ void testNumApplicationTransactions() { events.add(event); } - final ConsensusRound round = new ConsensusRound(events, mock(GraphGenerations.class)); + final ConsensusRound round = new ConsensusRound(events, mock(EventImpl.class), mock(GraphGenerations.class)); assertEquals( numActualTransactions, round.getNumAppTransactions(), "Incorrect number of application transactions."); diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/SignedStateFileManagerTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/SignedStateFileManagerTests.java index fe1fa07b352b..f1cb04808049 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/SignedStateFileManagerTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/SignedStateFileManagerTests.java @@ -75,6 +75,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -198,7 +199,8 @@ void standardOperationTest(final boolean successExpected) throws InterruptedExce MAIN_CLASS_NAME, SELF_ID, SWIRLD_NAME, - consumer); + consumer, + x -> {}); manager.start(); manager.saveSignedStateToDisk(signedState); @@ -245,7 +247,8 @@ void saveFatalSignedState() throws InterruptedException, IOException { MAIN_CLASS_NAME, SELF_ID, SWIRLD_NAME, - consumer); + consumer, + x -> {}); manager.start(); final Thread thread = new ThreadConfiguration(getStaticThreadManager()) @@ -296,7 +299,8 @@ void saveISSignedState() throws IOException { MAIN_CLASS_NAME, SELF_ID, SWIRLD_NAME, - consumer); + consumer, + x -> {}); manager.start(); manager.dumpState(signedState, "iss", false); @@ -392,7 +396,8 @@ void maxCapacityTest() { MAIN_CLASS_NAME, SELF_ID, SWIRLD_NAME, - consumer); + consumer, + x -> {}); manager.start(); final List states = new ArrayList<>(); @@ -453,6 +458,8 @@ void sequenceOfStatesTest(final boolean startAtGenesis) { final int averageTimeBetweenStates = 10; final double standardDeviationTimeBetweenStates = 0.5; + final AtomicLong minimumGenerationNonAncientSetByCallback = new AtomicLong(); + final SignedStateFileManager manager = new SignedStateFileManager( context, getStaticThreadManager(), @@ -461,7 +468,11 @@ void sequenceOfStatesTest(final boolean startAtGenesis) { MAIN_CLASS_NAME, SELF_ID, SWIRLD_NAME, - (ssw, path, success) -> ssw.release()); + (ssw, path, success) -> ssw.release(), + x -> { + assertTrue(x > minimumGenerationNonAncientSetByCallback.get()); + minimumGenerationNonAncientSetByCallback.set(x); + }); manager.start(); Instant timestamp; @@ -498,6 +509,8 @@ void sequenceOfStatesTest(final boolean startAtGenesis) { final SignedState signedState = new RandomSignedStateGenerator(random) .setConsensusTimestamp(timestamp) .setRound(round) + // .setMinimumGenerationNonAncient(random.nextLong((round + 1) * 100, (round + 1) + // * 101)) // TODO .build(); manager.determineIfStateShouldBeSaved(signedState); @@ -523,6 +536,9 @@ void sequenceOfStatesTest(final boolean startAtGenesis) { assertEquals( oldestMetadata.minimumGenerationNonAncient(), manager.getMinimumGenerationNonAncientForOldestState()); + assertEquals( + minimumGenerationNonAncientSetByCallback.get(), + manager.getMinimumGenerationNonAncientForOldestState()); assertTrue( currentStatesOnDisk.length <= statesOnDisk, @@ -585,7 +601,8 @@ void stateDeletionTest() throws IOException { MAIN_CLASS_NAME, SELF_ID, SWIRLD_NAME, - (ssw, path, success) -> ssw.release()); + (ssw, path, success) -> ssw.release(), + x -> {}); manager.start(); final Path statesDirectory = 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 d5a0462a2de1..3e4a7a181b34 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 @@ -281,7 +281,8 @@ void cleanStateDirectoryTest() throws IOException { MAIN_CLASS_NAME, SELF_ID, SWIRLD_NAME, - (ss, path, success) -> {}); + (ss, path, success) -> {}, + x -> {}); manager.start(); final int rounds = 3; diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/components/state/StateManagementComponentTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/components/state/StateManagementComponentTests.java index 286e1e5ea775..6cbbc1ad5637 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/components/state/StateManagementComponentTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/components/state/StateManagementComponentTests.java @@ -42,6 +42,7 @@ import com.swirlds.common.utility.AutoCloseableWrapper; import com.swirlds.platform.Settings; import com.swirlds.platform.crypto.PlatformSigner; +import com.swirlds.platform.event.preconsensus.PreConsensusEventWriter; import com.swirlds.platform.state.RandomSignedStateGenerator; import com.swirlds.platform.state.signed.SignedState; import com.swirlds.platform.state.signed.SourceOfSignedState; @@ -574,6 +575,7 @@ private DefaultStateManagementComponent newStateManagementComponent(final Random stateHasEnoughSignaturesConsumer::consume, issConsumer::consume, (msg) -> {}, - (msg, t, code) -> {}); + (msg, t, code) -> {}, + mock(PreConsensusEventWriter.class)); } } diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/ConsensusRoundHandlerTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/ConsensusRoundHandlerTests.java index 3476a89cdb4a..8b26e3d1f5f7 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/ConsensusRoundHandlerTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/eventhandling/ConsensusRoundHandlerTests.java @@ -119,6 +119,7 @@ void queueNotDrainedOnReconnect() { consensusHandlingMetrics, eventStreamManager, stateHashSignQueue, + e -> {}, () -> {}, (round) -> {}, null); @@ -206,6 +207,7 @@ private void initConsensusHandler(final SwirldState swirldState) { consensusHandlingMetrics, eventStreamManager, stateHashSignQueue, + e -> {}, () -> {}, (round) -> {}, null); diff --git a/platform-sdk/swirlds-unit-tests/common/swirlds-common-test/build.gradle.kts b/platform-sdk/swirlds-unit-tests/common/swirlds-common-test/build.gradle.kts index 547e8b4a49b6..5b542bd0c1e7 100644 --- a/platform-sdk/swirlds-unit-tests/common/swirlds-common-test/build.gradle.kts +++ b/platform-sdk/swirlds-unit-tests/common/swirlds-common-test/build.gradle.kts @@ -26,6 +26,7 @@ extraJavaModuleInfo { failOnMissingModuleInfo.set(false) } dependencies { // Individual Dependencies implementation(project(":swirlds-common")) + implementation(project(":swirlds-base")) api(testLibs.junit.jupiter.api) compileOnly(libs.spotbugs.annotations) diff --git a/platform-sdk/swirlds-unit-tests/common/swirlds-common-test/src/main/java/module-info.java b/platform-sdk/swirlds-unit-tests/common/swirlds-common-test/src/main/java/module-info.java index 36d3de2069f8..ad3944915bf7 100644 --- a/platform-sdk/swirlds-unit-tests/common/swirlds-common-test/src/main/java/module-info.java +++ b/platform-sdk/swirlds-unit-tests/common/swirlds-common-test/src/main/java/module-info.java @@ -12,6 +12,7 @@ exports com.swirlds.common.test.crypto; exports com.swirlds.common.test.fcqueue; + requires com.swirlds.base; requires com.swirlds.test.framework; requires com.swirlds.common; requires org.bouncycastle.provider; diff --git a/platform-sdk/swirlds-unit-tests/common/swirlds-common-test/src/test/java/com/swirlds/common/test/utility/UninterruptableTest.java b/platform-sdk/swirlds-unit-tests/common/swirlds-common-test/src/test/java/com/swirlds/common/test/utility/UninterruptableTest.java index be7c80806b3f..34b268d386ff 100644 --- a/platform-sdk/swirlds-unit-tests/common/swirlds-common-test/src/test/java/com/swirlds/common/test/utility/UninterruptableTest.java +++ b/platform-sdk/swirlds-unit-tests/common/swirlds-common-test/src/test/java/com/swirlds/common/test/utility/UninterruptableTest.java @@ -207,4 +207,31 @@ void tryToSleepTest() throws InterruptedException { assertEventuallyFalse(thread::isAlive, Duration.ofSeconds(1), "thread should be dead by now"); assertFalse(exceptionEncountered.get(), "no exceptions should have been thrown"); } + + @Test + @DisplayName("abortAndLogIfInterrupted() Consumer Test") + void abortAndLogIfInterruptedConsumerTest() { + final AtomicBoolean exceptionEncountered = new AtomicBoolean(false); + + final BlockingQueue queue = new LinkedBlockingDeque<>(1); + + final Thread thread = new ThreadConfiguration(getStaticThreadManager()) + .setRunnable(() -> { + abortAndLogIfInterrupted(queue::put, 0, "unexpected error"); + abortAndLogIfInterrupted(queue::put, 1, "expected error"); + }) + .setExceptionHandler((t, throwable) -> exceptionEncountered.set(true)) + .build(true); + + assertEventuallyEquals(1, queue::size, Duration.ofSeconds(1), "element should eventually added to queue"); + + // Thread will be blocked on adding next element. Interrupt should unblock the thread. + thread.interrupt(); + + assertEventuallyFalse(thread::isAlive, Duration.ofSeconds(1), "thread should be dead"); + + assertEquals(0, queue.remove(), "unexpected element in queue"); + assertTrue(queue.isEmpty(), "nothing else should be in the queue"); + assertFalse(exceptionEncountered.get(), "no exceptions should have been thrown"); + } } diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/chatter/ChatterNotifierTest.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/chatter/ChatterNotifierTest.java index ca18216dcee6..783c46bff0f5 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/chatter/ChatterNotifierTest.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/chatter/ChatterNotifierTest.java @@ -18,6 +18,7 @@ import static com.swirlds.test.framework.TestQualifierTags.TIME_CONSUMING; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import com.swirlds.platform.chatter.ChatterNotifier; @@ -65,7 +66,7 @@ void testEventAdded() { @Test @Tag(TIME_CONSUMING) void testPurge() { - notifier.consensusRound(new ConsensusRound(List.of(event), new Generations(1, 2, 3))); + notifier.consensusRound(new ConsensusRound(List.of(event), mock(EventImpl.class), new Generations(1, 2, 3))); verify(chatterCore).shiftWindow(1); } } diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/components/EventObserverDispatcherTests.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/components/EventObserverDispatcherTests.java index 2fc591c702c9..9680ea556a80 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/components/EventObserverDispatcherTests.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/components/EventObserverDispatcherTests.java @@ -21,8 +21,10 @@ import static com.swirlds.platform.test.observers.ObservationType.STALE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.mock; import com.swirlds.platform.internal.ConsensusRound; +import com.swirlds.platform.internal.EventImpl; import com.swirlds.platform.observers.EventObserverDispatcher; import com.swirlds.platform.sync.Generations; import com.swirlds.platform.test.event.SimpleEvent; @@ -71,7 +73,9 @@ void test() { dispatchAndCheck(STALE, e1, stale, addedStale); e2.setLastInRoundReceived(true); - dispatchAndCheckConsensus(new ConsensusRound(List.of(e1, e2), Generations.GENESIS_GENERATIONS)); + + dispatchAndCheckConsensus( + new ConsensusRound(List.of(e1, e2), mock(EventImpl.class), Generations.GENESIS_GENERATIONS)); } private void dispatchAndCheckConsensus(final ConsensusRound consensusRound, final SimpleEventTracker... yes) { diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/components/TransactionHandlingTestUtils.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/components/TransactionHandlingTestUtils.java index a296dc2d400f..b16409df2784 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/components/TransactionHandlingTestUtils.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/components/TransactionHandlingTestUtils.java @@ -72,6 +72,6 @@ public static ConsensusRound newDummyRound(final List roundContents) { events.add(newDummyEvent(transactionCount)); } - return new ConsensusRound(events, mock(GraphGenerations.class)); + return new ConsensusRound(events, mock(EventImpl.class), mock(GraphGenerations.class)); } } diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/intake/OrphanEventsIntakeTest.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/intake/OrphanEventsIntakeTest.java index 90673dbc50e5..a323479e3e89 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/intake/OrphanEventsIntakeTest.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/intake/OrphanEventsIntakeTest.java @@ -16,6 +16,8 @@ package com.swirlds.platform.test.event.intake; +import static org.mockito.Mockito.mock; + import com.swirlds.common.config.ConsensusConfig; import com.swirlds.common.config.singleton.ConfigurationHolder; import com.swirlds.common.crypto.Hash; @@ -47,7 +49,6 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; class OrphanEventsIntakeTest { @Test @@ -102,8 +103,8 @@ public Intake(final long generatorSeed, final long randomSeed, final int numNode new EventObserverDispatcher( (EventAddedObserver) e -> linkedEventMap.put(e.getBaseHash(), e), (ConsensusRoundObserver) rnd -> consensusEvents.addAll(rnd.getConsensusEvents())), - Mockito.mock(IntakeCycleStats.class), - Mockito.mock(ShadowGraph.class)); + mock(IntakeCycleStats.class), + mock(ShadowGraph.class)); } public void generateAndFeed(final int numEvents) { diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/preconsensus/AsyncPreConsensusEventWriterTests.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/preconsensus/AsyncPreConsensusEventWriterTests.java index a96a13bc96ac..8b440c2df4a6 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/preconsensus/AsyncPreConsensusEventWriterTests.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/preconsensus/AsyncPreConsensusEventWriterTests.java @@ -28,23 +28,27 @@ import com.swirlds.common.constructable.ConstructableRegistry; import com.swirlds.common.constructable.ConstructableRegistryException; +import com.swirlds.common.context.PlatformContext; +import com.swirlds.common.context.internal.DefaultPlatformContext; +import com.swirlds.common.crypto.CryptographyHolder; import com.swirlds.common.internal.SettingsCommon; import com.swirlds.common.io.IOIterator; import com.swirlds.common.io.utility.FileUtils; +import com.swirlds.common.metrics.Metrics; import com.swirlds.common.system.transaction.internal.ConsensusTransactionImpl; import com.swirlds.common.system.transaction.internal.SwirldTransaction; import com.swirlds.common.test.RandomUtils; import com.swirlds.common.test.TransactionGenerator; +import com.swirlds.common.test.fixtures.FakeTime; import com.swirlds.common.test.io.FileManipulation; import com.swirlds.common.test.metrics.NoOpMetrics; import com.swirlds.common.time.OSTime; +import com.swirlds.config.api.Configuration; import com.swirlds.platform.event.preconsensus.AsyncPreConsensusEventWriter; import com.swirlds.platform.event.preconsensus.PreConsensusEventFile; import com.swirlds.platform.event.preconsensus.PreConsensusEventFileManager; import com.swirlds.platform.event.preconsensus.PreConsensusEventMultiFileIterator; -import com.swirlds.platform.event.preconsensus.PreConsensusEventStreamConfig; import com.swirlds.platform.event.preconsensus.PreConsensusEventWriter; -import com.swirlds.platform.event.preconsensus.PreconsensusEventMetrics; import com.swirlds.platform.event.preconsensus.PreconsensusEventStreamSequencer; import com.swirlds.platform.event.preconsensus.SyncPreConsensusEventWriter; import com.swirlds.platform.internal.EventImpl; @@ -104,10 +108,6 @@ void afterEach() throws IOException { FileUtils.deleteDirectory(testDirectory); } - static PreconsensusEventMetrics buildMetrics() { - return new PreconsensusEventMetrics(new NoOpMetrics()); - } - /** * Build a transaction generator. */ @@ -162,10 +162,10 @@ public static void assertEventsAreEqual(final EventImpl expected, final EventImp * Perform verification on a stream written by a {@link SyncPreConsensusEventWriter}. * * @param events the events that were written to the stream - * @param config the configuration of the writer + * @param platformContext the platform context */ static void verifyStream( - final List events, final PreConsensusEventStreamConfig config, final int truncatedFileCount) + final List events, final PlatformContext platformContext, final int truncatedFileCount) throws IOException { long lastGeneration = Long.MIN_VALUE; @@ -174,7 +174,7 @@ static void verifyStream( } final PreConsensusEventFileManager reader = - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()); + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0); // Verify that the events were written correctly final PreConsensusEventMultiFileIterator eventsIterator = reader.getEventIterator(0); @@ -234,6 +234,17 @@ static void verifyStream( } } + private PlatformContext buildContext() { + final Configuration configuration = new TestConfigBuilder() + .withValue("event.preconsensus.databaseDirectory", testDirectory) + .withValue("event.preconsensus.preferredFileSizeMegabytes", 5) + .getOrCreateConfig(); + + final Metrics metrics = new NoOpMetrics(); + + return new DefaultPlatformContext(configuration, metrics, CryptographyHolder.get()); + } + /** * In this test, keep adding events without increasing the first non-ancient generation. This will force the * preferred generations per file to eventually be reached and exceeded. @@ -255,18 +266,16 @@ void overflowTest(final boolean artificialPauses) throws IOException, Interrupte final int idleWaitPeriodMs = 10; - final PreConsensusEventStreamConfig config = new TestConfigBuilder() - .withValue("event.preconsensus.databaseDirectory", testDirectory) - .withValue("event.preconsensus.preferredFileSizeMegabytes", 5) - .getOrCreateConfig() - .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); final PreConsensusEventFileManager fileManager = - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()); + new PreConsensusEventFileManager(buildContext(), OSTime.getInstance(), 0); final PreconsensusEventStreamSequencer sequencer = new PreconsensusEventStreamSequencer(); final PreConsensusEventWriter writer = new AsyncPreConsensusEventWriter( - getStaticThreadManager(), config, new SyncPreConsensusEventWriter(config, fileManager)); + platformContext, + getStaticThreadManager(), + new SyncPreConsensusEventWriter(platformContext, fileManager)); writer.start(); @@ -284,7 +293,7 @@ void overflowTest(final boolean artificialPauses) throws IOException, Interrupte writer.stop(); - verifyStream(events, config, 0); + verifyStream(events, platformContext, 0); // Without advancing the first non-ancient generation, // we should never be able to increase the minimum generation from 0. @@ -338,20 +347,17 @@ void advanceNonAncientGenerationTest(final AdvanceNonAncientGenerationParams par final int idleWaitPeriodMs = 10; - final PreConsensusEventStreamConfig config = new TestConfigBuilder() - .withValue("event.preconsensus.databaseDirectory", testDirectory) - .withValue("event.preconsensus.preferredFileSizeMegabytes", 5) - .withValue("event.preconsensus.idleWaitPeriod", idleWaitPeriodMs + "ms") - .withValue("event.preconsensus.minimumRetentionPeriod", idleWaitPeriodMs + "1ns") - .getOrCreateConfig() - .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); - final PreConsensusEventFileManager fileManager = - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()); + final FakeTime time = new FakeTime(Duration.ofMillis(1)); + + final PreConsensusEventFileManager fileManager = new PreConsensusEventFileManager(platformContext, time, 0); final PreconsensusEventStreamSequencer sequencer = new PreconsensusEventStreamSequencer(); final PreConsensusEventWriter writer = new AsyncPreConsensusEventWriter( - getStaticThreadManager(), config, new SyncPreConsensusEventWriter(config, fileManager)); + platformContext, + getStaticThreadManager(), + new SyncPreConsensusEventWriter(platformContext, fileManager)); writer.start(); @@ -364,6 +370,8 @@ void advanceNonAncientGenerationTest(final AdvanceNonAncientGenerationParams par assertFalse(writer.isEventDurable(event)); writer.writeEvent(event); + time.tick(Duration.ofSeconds(1)); + if (event.getGeneration() < minimumGenerationNonAncient) { // This event is ancient and will have been rejected. rejectedEvents.add(event); @@ -395,7 +403,10 @@ void advanceNonAncientGenerationTest(final AdvanceNonAncientGenerationParams par assertFalse(writer.isEventDurable(event)); } - verifyStream(events, config, 0); + verifyStream(events, platformContext, 0); + + // Advance the time so that all files are GC eligible according to the clock. + time.tick(Duration.ofDays(1)); // Prune old files. final long minimumGenerationToStore = events.get(events.size() - 1).getGeneration() / 2; @@ -446,22 +457,16 @@ void restartSimulationTest(final boolean truncateLastFile) throws InterruptedExc } } - final int idleWaitPeriodMs = 10; - - final PreConsensusEventStreamConfig config = new TestConfigBuilder() - .withValue("event.preconsensus.databaseDirectory", testDirectory) - .withValue("event.preconsensus.preferredFileSizeMegabytes", 5) - .withValue("event.preconsensus.idleWaitPeriod", idleWaitPeriodMs + "ms") - .withValue("event.preconsensus.minimumRetentionPeriod", idleWaitPeriodMs + "1ns") - .getOrCreateConfig() - .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); final PreConsensusEventFileManager fileManager = - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()); + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0); final PreconsensusEventStreamSequencer sequencer1 = new PreconsensusEventStreamSequencer(); final PreConsensusEventWriter writer1 = new AsyncPreConsensusEventWriter( - getStaticThreadManager(), config, new SyncPreConsensusEventWriter(config, fileManager)); + platformContext, + getStaticThreadManager(), + new SyncPreConsensusEventWriter(platformContext, fileManager)); writer1.start(); @@ -503,7 +508,9 @@ void restartSimulationTest(final boolean truncateLastFile) throws InterruptedExc final PreconsensusEventStreamSequencer sequencer2 = new PreconsensusEventStreamSequencer(); final PreConsensusEventWriter writer2 = new AsyncPreConsensusEventWriter( - getStaticThreadManager(), config, new SyncPreConsensusEventWriter(config, fileManager)); + platformContext, + getStaticThreadManager(), + new SyncPreConsensusEventWriter(platformContext, fileManager)); writer2.start(); final Set rejectedEvents2 = new HashSet<>(); @@ -535,6 +542,6 @@ void restartSimulationTest(final boolean truncateLastFile) throws InterruptedExc allEvents.addAll(events1); allEvents.addAll(events2); - verifyStream(allEvents, config, truncateLastFile ? 1 : 0); + verifyStream(allEvents, platformContext, truncateLastFile ? 1 : 0); } } diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/preconsensus/PreConsensusEventFileManagerTests.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/preconsensus/PreConsensusEventFileManagerTests.java index 4e1ce39cd697..93d98ee91287 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/preconsensus/PreConsensusEventFileManagerTests.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/preconsensus/PreConsensusEventFileManagerTests.java @@ -23,16 +23,19 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.swirlds.common.context.PlatformContext; +import com.swirlds.common.context.internal.DefaultPlatformContext; +import com.swirlds.common.crypto.CryptographyHolder; import com.swirlds.common.io.streams.SerializableDataOutputStream; import com.swirlds.common.io.utility.FileUtils; +import com.swirlds.common.metrics.Metrics; import com.swirlds.common.test.fixtures.FakeTime; import com.swirlds.common.test.metrics.NoOpMetrics; import com.swirlds.common.time.OSTime; import com.swirlds.common.utility.CompareTo; +import com.swirlds.config.api.Configuration; import com.swirlds.platform.event.preconsensus.PreConsensusEventFile; import com.swirlds.platform.event.preconsensus.PreConsensusEventFileManager; -import com.swirlds.platform.event.preconsensus.PreConsensusEventStreamConfig; -import com.swirlds.platform.event.preconsensus.PreconsensusEventMetrics; import com.swirlds.test.framework.config.TestConfigBuilder; import java.io.FileOutputStream; import java.io.IOException; @@ -61,9 +64,12 @@ class PreConsensusEventFileManagerTests { @TempDir Path testDirectory; + private Path fileDirectory = null; + @BeforeEach void beforeEach() throws IOException { FileUtils.deleteDirectory(testDirectory); + fileDirectory = testDirectory.resolve("0"); } @AfterEach @@ -71,6 +77,22 @@ void afterEach() throws IOException { FileUtils.deleteDirectory(testDirectory); } + private PlatformContext buildContext() { + return buildContext(false); + } + + private PlatformContext buildContext(final boolean permitGaps) { + final Configuration configuration = new TestConfigBuilder() + .withValue("event.preconsensus.databaseDirectory", testDirectory) + .withValue("event.preconsensus.preferredFileSizeMegabytes", 5) + .withValue("event.preconsensus.permitGaps", permitGaps) + .getOrCreateConfig(); + + final Metrics metrics = new NoOpMetrics(); + + return new DefaultPlatformContext(configuration, metrics, CryptographyHolder.get()); + } + /** * Create a dummy file. * @@ -88,88 +110,72 @@ private void createDummyFile(final PreConsensusEventFile descriptor) throws IOEx out.close(); } - private PreconsensusEventMetrics buildMetrics() { - return new PreconsensusEventMetrics(new NoOpMetrics()); - } - @Test @DisplayName("Maximum Less Than Minimum Test") void maximumLessThanMinimumTest() throws IOException { - createDummyFile(PreConsensusEventFile.of(0, 0, 1, Instant.now(), testDirectory)); + createDummyFile(PreConsensusEventFile.of(0, 0, 1, Instant.now(), fileDirectory)); - createDummyFile(PreConsensusEventFile.of(1, 10, 5, Instant.now(), testDirectory)); + createDummyFile(PreConsensusEventFile.of(1, 10, 5, Instant.now(), fileDirectory)); - createDummyFile(PreConsensusEventFile.of(2, 10, 20, Instant.now(), testDirectory)); + createDummyFile(PreConsensusEventFile.of(2, 10, 20, Instant.now(), fileDirectory)); - final PreConsensusEventStreamConfig config = new TestConfigBuilder() - .withValue("event.preconsensus.databaseDirectory", testDirectory) - .getOrCreateConfig() - .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); assertThrows( IllegalStateException.class, - () -> new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics())); + () -> new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0)); } @Test @DisplayName("Minimum Decreases Test") void minimumDecreasesTest() throws IOException { - createDummyFile(PreConsensusEventFile.of(0, 5, 10, Instant.now(), testDirectory)); + createDummyFile(PreConsensusEventFile.of(0, 5, 10, Instant.now(), fileDirectory)); - createDummyFile(PreConsensusEventFile.of(1, 4, 11, Instant.now(), testDirectory)); + createDummyFile(PreConsensusEventFile.of(1, 4, 11, Instant.now(), fileDirectory)); - createDummyFile(PreConsensusEventFile.of(2, 10, 20, Instant.now(), testDirectory)); + createDummyFile(PreConsensusEventFile.of(2, 10, 20, Instant.now(), fileDirectory)); - final PreConsensusEventStreamConfig config = new TestConfigBuilder() - .withValue("event.preconsensus.databaseDirectory", testDirectory) - .getOrCreateConfig() - .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); assertThrows( IllegalStateException.class, - () -> new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics())); + () -> new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0)); } @Test @DisplayName("Maximum Decreases Test") void maximumDecreasesTest() throws IOException { - createDummyFile(PreConsensusEventFile.of(0, 5, 10, Instant.now(), testDirectory)); + createDummyFile(PreConsensusEventFile.of(0, 5, 10, Instant.now(), fileDirectory)); - createDummyFile(PreConsensusEventFile.of(1, 6, 9, Instant.now(), testDirectory)); + createDummyFile(PreConsensusEventFile.of(1, 6, 9, Instant.now(), fileDirectory)); - createDummyFile(PreConsensusEventFile.of(2, 10, 20, Instant.now(), testDirectory)); + createDummyFile(PreConsensusEventFile.of(2, 10, 20, Instant.now(), fileDirectory)); - final PreConsensusEventStreamConfig config = new TestConfigBuilder() - .withValue("event.preconsensus.databaseDirectory", testDirectory) - .getOrCreateConfig() - .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); assertThrows( IllegalStateException.class, - () -> new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics())); + () -> new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0)); } @Test @DisplayName("Timestamp Decreases Test") void timestampDecreasesTest() throws IOException { - createDummyFile(PreConsensusEventFile.of(0, 5, 10, Instant.ofEpochMilli(1000), testDirectory)); + createDummyFile(PreConsensusEventFile.of(0, 5, 10, Instant.ofEpochMilli(1000), fileDirectory)); - createDummyFile(PreConsensusEventFile.of(1, 6, 11, Instant.ofEpochMilli(500), testDirectory)); + createDummyFile(PreConsensusEventFile.of(1, 6, 11, Instant.ofEpochMilli(500), fileDirectory)); - createDummyFile(PreConsensusEventFile.of(2, 7, 12, Instant.ofEpochMilli(2000), testDirectory)); + createDummyFile(PreConsensusEventFile.of(2, 7, 12, Instant.ofEpochMilli(2000), fileDirectory)); - final PreConsensusEventStreamConfig config = new TestConfigBuilder() - .withValue("event.preconsensus.databaseDirectory", testDirectory) - .getOrCreateConfig() - .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); assertThrows( IllegalStateException.class, - () -> new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics())); + () -> new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0)); } @Test @@ -197,7 +203,7 @@ void readFilesInOrderTest() throws IOException { sequenceNumber++) { final PreConsensusEventFile file = PreConsensusEventFile.of( - sequenceNumber, minimumGeneration, maximumGeneration, timestamp, testDirectory); + sequenceNumber, minimumGeneration, maximumGeneration, timestamp, fileDirectory); minimumGeneration = random.nextLong(minimumGeneration, maximumGeneration + 1); maximumGeneration = @@ -208,13 +214,10 @@ void readFilesInOrderTest() throws IOException { createDummyFile(file); } - final PreConsensusEventStreamConfig config = new TestConfigBuilder() - .withValue("event.preconsensus.databaseDirectory", testDirectory) - .getOrCreateConfig() - .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); final PreConsensusEventFileManager manager = - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()); + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0); assertIteratorEquality( files.iterator(), manager.getFileIterator(PreConsensusEventFileManager.NO_MINIMUM_GENERATION, false)); @@ -256,7 +259,7 @@ void readFilesInOrderGapTest(final boolean permitGaps) throws IOException { sequenceNumber++) { final PreConsensusEventFile file = PreConsensusEventFile.of( - sequenceNumber, minimumGeneration, maximumGeneration, timestamp, testDirectory); + sequenceNumber, minimumGeneration, maximumGeneration, timestamp, fileDirectory); minimumGeneration = random.nextLong(minimumGeneration, maximumGeneration + 1); maximumGeneration = @@ -272,16 +275,12 @@ void readFilesInOrderGapTest(final boolean permitGaps) throws IOException { createDummyFile(file); } - final PreConsensusEventStreamConfig config = new TestConfigBuilder() - .withValue("event.preconsensus.databaseDirectory", testDirectory) - .withValue("event.preconsensus.permitGaps", permitGaps) - .getOrCreateConfig() - .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(permitGaps); if (permitGaps) { // Gaps are allowed. We should see all files except for the one that was skipped. final PreConsensusEventFileManager manager = - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()); + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0); assertIteratorEquality( files.iterator(), manager.getFileIterator(PreConsensusEventFileManager.NO_MINIMUM_GENERATION)); @@ -289,7 +288,7 @@ void readFilesInOrderGapTest(final boolean permitGaps) throws IOException { // Gaps are not allowed. assertThrows( IllegalStateException.class, - () -> new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics())); + () -> new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0)); } } @@ -317,7 +316,7 @@ void readFilesFromMiddleTest() throws IOException { sequenceNumber++) { final PreConsensusEventFile file = PreConsensusEventFile.of( - sequenceNumber, minimumGeneration, maximumGeneration, timestamp, testDirectory); + sequenceNumber, minimumGeneration, maximumGeneration, timestamp, fileDirectory); minimumGeneration = random.nextLong(minimumGeneration, maximumGeneration + 1); maximumGeneration = @@ -328,13 +327,10 @@ void readFilesFromMiddleTest() throws IOException { createDummyFile(file); } - final PreConsensusEventStreamConfig config = new TestConfigBuilder() - .withValue("event.preconsensus.databaseDirectory", testDirectory) - .getOrCreateConfig() - .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); final PreConsensusEventFileManager manager = - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()); + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0); // For this test, we want to iterate over files so that we are guaranteed to observe every event // with a generation greater than or equal to the target generation. Choose a generation that falls @@ -397,7 +393,7 @@ void readFilesFromMiddleRepeatingGenerationsTest() throws IOException { sequenceNumber++) { final PreConsensusEventFile file = PreConsensusEventFile.of( - sequenceNumber, minimumGeneration, maximumGeneration, timestamp, testDirectory); + sequenceNumber, minimumGeneration, maximumGeneration, timestamp, fileDirectory); // Advance the generation bounds only 10% of the time if (random.nextLong() < 0.1) { @@ -410,13 +406,10 @@ void readFilesFromMiddleRepeatingGenerationsTest() throws IOException { files.add(file); createDummyFile(file); } - final PreConsensusEventStreamConfig config = new TestConfigBuilder() - .withValue("event.preconsensus.databaseDirectory", testDirectory) - .getOrCreateConfig() - .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); final PreConsensusEventFileManager manager = - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()); + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0); // For this test, we want to iterate over files so that we are guaranteed to observe every event // with a generation greater than or equal to the target generation. Choose a generation that falls @@ -474,7 +467,7 @@ void readFilesFromHighGeneration() throws IOException { sequenceNumber++) { final PreConsensusEventFile file = PreConsensusEventFile.of( - sequenceNumber, minimumGeneration, maximumGeneration, timestamp, testDirectory); + sequenceNumber, minimumGeneration, maximumGeneration, timestamp, fileDirectory); minimumGeneration = random.nextLong(minimumGeneration, maximumGeneration + 1); maximumGeneration = @@ -485,13 +478,10 @@ void readFilesFromHighGeneration() throws IOException { createDummyFile(file); } - final PreConsensusEventStreamConfig config = new TestConfigBuilder() - .withValue("event.preconsensus.databaseDirectory", testDirectory) - .getOrCreateConfig() - .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); final PreConsensusEventFileManager manager = - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()); + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0); // Request a generation higher than all files in the data store final long targetGeneration = files.get(fileCount - 1).maximumGeneration() + 1; @@ -510,10 +500,7 @@ void generateDescriptorsWithManagerTest() throws IOException { final List files = new ArrayList<>(); - final PreConsensusEventStreamConfig config = new TestConfigBuilder() - .withValue("event.preconsensus.databaseDirectory", testDirectory) - .getOrCreateConfig() - .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); final long maxDelta = random.nextLong(10, 20); long minimumGeneration = random.nextLong(0, 1000); @@ -521,7 +508,7 @@ void generateDescriptorsWithManagerTest() throws IOException { Instant timestamp = Instant.now(); final PreConsensusEventFileManager generatingManager = - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()); + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0); for (int i = 0; i < fileCount; i++) { final PreConsensusEventFile file = @@ -540,7 +527,7 @@ void generateDescriptorsWithManagerTest() throws IOException { } final PreConsensusEventFileManager manager = - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()); + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0); assertIteratorEquality( files.iterator(), manager.getFileIterator(PreConsensusEventFileManager.NO_MINIMUM_GENERATION)); @@ -570,7 +557,7 @@ void incrementalPruningByGenerationTest() throws IOException { sequenceNumber++) { final PreConsensusEventFile file = PreConsensusEventFile.of( - sequenceNumber, minimumGeneration, maximumGeneration, timestamp, testDirectory); + sequenceNumber, minimumGeneration, maximumGeneration, timestamp, fileDirectory); minimumGeneration = random.nextLong(minimumGeneration, maximumGeneration + 1); maximumGeneration = @@ -581,11 +568,7 @@ void incrementalPruningByGenerationTest() throws IOException { createDummyFile(file); } - final PreConsensusEventStreamConfig config = new TestConfigBuilder() - .withValue("event.preconsensus.databaseDirectory", testDirectory) - .withValue("event.preconsensus.minimumRetentionPeriod", "1h") - .getOrCreateConfig() - .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); // Choose a file in the middle. The goal is to incrementally purge all files before this file, but not // to purge this file or any of the ones after it. @@ -598,7 +581,7 @@ void incrementalPruningByGenerationTest() throws IOException { // Set the far in the future, we want all files to be GC eligible by temporal reckoning. final FakeTime time = new FakeTime(lastFile.timestamp().plus(Duration.ofHours(1)), Duration.ZERO); - final PreConsensusEventFileManager manager = new PreConsensusEventFileManager(time, config, buildMetrics()); + final PreConsensusEventFileManager manager = new PreConsensusEventFileManager(platformContext, time, 0); assertIteratorEquality( files.iterator(), manager.getFileIterator(PreConsensusEventFileManager.NO_MINIMUM_GENERATION)); @@ -614,7 +597,7 @@ void incrementalPruningByGenerationTest() throws IOException { // Parse files with a new manager to make sure we aren't "cheating" by just // removing the in-memory descriptor without also removing the file on disk final List parsedFiles = new ArrayList<>(); - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()) + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0) .getFileIterator(PreConsensusEventFileManager.NO_MINIMUM_GENERATION) .forEachRemaining(parsedFiles::add); @@ -654,7 +637,7 @@ void incrementalPruningByGenerationTest() throws IOException { // Parse files with a new manager to make sure we aren't "cheating" by just // removing the in-memory descriptor without also removing the file on disk final List parsedFiles = new ArrayList<>(); - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()) + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0) .getFileIterator(PreConsensusEventFileManager.NO_MINIMUM_GENERATION) .forEachRemaining(parsedFiles::add); @@ -695,7 +678,7 @@ void incrementalPruningByTimestampTest() throws IOException { sequenceNumber++) { final PreConsensusEventFile file = PreConsensusEventFile.of( - sequenceNumber, minimumGeneration, maximumGeneration, timestamp, testDirectory); + sequenceNumber, minimumGeneration, maximumGeneration, timestamp, fileDirectory); minimumGeneration = random.nextLong(minimumGeneration, maximumGeneration + 1); maximumGeneration = @@ -706,11 +689,7 @@ void incrementalPruningByTimestampTest() throws IOException { createDummyFile(file); } - final PreConsensusEventStreamConfig config = new TestConfigBuilder() - .withValue("event.preconsensus.databaseDirectory", testDirectory) - .withValue("event.preconsensus.minimumRetentionPeriod", "1h") - .getOrCreateConfig() - .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); // Choose a file in the middle. The goal is to incrementally purge all files before this file, but not // to purge this file or any of the ones after it. @@ -723,7 +702,7 @@ void incrementalPruningByTimestampTest() throws IOException { // Set the clock before the first file is not garbage collection eligible final FakeTime time = new FakeTime(firstFile.timestamp().plus(Duration.ofMinutes(59)), Duration.ZERO); - final PreConsensusEventFileManager manager = new PreConsensusEventFileManager(time, config, buildMetrics()); + final PreConsensusEventFileManager manager = new PreConsensusEventFileManager(platformContext, time, 0); assertIteratorEquality( files.iterator(), manager.getFileIterator(PreConsensusEventFileManager.NO_MINIMUM_GENERATION)); @@ -738,7 +717,7 @@ void incrementalPruningByTimestampTest() throws IOException { // Parse files with a new manager to make sure we aren't "cheating" by just // removing the in-memory descriptor without also removing the file on disk final List parsedFiles = new ArrayList<>(); - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()) + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0) .getFileIterator(PreConsensusEventFileManager.NO_MINIMUM_GENERATION) .forEachRemaining(parsedFiles::add); @@ -783,7 +762,7 @@ void incrementalPruningByTimestampTest() throws IOException { // Parse files with a new manager to make sure we aren't "cheating" by just // removing the in-memory descriptor without also removing the file on disk final List parsedFiles = new ArrayList<>(); - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()) + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0) .getFileIterator(PreConsensusEventFileManager.NO_MINIMUM_GENERATION) .forEachRemaining(parsedFiles::add); diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/preconsensus/SyncPreConsensusEventWriterTests.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/preconsensus/SyncPreConsensusEventWriterTests.java index 2353fc49aa9d..bbfa45e0cf63 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/preconsensus/SyncPreConsensusEventWriterTests.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/preconsensus/SyncPreConsensusEventWriterTests.java @@ -17,17 +17,22 @@ package com.swirlds.platform.test.event.preconsensus; import static com.swirlds.platform.test.event.preconsensus.AsyncPreConsensusEventWriterTests.buildGraphGenerator; -import static com.swirlds.platform.test.event.preconsensus.AsyncPreConsensusEventWriterTests.buildMetrics; import static com.swirlds.platform.test.event.preconsensus.AsyncPreConsensusEventWriterTests.verifyStream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import com.swirlds.common.constructable.ConstructableRegistry; import com.swirlds.common.constructable.ConstructableRegistryException; +import com.swirlds.common.context.PlatformContext; +import com.swirlds.common.context.internal.DefaultPlatformContext; +import com.swirlds.common.crypto.CryptographyHolder; import com.swirlds.common.internal.SettingsCommon; import com.swirlds.common.io.utility.FileUtils; +import com.swirlds.common.metrics.Metrics; import com.swirlds.common.test.RandomUtils; +import com.swirlds.common.test.metrics.NoOpMetrics; import com.swirlds.common.time.OSTime; +import com.swirlds.config.api.Configuration; import com.swirlds.platform.event.preconsensus.PreConsensusEventFile; import com.swirlds.platform.event.preconsensus.PreConsensusEventFileManager; import com.swirlds.platform.event.preconsensus.PreConsensusEventStreamConfig; @@ -40,6 +45,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -85,6 +91,17 @@ void afterEach() throws IOException { FileUtils.deleteDirectory(testDirectory); } + private PlatformContext buildContext() { + final Configuration configuration = new TestConfigBuilder() + .withValue("event.preconsensus.databaseDirectory", testDirectory) + .withValue("event.preconsensus.preferredFileSizeMegabytes", 5) + .getOrCreateConfig(); + + final Metrics metrics = new NoOpMetrics(); + + return new DefaultPlatformContext(configuration, metrics, CryptographyHolder.get()); + } + @Test @DisplayName("Standard Operation Test") void standardOperationTest() throws IOException, InterruptedException { @@ -106,11 +123,13 @@ void standardOperationTest() throws IOException, InterruptedException { .getOrCreateConfig() .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); + final PreConsensusEventFileManager fileManager = - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()); + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0); final PreconsensusEventStreamSequencer sequencer = new PreconsensusEventStreamSequencer(); - final PreConsensusEventWriter writer = new SyncPreConsensusEventWriter(config, fileManager); + final PreConsensusEventWriter writer = new SyncPreConsensusEventWriter(platformContext, fileManager); writer.start(); @@ -127,10 +146,10 @@ void standardOperationTest() throws IOException, InterruptedException { writer.requestFlush(events.get(events.size() - 1)); - // Since we are not using threads, the stream should be flushed when we reach this point + assertTrue(writer.waitUntilDurable(events.get(events.size() - 1), Duration.ofSeconds(1))); assertTrue(writer.isEventDurable(events.get(events.size() - 1))); - verifyStream(events, config, 0); + verifyStream(events, platformContext, 0); writer.stop(); } @@ -165,10 +184,12 @@ void multipleFlushesTest() throws IOException, InterruptedException { .getOrCreateConfig() .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); + final PreConsensusEventFileManager fileManager = - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()); + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0); - final PreConsensusEventWriter writer = new SyncPreConsensusEventWriter(config, fileManager); + final PreConsensusEventWriter writer = new SyncPreConsensusEventWriter(platformContext, fileManager); for (final int flushIndex : flushIndexes) { writer.requestFlush(events.get(flushIndex)); @@ -189,9 +210,10 @@ void multipleFlushesTest() throws IOException, InterruptedException { } writer.requestFlush(events.get(events.size() - 1)); + assertTrue(writer.waitUntilDurable(events.get(events.size() - 1), Duration.ofSeconds(1))); assertTrue(writer.isEventDurable(events.get(events.size() - 1))); - verifyStream(events, config, 0); + verifyStream(events, platformContext, 0); writer.stop(); } @@ -228,10 +250,12 @@ void outOfOrderFlushesTest() throws IOException, InterruptedException { .getOrCreateConfig() .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); + final PreConsensusEventFileManager fileManager = - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()); + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0); - final PreConsensusEventWriter writer = new SyncPreConsensusEventWriter(config, fileManager); + final PreConsensusEventWriter writer = new SyncPreConsensusEventWriter(platformContext, fileManager); for (final int flushIndex : flushIndexes) { writer.requestFlush(events.get(flushIndex)); @@ -252,9 +276,10 @@ void outOfOrderFlushesTest() throws IOException, InterruptedException { } writer.requestFlush(events.get(events.size() - 1)); + assertTrue(writer.waitUntilDurable(events.get(events.size() - 1), Duration.ofSeconds(1))); assertTrue(writer.isEventDurable(events.get(events.size() - 1))); - verifyStream(events, config, 0); + verifyStream(events, platformContext, 0); writer.stop(); } @@ -280,11 +305,13 @@ void stopFlushesEventsTest() throws IOException, InterruptedException { .getOrCreateConfig() .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); + final PreConsensusEventFileManager fileManager = - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()); + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0); final PreconsensusEventStreamSequencer sequencer = new PreconsensusEventStreamSequencer(); - final PreConsensusEventWriter writer = new SyncPreConsensusEventWriter(config, fileManager); + final PreConsensusEventWriter writer = new SyncPreConsensusEventWriter(platformContext, fileManager); writer.start(); @@ -304,7 +331,7 @@ void stopFlushesEventsTest() throws IOException, InterruptedException { // Since we are not using threads, the stream should be flushed when we reach this point assertTrue(writer.isEventDurable(events.get(events.size() - 1))); - verifyStream(events, config, 0); + verifyStream(events, platformContext, 0); } @Test @@ -331,11 +358,13 @@ void ancientEventTest() throws IOException, InterruptedException { .getOrCreateConfig() .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); + final PreConsensusEventFileManager fileManager = - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()); + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0); final PreconsensusEventStreamSequencer sequencer = new PreconsensusEventStreamSequencer(); - final PreConsensusEventWriter writer = new SyncPreConsensusEventWriter(config, fileManager); + final PreConsensusEventWriter writer = new SyncPreConsensusEventWriter(platformContext, fileManager); writer.start(); @@ -366,10 +395,10 @@ void ancientEventTest() throws IOException, InterruptedException { writer.requestFlush(events.get(events.size() - 1)); - // Since we are not using threads, the stream should be flushed when we reach this point + assertTrue(writer.waitUntilDurable(events.get(events.size() - 1), Duration.ofSeconds(1))); assertTrue(writer.isEventDurable(events.get(events.size() - 1))); - verifyStream(events, config, 0); + verifyStream(events, platformContext, 0); writer.stop(); } @@ -398,11 +427,13 @@ void overflowTest() throws IOException, InterruptedException { .getOrCreateConfig() .getConfigData(PreConsensusEventStreamConfig.class); + final PlatformContext platformContext = buildContext(); + final PreConsensusEventFileManager fileManager = - new PreConsensusEventFileManager(OSTime.getInstance(), config, buildMetrics()); + new PreConsensusEventFileManager(platformContext, OSTime.getInstance(), 0); final PreconsensusEventStreamSequencer sequencer = new PreconsensusEventStreamSequencer(); - final PreConsensusEventWriter writer = new SyncPreConsensusEventWriter(config, fileManager); + final PreConsensusEventWriter writer = new SyncPreConsensusEventWriter(platformContext, fileManager); writer.start(); @@ -413,7 +444,7 @@ void overflowTest() throws IOException, InterruptedException { writer.stop(); - verifyStream(events, config, 0); + verifyStream(events, platformContext, 0); // Without advancing the first non-ancient generation, // we should never be able to increase the minimum generation from 0. diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/eventflow/EventFlowTests.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/eventflow/EventFlowTests.java index 64f55bb8a8f5..96263a841598 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/eventflow/EventFlowTests.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/eventflow/EventFlowTests.java @@ -628,6 +628,7 @@ protected void init( consStats, eventStreamManager, signedStateTracker, + e -> {}, () -> {}, (round) -> {}, SoftwareVersion.NO_VERSION); diff --git a/platform-sdk/swirlds-virtualmap/build.gradle.kts b/platform-sdk/swirlds-virtualmap/build.gradle.kts index e1758343d30c..d184a04dea29 100644 --- a/platform-sdk/swirlds-virtualmap/build.gradle.kts +++ b/platform-sdk/swirlds-virtualmap/build.gradle.kts @@ -24,6 +24,7 @@ plugins { dependencies { // Individual Dependencies api(project(":swirlds-common")) + api(project(":swirlds-base")) compileOnly(libs.spotbugs.annotations) // Test Dependencies diff --git a/platform-sdk/swirlds-virtualmap/src/main/java/module-info.java b/platform-sdk/swirlds-virtualmap/src/main/java/module-info.java index e36f2ab539ea..3c649f6a3890 100644 --- a/platform-sdk/swirlds-virtualmap/src/main/java/module-info.java +++ b/platform-sdk/swirlds-virtualmap/src/main/java/module-info.java @@ -9,6 +9,7 @@ exports com.swirlds.virtualmap.config; requires com.swirlds.common; + requires com.swirlds.base; requires com.swirlds.logging; requires org.apache.logging.log4j; requires java.sql;