diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/address/AddressBookInitializer.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/address/AddressBookInitializer.java index f7fbd82fe34c..e2643f2627b7 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/address/AddressBookInitializer.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/address/AddressBookInitializer.java @@ -98,8 +98,8 @@ public class AddressBookInitializer { private final int maxNumFiles; /** Indicate that the unmodified config address book must be used. */ private final boolean useConfigAddressBook; - /** Indicates that the address book has changed */ - private boolean addressBookChanged = false; + /** Indicates that one of the address books has changed */ + private boolean addressBookChange = false; /** * Constructs an AddressBookInitializer to initialize an address book from config.txt, the saved state from disk, or @@ -127,7 +127,12 @@ public AddressBookInitializer( final AddressBookConfig addressBookConfig = platformContext.getConfiguration().getConfigData(AddressBookConfig.class); this.initialState = Objects.requireNonNull(initialState, "The initialState must not be null."); - this.stateAddressBook = initialState.isGenesisState() ? null : initialState.getAddressBook(); + + this.stateAddressBook = initialState.getState().getPlatformState().getAddressBook(); + if (stateAddressBook == null && !initialState.isGenesisState()) { + throw new IllegalStateException("Only genesis states can have null address books."); + } + this.pathToAddressBookDirectory = Path.of(addressBookConfig.addressBookDirectory()); try { Files.createDirectories(pathToAddressBookDirectory); @@ -172,7 +177,7 @@ public AddressBook getPreviousAddressBook() { * @return true if the address book has changed on initialization */ public boolean hasAddressBookChanged() { - return addressBookChanged; + return addressBookChange; } /** @@ -195,13 +200,11 @@ private InitializedAddressBooks initialize() { "Overriding the address book in the state with the address book from config.txt"); candidateAddressBook = configAddressBook; previousAddressBook = stateAddressBook; - addressBookChanged = !Objects.equals(configAddressBook, stateAddressBook); } else if (initialState.isGenesisState()) { // If this is a genesis state, the state's address book and previous address book will be null. // Adopt the config.txt address book. logger.info(STARTUP.getMarker(), "Starting from genesis: using the config address book."); candidateAddressBook = configAddressBook; - addressBookChanged = true; checkCandidateAddressBookValidity(candidateAddressBook); previousAddressBook = null; } else if (!softwareUpgrade) { @@ -210,7 +213,6 @@ private InitializedAddressBooks initialize() { candidateAddressBook = stateAddressBook; // since state address book was checked for validity prior to adoption, no check needed here. previousAddressBook = null; - addressBookChanged = false; } else { // Loaded State from Disk, Non-Genesis, There is a software version upgrade logger.info( @@ -222,12 +224,21 @@ private InitializedAddressBooks initialize() { .copy(); candidateAddressBook = checkCandidateAddressBookValidity(candidateAddressBook); previousAddressBook = stateAddressBook; - addressBookChanged = !Objects.equals(configAddressBook, stateAddressBook); } + // This flag indicates the state needs to be updated with the changes to the address books. + addressBookChange = hasAddressBookChanged(initialState, candidateAddressBook, previousAddressBook); recordAddressBooks(candidateAddressBook); return new InitializedAddressBooks(candidateAddressBook, previousAddressBook); } + private boolean hasAddressBookChanged( + @NonNull final SignedState state, + @Nullable final AddressBook currentAddressBook, + @Nullable final AddressBook previousAddressBook) { + return !Objects.equals(state.getState().getPlatformState().getAddressBook(), currentAddressBook) + || !Objects.equals(state.getState().getPlatformState().getPreviousAddressBook(), previousAddressBook); + } + /** * Checks if the candidateAddressBook is valid and returns it, otherwise returns the configAddressBook if it has * non-zero weight. If the candidateAddressBook's addresses are out of sync with the configAddressBook or both 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 2fd4f078025e..9df5259872c2 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 @@ -23,6 +23,7 @@ import static com.swirlds.platform.state.address.AddressBookInitializer.STATE_ADDRESS_BOOK_USED; import static com.swirlds.platform.state.address.AddressBookInitializer.USED_ADDRESS_BOOK_HEADER; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -46,6 +47,7 @@ import com.swirlds.test.framework.config.TestConfigBuilder; import com.swirlds.test.framework.context.TestPlatformContextBuilder; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -89,6 +91,7 @@ void forceUseOfConfigAddressBook() throws IOException { "The previous address book must equal the state address book."); assertAddressBookFileContent( initializer, configAddressBook, signedState.getAddressBook(), inititializedAddressBook); + assertTrue(initializer.hasAddressBookChanged()); } @Test @@ -96,7 +99,8 @@ void forceUseOfConfigAddressBook() throws IOException { void noStateLoadedFromDisk() throws IOException { clearTestDirectory(); final AddressBook configAddressBook = getRandomAddressBook(); - final SignedState signedState = getMockSignedState(10, configAddressBook, true); + // initial state has no address books set. + final SignedState signedState = getMockSignedState(10, null, null, true); final AddressBookInitializer initializer = new AddressBookInitializer( new NodeId(0), getMockSoftwareVersion(2), @@ -113,6 +117,7 @@ void noStateLoadedFromDisk() throws IOException { assertNull(initializer.getPreviousAddressBook(), "The previous address book should be null."); assertAddressBookFileContent( initializer, configAddressBook, signedState.getAddressBook(), inititializedAddressBook); + assertTrue(initializer.hasAddressBookChanged()); } @Test @@ -120,7 +125,8 @@ void noStateLoadedFromDisk() throws IOException { void noStateLoadedFromDiskGenesisStateSetZeroWeight() throws IOException { clearTestDirectory(); final AddressBook configAddressBook = getRandomAddressBook(); - final SignedState signedState = getMockSignedState(10, configAddressBook, true); + // initial state has currentAddressBook set to configAddressBook + final SignedState signedState = getMockSignedState(10, configAddressBook, null, true); final AddressBookInitializer initializer = new AddressBookInitializer( new NodeId(0), getMockSoftwareVersion(2), @@ -137,6 +143,7 @@ void noStateLoadedFromDiskGenesisStateSetZeroWeight() throws IOException { assertNull(initializer.getPreviousAddressBook(), "The previous address book should be null."); assertAddressBookFileContent( initializer, configAddressBook, signedState.getAddressBook(), inititializedAddressBook); + assertFalse(initializer.hasAddressBookChanged()); } @Test @@ -144,7 +151,8 @@ void noStateLoadedFromDiskGenesisStateSetZeroWeight() throws IOException { void noStateLoadedFromDiskGenesisStateChangedAddressBook() throws IOException { clearTestDirectory(); final AddressBook configAddressBook = getRandomAddressBook(); - final SignedState signedState = getMockSignedState(7, configAddressBook, true); + // initial state has currentAddressBook set to configAddressBook + final SignedState signedState = getMockSignedState(7, configAddressBook, null, true); final AddressBookInitializer initializer = new AddressBookInitializer( new NodeId(0), getMockSoftwareVersion(2), @@ -161,13 +169,15 @@ void noStateLoadedFromDiskGenesisStateChangedAddressBook() throws IOException { assertNull(initializer.getPreviousAddressBook(), "The previous address book should be null."); assertAddressBookFileContent( initializer, configAddressBook, signedState.getAddressBook(), inititializedAddressBook); + assertFalse(initializer.hasAddressBookChanged()); } @Test @DisplayName("Current software version is equal to state software version.") void currentVersionEqualsStateVersion() throws IOException { clearTestDirectory(); - final SignedState signedState = getMockSignedState(2, getRandomAddressBook(), false); + // start state with previous address book + final SignedState signedState = getMockSignedState(2, getRandomAddressBook(), getRandomAddressBook(), false); final AddressBook configAddressBook = copyWithWeightChanges(signedState.getAddressBook(), 10); final AddressBookInitializer initializer = new AddressBookInitializer( new NodeId(0), @@ -188,13 +198,15 @@ void currentVersionEqualsStateVersion() throws IOException { "When there is no upgrade, the address book should not change"); assertAddressBookFileContent( initializer, configAddressBook, signedState.getAddressBook(), inititializedAddressBook); + // Initializer nullifies the previous address book when there is no software upgrade. + assertTrue(initializer.hasAddressBookChanged()); } @Test @DisplayName("Version upgrade, SwirldState set 0 weight.") void versionUpgradeSwirldStateZeroWeight() throws IOException { clearTestDirectory(); - final SignedState signedState = getMockSignedState(0, getRandomAddressBook(), false); + final SignedState signedState = getMockSignedState(0, getRandomAddressBook(), getRandomAddressBook(), false); final AddressBook configAddressBook = copyWithWeightChanges(signedState.getAddressBook(), 10); final AddressBookInitializer initializer = new AddressBookInitializer( new NodeId(0), @@ -215,13 +227,14 @@ void versionUpgradeSwirldStateZeroWeight() throws IOException { "The previous address book must equal the state address book."); assertAddressBookFileContent( initializer, configAddressBook, signedState.getAddressBook(), inititializedAddressBook); + assertTrue(initializer.hasAddressBookChanged()); } @Test @DisplayName("Version upgrade, Swirld State modified the address book.") void versionUpgradeSwirldStateModifiedAddressBook() throws IOException { clearTestDirectory(); - final SignedState signedState = getMockSignedState(2, getRandomAddressBook(), false); + final SignedState signedState = getMockSignedState(2, getRandomAddressBook(), getRandomAddressBook(), false); final AddressBook configAddressBook = copyWithWeightChanges(signedState.getAddressBook(), 3); final AddressBookInitializer initializer = new AddressBookInitializer( new NodeId(0), @@ -242,6 +255,7 @@ void versionUpgradeSwirldStateModifiedAddressBook() throws IOException { "The previous address book must equal the state address book."); assertAddressBookFileContent( initializer, configAddressBook, signedState.getAddressBook(), inititializedAddressBook); + assertTrue(initializer.hasAddressBookChanged()); } @Test @@ -273,6 +287,7 @@ void versionUpgradeSwirldStateWeightUpdateWorks() throws IOException { "The previous address book must equal the state address book."); assertAddressBookFileContent( initializer, configAddressBook, signedState.getAddressBook(), inititializedAddressBook); + assertTrue(initializer.hasAddressBookChanged()); } /** @@ -321,35 +336,39 @@ private SoftwareVersion getMockSoftwareVersion(int version) { * @return The mock SignedState. */ private SignedState getMockSignedState7WeightRandomAddressBook() { - return getMockSignedState(7, getRandomAddressBook(), false); + return getMockSignedState(7, getRandomAddressBook(), getRandomAddressBook(), false); } /** * Creates a mock signed state and a SwirldState that sets all addresses to the given weightValue. * - * @param weightValue The weight value that the SwirldState should set all addresses to in its updateWeight - * method. - * @param stateAddressBook The address book that the SignedState should return in its getAddressBook method. - * @param fromGenesis Whether the state should be from genesis or not. + * @param weightValue The weight value that the SwirldState should set all addresses to in its updateWeight + * method. + * @param currentAddressBook The address book that should be returned by {@link SignedState#getAddressBook()} and + * {@link PlatformState#getAddressBook()} + * @param previousAddressBook The address book that should be returned by + * {@link PlatformState#getPreviousAddressBook()} + * @param fromGenesis Whether the state should be from genesis or not. * @return The mock SignedState and SwirldState configured to set all addresses with given weightValue. */ private SignedState getMockSignedState( - final int weightValue, @NonNull final AddressBook stateAddressBook, boolean fromGenesis) { + final int weightValue, + @Nullable final AddressBook currentAddressBook, + @Nullable final AddressBook previousAddressBook, + boolean fromGenesis) { final SignedState signedState = mock(SignedState.class); final SoftwareVersion softwareVersion = getMockSoftwareVersion(2); final SwirldState swirldState = getMockSwirldStateSupplier(weightValue).get(); - if (fromGenesis) { - when(signedState.getAddressBook()).thenReturn(null); - } else { - when(signedState.getAddressBook()).thenReturn(stateAddressBook); - } when(signedState.getSwirldState()).thenReturn(swirldState); final PlatformState platformState = mock(PlatformState.class); when(platformState.getCreationSoftwareVersion()).thenReturn(softwareVersion); + when(platformState.getAddressBook()).thenReturn(currentAddressBook); + when(platformState.getPreviousAddressBook()).thenReturn(previousAddressBook); final State state = mock(State.class); when(state.getPlatformState()).thenReturn(platformState); when(signedState.getState()).thenReturn(state); when(signedState.isGenesisState()).thenReturn(fromGenesis); + when(signedState.getAddressBook()).thenReturn(currentAddressBook); return signedState; } diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/orphan/OrphanBufferTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/orphan/OrphanBufferTests.java index 6d4d50ab65ad..7c1a8a23bfcb 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/orphan/OrphanBufferTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/orphan/OrphanBufferTests.java @@ -167,6 +167,10 @@ private static GossipEvent createGossipEvent( .thenReturn(new EventDescriptor(eventHash, eventCreator, eventGeneration, eventBirthRound)); when(event.getGeneration()).thenReturn(eventGeneration); when(event.getSenderId()).thenReturn(eventCreator); + when(event.getAncientIndicator(any())) + .thenAnswer(args -> args.getArguments()[0] == AncientMode.BIRTH_ROUND_THRESHOLD + ? eventBirthRound + : eventGeneration); return event; }