diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java index 337168e3a98f..190414996d52 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/Hedera.java @@ -38,12 +38,12 @@ import static com.hedera.node.app.workflows.handle.metric.UnavailableMetrics.UNAVAILABLE_METRICS; import static com.hedera.node.config.types.StreamMode.BLOCKS; import static com.hedera.node.config.types.StreamMode.RECORDS; -import static com.swirlds.platform.roster.RosterRetriever.buildRoster; import static com.swirlds.platform.state.service.PlatformStateService.PLATFORM_STATE_SERVICE; import static com.swirlds.platform.state.service.schemas.V0540PlatformStateSchema.PLATFORM_STATE_KEY; import static com.swirlds.platform.system.InitTrigger.EVENT_STREAM_RECOVERY; import static com.swirlds.platform.system.InitTrigger.GENESIS; import static com.swirlds.platform.system.InitTrigger.RECONNECT; +import static com.swirlds.platform.system.address.AddressBookUtils.createRoster; import static com.swirlds.platform.system.status.PlatformStatus.ACTIVE; import static com.swirlds.platform.system.status.PlatformStatus.STARTING_UP; import static java.nio.charset.StandardCharsets.UTF_8; @@ -607,7 +607,7 @@ private List onMigrate( if (trigger == GENESIS) { final var config = configProvider.getConfiguration(); final var ledgerConfig = config.getConfigData(LedgerConfig.class); - final var genesisRoster = buildRoster(requireNonNull(genesisAddressBook)); + final var genesisRoster = createRoster(requireNonNull(genesisAddressBook)); genesisNetworkInfo = new GenesisNetworkInfo(genesisRoster, ledgerConfig.id()); } @@ -968,7 +968,7 @@ private void initializeDagger(@NonNull final State state, @NonNull final InitTri serviceMigrator, version, configProvider.getConfiguration(), - buildRoster(platform.getAddressBook())); + createRoster(platform.getAddressBook())); final var networkInfo = new StateNetworkInfo(state, activeRoster, platform.getSelfId().id(), configProvider); // Fully qualified so as to not confuse javadoc diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/ServicesMain.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/ServicesMain.java index 0fb5e264d716..bd24e87cde7c 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/ServicesMain.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/ServicesMain.java @@ -26,11 +26,11 @@ import static com.swirlds.platform.builder.internal.StaticPlatformBuilder.setupGlobalMetrics; import static com.swirlds.platform.config.internal.PlatformConfigUtils.checkConfiguration; import static com.swirlds.platform.crypto.CryptoStatic.initNodeSecurity; -import static com.swirlds.platform.roster.RosterRetriever.buildRoster; import static com.swirlds.platform.state.signed.StartupStateUtils.getInitialState; import static com.swirlds.platform.system.SystemExitCode.CONFIGURATION_ERROR; import static com.swirlds.platform.system.SystemExitCode.NODE_ADDRESS_MISMATCH; import static com.swirlds.platform.system.SystemExitUtils.exitSystem; +import static com.swirlds.platform.system.address.AddressBookUtils.createRoster; import static com.swirlds.platform.system.address.AddressBookUtils.initializeAddressBook; import static com.swirlds.platform.util.BootstrapUtils.checkNodesToRun; import static com.swirlds.platform.util.BootstrapUtils.getNodesToRun; @@ -287,7 +287,7 @@ public static void main(final String... args) throws Exception { final var addressBook = initializeAddressBook(selfId, version, initialState, diskAddressBook, platformContext); // Follow the Inversion of Control pattern by injecting all needed dependencies into the PlatformBuilder. - final var roster = buildRoster(addressBook); + final var roster = createRoster(addressBook); final var platformBuilder = PlatformBuilder.create( Hedera.APP_NAME, Hedera.SWIRLD_NAME, diff --git a/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/AppTestBase.java b/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/AppTestBase.java index 684d91489136..8177f3424690 100644 --- a/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/AppTestBase.java +++ b/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/AppTestBase.java @@ -16,7 +16,7 @@ package com.hedera.node.app.fixtures; -import static com.swirlds.platform.roster.RosterRetriever.buildRoster; +import static com.swirlds.platform.system.address.AddressBookUtils.createRoster; import static com.swirlds.platform.system.address.AddressBookUtils.endpointFor; import static com.swirlds.platform.test.fixtures.state.TestSchema.CURRENT_VERSION; import static java.util.Objects.requireNonNull; @@ -337,7 +337,7 @@ public App build() { final var addressBook = new AddressBook(addresses); final var platform = new FakePlatform(realSelfNodeInfo.nodeId(), addressBook); final var initialState = new FakeState(); - final var networkInfo = new GenesisNetworkInfo(buildRoster(addressBook), Bytes.fromHex("03")); + final var networkInfo = new GenesisNetworkInfo(createRoster(addressBook), Bytes.fromHex("03")); services.forEach(svc -> { final var reg = new FakeSchemaRegistry(); svc.registerSchemas(reg); diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/hedera/embedded/AbstractEmbeddedHedera.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/hedera/embedded/AbstractEmbeddedHedera.java index 3f367781b9aa..43b6b93db8fd 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/hedera/embedded/AbstractEmbeddedHedera.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/hedera/embedded/AbstractEmbeddedHedera.java @@ -19,10 +19,10 @@ import static com.hedera.hapi.util.HapiUtils.parseAccount; import static com.hedera.node.app.hapi.utils.CommonPbjConverters.fromPbj; import static com.hedera.services.bdd.junit.hedera.ExternalPath.ADDRESS_BOOK; -import static com.swirlds.platform.roster.RosterRetriever.buildRoster; import static com.swirlds.platform.state.service.PbjConverter.toPbjAddressBook; import static com.swirlds.platform.state.service.schemas.V0540PlatformStateSchema.PLATFORM_STATE_KEY; import static com.swirlds.platform.system.InitTrigger.GENESIS; +import static com.swirlds.platform.system.address.AddressBookUtils.createRoster; import static com.swirlds.platform.system.status.PlatformStatus.ACTIVE; import static com.swirlds.platform.system.status.PlatformStatus.FREEZE_COMPLETE; import static java.util.Objects.requireNonNull; @@ -56,6 +56,7 @@ import com.swirlds.config.api.ConfigurationBuilder; import com.swirlds.platform.config.legacy.LegacyConfigPropertiesLoader; import com.swirlds.platform.listeners.PlatformStatusChangeNotification; +import com.swirlds.platform.roster.RosterRetriever; import com.swirlds.platform.state.service.PlatformStateService; import com.swirlds.platform.state.service.WritableRosterStore; import com.swirlds.platform.system.SoftwareVersion; @@ -118,7 +119,7 @@ public abstract class AbstractEmbeddedHedera implements EmbeddedHedera { protected AbstractEmbeddedHedera(@NonNull final EmbeddedNode node) { requireNonNull(node); addressBook = loadAddressBook(node.getExternalPath(ADDRESS_BOOK)); - roster = buildRoster(addressBook); + roster = RosterRetriever.buildRoster(addressBook); nodeIds = stream(spliteratorUnknownSize(addressBook.iterator(), 0), false) .collect(toMap(AbstractEmbeddedHedera::accountIdOf, Address::getNodeId)); accountIds = stream(spliteratorUnknownSize(addressBook.iterator(), 0), false) @@ -155,7 +156,7 @@ public void start() { .copyBuilder() .addressBook(toPbjAddressBook(addressBook)) .build()); - writableRosterStore.putActiveRoster(buildRoster(addressBook), 0); + writableRosterStore.putActiveRoster(createRoster(addressBook), 0); ((CommittableWritableStates) writableStates).commit(); ((CommittableWritableStates) writableRosterStates).commit(); diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/system/address/AddressBookUtils.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/system/address/AddressBookUtils.java index 0a493c468943..3a33e3ee3773 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/system/address/AddressBookUtils.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/system/address/AddressBookUtils.java @@ -19,6 +19,7 @@ import static com.swirlds.platform.util.BootstrapUtils.detectSoftwareUpgrade; import com.hedera.hapi.node.base.ServiceEndpoint; +import com.hedera.hapi.node.state.roster.Roster; import com.hedera.hapi.node.state.roster.RosterEntry; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.common.context.PlatformContext; @@ -32,7 +33,10 @@ import com.swirlds.platform.system.SoftwareVersion; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; +import java.security.cert.CertificateEncodingException; import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.regex.Pattern; @@ -271,6 +275,60 @@ public static ServiceEndpoint endpointFor(@NonNull final String host, final int return builder.build(); } + /** + * Creates a new roster from the bootstrap address book. + * + * @return a new roster + */ + @NonNull + public static Roster createRoster(@NonNull final AddressBook bootstrapAddressBook) { + Objects.requireNonNull(bootstrapAddressBook, "The bootstrapAddressBook must not be null."); + final List rosterEntries = new ArrayList<>(bootstrapAddressBook.getSize()); + for (int i = 0; i < bootstrapAddressBook.getSize(); i++) { + final NodeId nodeId = bootstrapAddressBook.getNodeId(i); + final Address address = bootstrapAddressBook.getAddress(nodeId); + + final RosterEntry rosterEntry = AddressBookUtils.toRosterEntry(address, nodeId); + rosterEntries.add(rosterEntry); + } + return Roster.newBuilder().rosterEntries(rosterEntries).build(); + } + + /** + * Converts an address to a roster entry. + * + * @param address the address to convert + * @param nodeId the node ID to use for the roster entry + * @return the roster entry + */ + private static RosterEntry toRosterEntry(@NonNull final Address address, @NonNull final NodeId nodeId) { + Objects.requireNonNull(address); + Objects.requireNonNull(nodeId); + final var signingCertificate = address.getSigCert(); + Bytes signingCertificateBytes; + try { + signingCertificateBytes = + signingCertificate == null ? Bytes.EMPTY : Bytes.wrap(signingCertificate.getEncoded()); + } catch (final CertificateEncodingException e) { + signingCertificateBytes = Bytes.EMPTY; + } + + final List serviceEndpoints = new ArrayList<>(2); + if (address.getHostnameInternal() != null) { + serviceEndpoints.add(endpointFor(address.getHostnameInternal(), address.getPortInternal())); + } + if (address.getHostnameExternal() != null) { + serviceEndpoints.add(endpointFor(address.getHostnameExternal(), address.getPortExternal())); + } + + return RosterEntry.newBuilder() + .nodeId(nodeId.id()) + .weight(address.getWeight()) + .gossipCaCertificate(signingCertificateBytes) + .gossipEndpoint(serviceEndpoints) + .build(); + } + /** * Initializes the address book from the configuration and platform saved state. * diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/util/AddressBookNetworkUtilsTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/util/AddressBookNetworkUtilsTests.java index f63ca9790b7a..4381dcac7237 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/util/AddressBookNetworkUtilsTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/util/AddressBookNetworkUtilsTests.java @@ -16,11 +16,9 @@ package com.swirlds.platform.util; -import static com.swirlds.platform.roster.RosterRetriever.buildRoster; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; @@ -32,7 +30,6 @@ import com.swirlds.common.platform.NodeId; import com.swirlds.common.test.fixtures.Randotron; import com.swirlds.platform.network.Network; -import com.swirlds.platform.roster.InvalidAddressBookException; import com.swirlds.platform.state.address.AddressBookNetworkUtils; import com.swirlds.platform.system.address.Address; import com.swirlds.platform.system.address.AddressBook; @@ -99,7 +96,7 @@ void testCreateRosterFromNonEmptyAddressBook() { final AddressBook addressBook = new AddressBook(); addressBook.add(address1); addressBook.add(address2); - final Roster roster = buildRoster(addressBook); + final Roster roster = AddressBookUtils.createRoster(addressBook); assertNotNull(roster); assertEquals(2, roster.rosterEntries().size()); @@ -109,13 +106,16 @@ void testCreateRosterFromNonEmptyAddressBook() { @Test void testCreateRosterFromNullAddressBook() { - assertNull(buildRoster(null), "A null address book should produce a null roster."); + assertThrows( + NullPointerException.class, + () -> AddressBookUtils.createRoster(null), + "Illegal attempt to create a Roster from a null AddressBook"); } @Test void testCreateRosterFromEmptyAddressBook() { final AddressBook addressBook = new AddressBook(); - final Roster roster = buildRoster(addressBook); + final Roster roster = AddressBookUtils.createRoster(addressBook); assertNotNull(roster); assertTrue(roster.rosterEntries().isEmpty()); @@ -128,17 +128,16 @@ void testToRosterEntryWithCertificateEncodingException() throws CertificateEncod when(certificate.getEncoded()).thenThrow(new CertificateEncodingException()); final AddressBook addressBook = new AddressBook(List.of(address)); - assertThrows( - InvalidAddressBookException.class, - () -> buildRoster(addressBook), - "Invalid certificates are not allowed in Rosters."); + final Roster roster = AddressBookUtils.createRoster(addressBook); + + assertEquals(Bytes.EMPTY, roster.rosterEntries().getFirst().gossipCaCertificate()); } @Test void testToRosterEntryWithExternalHostname() { final Address address = new Address().copySetHostnameExternal("hostnameExternal"); final AddressBook addressBook = new AddressBook(List.of(address)); - final Roster roster = buildRoster(addressBook); + final Roster roster = AddressBookUtils.createRoster(addressBook); assertEquals(1, roster.rosterEntries().size()); assertEquals( @@ -150,7 +149,7 @@ void testToRosterEntryWithExternalHostname() { void testToRosterEntryWithInternalHostname() { final Address address = new Address().copySetHostnameInternal("hostnameInternal"); final AddressBook addressBook = new AddressBook(List.of(address)); - final Roster roster = buildRoster(addressBook); + final Roster roster = AddressBookUtils.createRoster(addressBook); assertEquals(1, roster.rosterEntries().size()); assertEquals(