diff --git a/hedera-node/hedera-addressbook-service-impl/build.gradle.kts b/hedera-node/hedera-addressbook-service-impl/build.gradle.kts index f46f5d6533ff..8c8758cdb8fa 100644 --- a/hedera-node/hedera-addressbook-service-impl/build.gradle.kts +++ b/hedera-node/hedera-addressbook-service-impl/build.gradle.kts @@ -21,6 +21,8 @@ plugins { description = "Default Hedera AddressBook Service Implementation" +dependencies { implementation(project(":swirlds-platform-core")) } + mainModuleInfo { annotationProcessor("dagger.compiler") } testModuleInfo { @@ -29,7 +31,6 @@ testModuleInfo { requires("com.hedera.node.app.service.token.impl") requires("com.hedera.node.config.test.fixtures") requires("com.swirlds.config.extensions.test.fixtures") - requires("com.swirlds.platform.core") requires("com.swirlds.platform.core.test.fixtures") requires("com.swirlds.state.api.test.fixtures") requires("com.hedera.node.app.spi.test.fixtures") diff --git a/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/schemas/V053AddressBookSchema.java b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/schemas/V053AddressBookSchema.java index 37e683b4226a..9f112c753e0f 100644 --- a/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/schemas/V053AddressBookSchema.java +++ b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/schemas/V053AddressBookSchema.java @@ -17,6 +17,7 @@ package com.hedera.node.app.service.addressbook.impl.schemas; import static com.hedera.node.app.service.addressbook.AddressBookHelper.NODES_KEY; +import static com.swirlds.platform.roster.RosterUtils.formatNodeName; import static java.util.Objects.requireNonNull; import com.hedera.hapi.node.base.AccountID; @@ -41,7 +42,6 @@ import com.swirlds.state.spi.StateDefinition; import com.swirlds.state.spi.WritableKVState; import edu.umd.cs.findbugs.annotations.NonNull; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Function; @@ -78,11 +78,12 @@ public Set statesToCreate() { @Override public void migrate(@NonNull final MigrationContext ctx) { requireNonNull(ctx); + final var networkInfo = ctx.genesisNetworkInfo(); + if (networkInfo == null) { + throw new IllegalStateException("Genesis network info is not found"); + } final WritableKVState writableNodes = ctx.newStates().get(NODES_KEY); - - final var networkInfo = ctx.networkInfo(); - final var addressBook = networkInfo.addressBook(); final var bootstrapConfig = ctx.configuration().getConfigData(BootstrapConfig.class); log.info("Started migrating nodes from address book"); @@ -93,14 +94,14 @@ public void migrate(@NonNull final MigrationContext ctx) { ? Key.newBuilder().ed25519(bootstrapConfig.genesisPublicKey()).build() : adminKey; NodeAddress nodeDetail; + final var addressBook = networkInfo.addressBook(); for (final var nodeInfo : addressBook) { final var nodeBuilder = Node.newBuilder() .nodeId(nodeInfo.nodeId()) .accountId(nodeInfo.accountId()) - .description(nodeInfo.selfName()) - .gossipEndpoint(List.of( - endpointFor(nodeInfo.internalHostName(), nodeInfo.internalPort()), - endpointFor(nodeInfo.externalHostName(), nodeInfo.externalPort()))) + // Default node description hard coded to the values used currently + .description(formatNodeName(nodeInfo.nodeId())) + .gossipEndpoint(nodeInfo.gossipEndpoints()) .gossipCaCertificate(nodeInfo.sigCertBytes()) .weight(nodeInfo.stake()) .adminKey(finalAdminKey); diff --git a/hedera-node/hedera-addressbook-service-impl/src/main/java/module-info.java b/hedera-node/hedera-addressbook-service-impl/src/main/java/module-info.java index 789884e6d6d5..be5f70723bfc 100644 --- a/hedera-node/hedera-addressbook-service-impl/src/main/java/module-info.java +++ b/hedera-node/hedera-addressbook-service-impl/src/main/java/module-info.java @@ -11,6 +11,7 @@ requires transitive dagger; requires transitive javax.inject; requires com.hedera.node.app.service.token; + requires com.swirlds.platform.core; requires org.apache.logging.log4j; requires static transitive java.compiler; requires static com.github.spotbugs.annotations; diff --git a/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/schemas/V053AddressBookSchemaTest.java b/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/schemas/V053AddressBookSchemaTest.java index b7c1c727d350..5e3b77b7be52 100644 --- a/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/schemas/V053AddressBookSchemaTest.java +++ b/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/schemas/V053AddressBookSchemaTest.java @@ -120,8 +120,8 @@ void migrateAsExpected2() { Node.newBuilder() .nodeId(1) .accountId(payerId) - .description("memo1") - .gossipEndpoint(List.of(endpointFor("127.0.0.1", 123), endpointFor("23.45.34.245", 22))) + .description("node2") + .gossipEndpoint(List.of(endpointFor("23.45.34.245", 22), endpointFor("127.0.0.1", 123))) .gossipCaCertificate(Bytes.wrap(gossipCaCertificate)) .weight(0) .adminKey(anotherKey) @@ -131,8 +131,8 @@ void migrateAsExpected2() { Node.newBuilder() .nodeId(2) .accountId(accountId) - .description("memo2") - .gossipEndpoint(List.of(endpointFor("127.0.0.2", 123), endpointFor("23.45.34.240", 23))) + .description("node3") + .gossipEndpoint(List.of(endpointFor("23.45.34.240", 23), endpointFor("127.0.0.2", 123))) .gossipCaCertificate(Bytes.wrap(grpcCertificateHash)) .weight(1) .adminKey(anotherKey) @@ -142,8 +142,8 @@ void migrateAsExpected2() { Node.newBuilder() .nodeId(3) .accountId(accountId) - .description("memo3") - .gossipEndpoint(List.of(endpointFor("127.0.0.3", 124), endpointFor("23.45.34.243", 45))) + .description("node4") + .gossipEndpoint(List.of(endpointFor("23.45.34.243", 45), endpointFor("127.0.0.3", 124))) .gossipCaCertificate(Bytes.wrap(grpcCertificateHash)) .weight(10) .adminKey(anotherKey) @@ -162,8 +162,8 @@ void migrateAsExpected3() { Node.newBuilder() .nodeId(1) .accountId(payerId) - .description("memo1") - .gossipEndpoint(List.of(endpointFor("127.0.0.1", 123), endpointFor("23.45.34.245", 22))) + .description("node2") + .gossipEndpoint(List.of(endpointFor("23.45.34.245", 22), endpointFor("127.0.0.1", 123))) .gossipCaCertificate(Bytes.wrap(gossipCaCertificate)) .weight(0) .adminKey(anotherKey) @@ -173,8 +173,8 @@ void migrateAsExpected3() { Node.newBuilder() .nodeId(2) .accountId(accountId) - .description("memo2") - .gossipEndpoint(List.of(endpointFor("127.0.0.2", 123), endpointFor("23.45.34.240", 23))) + .description("node3") + .gossipEndpoint(List.of(endpointFor("23.45.34.240", 23), endpointFor("127.0.0.2", 123))) .gossipCaCertificate(Bytes.wrap(grpcCertificateHash)) .weight(1) .adminKey(anotherKey) @@ -186,8 +186,8 @@ void migrateAsExpected3() { Node.newBuilder() .nodeId(3) .accountId(accountId) - .description("memo3") - .gossipEndpoint(List.of(endpointFor("127.0.0.3", 124), endpointFor("23.45.34.243", 45))) + .description("node4") + .gossipEndpoint(List.of(endpointFor("23.45.34.243", 45), endpointFor("127.0.0.3", 124))) .gossipCaCertificate(Bytes.wrap(grpcCertificateHash)) .weight(10) .adminKey(anotherKey) @@ -212,8 +212,8 @@ void migrateAsExpected4() { Node.newBuilder() .nodeId(1) .accountId(payerId) - .description("memo1") - .gossipEndpoint(List.of(endpointFor("127.0.0.1", 123), endpointFor("23.45.34.245", 22))) + .description("node2") + .gossipEndpoint(List.of(endpointFor("23.45.34.245", 22), endpointFor("127.0.0.1", 123))) .gossipCaCertificate(Bytes.wrap(gossipCaCertificate)) .weight(0) .adminKey(anotherKey) @@ -223,8 +223,8 @@ void migrateAsExpected4() { Node.newBuilder() .nodeId(2) .accountId(accountId) - .description("memo2") - .gossipEndpoint(List.of(endpointFor("127.0.0.2", 123), endpointFor("23.45.34.240", 23))) + .description("node3") + .gossipEndpoint(List.of(endpointFor("23.45.34.240", 23), endpointFor("127.0.0.2", 123))) .gossipCaCertificate(Bytes.wrap(grpcCertificateHash)) .weight(1) .adminKey(anotherKey) @@ -234,8 +234,8 @@ void migrateAsExpected4() { Node.newBuilder() .nodeId(3) .accountId(accountId) - .description("memo3") - .gossipEndpoint(List.of(endpointFor("127.0.0.3", 124), endpointFor("23.45.34.243", 45))) + .description("node4") + .gossipEndpoint(List.of(endpointFor("23.45.34.243", 45), endpointFor("127.0.0.3", 124))) .gossipCaCertificate(Bytes.wrap(grpcCertificateHash)) .weight(10) .adminKey(anotherKey) @@ -251,40 +251,22 @@ private void setupMigrationContext() { 1, payerId, 0, - "23.45.34.245", - 22, - "127.0.0.1", - 123, - "pubKey1", - "memo1", - Bytes.wrap(gossipCaCertificate), - "memo1"); + List.of(endpointFor("23.45.34.245", 22), endpointFor("127.0.0.1", 123)), + Bytes.wrap(gossipCaCertificate)); final var nodeInfo2 = new NodeInfoImpl( 2, accountId, 1, - "23.45.34.240", - 23, - "127.0.0.2", - 123, - "pubKey2", - "memo2", - Bytes.wrap(grpcCertificateHash), - "memo2"); + List.of(endpointFor("23.45.34.240", 23), endpointFor("127.0.0.2", 123)), + Bytes.wrap(grpcCertificateHash)); final var nodeInfo3 = new NodeInfoImpl( 3, accountId, 10, - "23.45.34.243", - 45, - "127.0.0.3", - 124, - "pubKey3", - "memo3", - Bytes.wrap(grpcCertificateHash), - "memo3"); + List.of(endpointFor("23.45.34.243", 45), endpointFor("127.0.0.3", 124)), + Bytes.wrap(grpcCertificateHash)); given(networkInfo.addressBook()).willReturn(List.of(nodeInfo1, nodeInfo2, nodeInfo3)); - given(migrationContext.networkInfo()).willReturn(networkInfo); + given(migrationContext.genesisNetworkInfo()).willReturn(networkInfo); final var config = HederaTestConfigBuilder.create() .withValue("bootstrap.genesisPublicKey", defauleAdminKeyBytes) .getOrCreateConfig(); diff --git a/hedera-node/hedera-app-spi/src/testFixtures/java/com/hedera/node/app/spi/fixtures/info/FakeNetworkInfo.java b/hedera-node/hedera-app-spi/src/testFixtures/java/com/hedera/node/app/spi/fixtures/info/FakeNetworkInfo.java index c7579b5c90b1..81a306d912a2 100644 --- a/hedera-node/hedera-app-spi/src/testFixtures/java/com/hedera/node/app/spi/fixtures/info/FakeNetworkInfo.java +++ b/hedera-node/hedera-app-spi/src/testFixtures/java/com/hedera/node/app/spi/fixtures/info/FakeNetworkInfo.java @@ -16,15 +16,15 @@ package com.hedera.node.app.spi.fixtures.info; -import static com.swirlds.platform.test.fixtures.state.TestSchema.CURRENT_VERSION; +import static com.swirlds.platform.system.address.AddressBookUtils.endpointFor; import com.hedera.hapi.node.base.AccountID; -import com.hedera.hapi.node.base.SemanticVersion; +import com.hedera.hapi.node.base.ServiceEndpoint; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.common.platform.NodeId; +import com.swirlds.state.State; import com.swirlds.state.spi.info.NetworkInfo; import com.swirlds.state.spi.info.NodeInfo; -import com.swirlds.state.spi.info.SelfNodeInfo; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.List; @@ -40,34 +40,19 @@ public class FakeNetworkInfo implements NetworkInfo { 2L, AccountID.newBuilder().accountNum(3).build(), 30, - "333.333.333.333", - 50233, - "3333333333333333333333333333333333333333333333333333333333333333", - "Alpha", - "127.0.0.1", - 20, + List.of(endpointFor("333.333.333.333", 50233), endpointFor("127.0.0.1", 20)), Bytes.wrap("cert1")), fakeInfoWith( 4L, AccountID.newBuilder().accountNum(4).build(), 40, - "444.444.444.444", - 50244, - "444444444444444444444444444444444444444444444444444444444444444", - "Bravo", - "127.0.0.2", - 21, + List.of(endpointFor("444.444.444.444", 50244), endpointFor("127.0.0.2", 21)), Bytes.wrap("cert2")), fakeInfoWith( 8L, AccountID.newBuilder().accountNum(5).build(), 50, - "555.555.555.555", - 50255, - "555555555555555555555555555555555555555555555555555555555555555", - "Charlie", - "127.0.0.3", - 22, + List.of(endpointFor("555.555.555.555", 50255), endpointFor("127.0.0.3", 22)), Bytes.wrap("cert3"))); @NonNull @@ -78,74 +63,8 @@ public Bytes ledgerId() { @NonNull @Override - public SelfNodeInfo selfNodeInfo() { - return new SelfNodeInfo() { - @NonNull - @Override - public SemanticVersion hapiVersion() { - return CURRENT_VERSION; - } - - @Override - public boolean zeroStake() { - return FAKE_NODE_INFOS.get(0).zeroStake(); - } - - @Override - public long nodeId() { - return FAKE_NODE_INFOS.get(0).nodeId(); - } - - @Override - public AccountID accountId() { - return FAKE_NODE_INFOS.get(0).accountId(); - } - - @Override - public String memo() { - return FAKE_NODE_INFOS.get(0).memo(); - } - - @Override - public String externalHostName() { - return FAKE_NODE_INFOS.get(0).externalHostName(); - } - - @Override - public int externalPort() { - return FAKE_NODE_INFOS.get(0).externalPort(); - } - - @Override - public String hexEncodedPublicKey() { - return FAKE_NODE_INFOS.get(0).hexEncodedPublicKey(); - } - - @Override - public long stake() { - return FAKE_NODE_INFOS.get(0).stake(); - } - - @Override - public String internalHostName() { - return FAKE_NODE_INFOS.get(0).internalHostName(); - } - - @Override - public int internalPort() { - return FAKE_NODE_INFOS.get(0).internalPort(); - } - - @Override - public Bytes sigCertBytes() { - return FAKE_NODE_INFOS.get(0).sigCertBytes(); - } - - @Override - public String selfName() { - return FAKE_NODE_INFOS.getFirst().selfName(); - } - }; + public NodeInfo selfNodeInfo() { + return FAKE_NODE_INFOS.get(0); } @NonNull @@ -165,16 +84,16 @@ public boolean containsNode(final long nodeId) { return FAKE_NODE_INFO_IDS.contains(new NodeId(nodeId)); } + @Override + public void updateFrom(final State state) { + throw new UnsupportedOperationException("Not implemented"); + } + private static NodeInfo fakeInfoWith( final long nodeId, @NonNull final AccountID nodeAccountId, long stake, - @NonNull String externalHostName, - int externalPort, - @NonNull String hexEncodedPublicKey, - @NonNull String memo, - @NonNull String internalHostName, - @NonNull int internalPort, + List gossipEndpoints, @Nullable Bytes sigCertBytes) { return new NodeInfo() { @Override @@ -182,54 +101,24 @@ public long nodeId() { return nodeId; } - @Override - public String memo() { - return memo; - } - @Override public AccountID accountId() { return nodeAccountId; } - @Override - public String externalHostName() { - return externalHostName; - } - - @Override - public int externalPort() { - return externalPort; - } - - @Override - public String hexEncodedPublicKey() { - return hexEncodedPublicKey; - } - @Override public long stake() { return stake; } - @Override - public String internalHostName() { - return internalHostName; - } - - @Override - public int internalPort() { - return internalPort; - } - @Override public Bytes sigCertBytes() { return sigCertBytes; } @Override - public String selfName() { - return memo; + public List gossipEndpoints() { + return gossipEndpoints; } }; } diff --git a/hedera-node/hedera-app-spi/src/testFixtures/java/module-info.java b/hedera-node/hedera-app-spi/src/testFixtures/java/module-info.java index 287a3008266f..9fc5c2497948 100644 --- a/hedera-node/hedera-app-spi/src/testFixtures/java/module-info.java +++ b/hedera-node/hedera-app-spi/src/testFixtures/java/module-info.java @@ -18,6 +18,7 @@ requires transitive org.junit.jupiter.api; requires com.hedera.node.app.hapi.utils; requires com.swirlds.common; + requires com.swirlds.platform.core; requires org.apache.logging.log4j.core; requires static com.github.spotbugs.annotations; } diff --git a/hedera-node/hedera-app/build.gradle.kts b/hedera-node/hedera-app/build.gradle.kts index 44776cc290de..9ba5fd9c9d9d 100644 --- a/hedera-node/hedera-app/build.gradle.kts +++ b/hedera-node/hedera-app/build.gradle.kts @@ -132,18 +132,10 @@ tasks.assemble { // Create the "run" task for running a Hedera consensus node tasks.register("run") { group = "application" - dependsOn(tasks.assemble) - workingDir = nodeWorkingDir.get().asFile - jvmArgs = listOf("-cp", "data/lib/*") - mainClass.set("com.swirlds.platform.Browser") -} - -tasks.register("modrun") { - group = "build" description = "Run a Hedera consensus node instance." dependsOn(tasks.assemble) workingDir = nodeWorkingDir.get().asFile - jvmArgs = listOf("-cp", "data/lib/*:data/apps/*", "-Dhedera.workflows.enabled=true") + jvmArgs = listOf("-cp", "data/lib/*:data/apps/*") mainClass.set("com.hedera.node.app.ServicesMain") } 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 30299e71a22f..5cf7ff07db84 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 @@ -34,6 +34,7 @@ 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 java.nio.charset.StandardCharsets.UTF_8; import static java.util.Objects.requireNonNull; import static java.util.concurrent.CompletableFuture.completedFuture; @@ -59,8 +60,8 @@ import com.hedera.node.app.fees.FeeService; import com.hedera.node.app.ids.EntityIdService; import com.hedera.node.app.info.CurrentPlatformStatusImpl; -import com.hedera.node.app.info.SelfNodeInfoImpl; -import com.hedera.node.app.info.UnavailableLedgerIdNetworkInfo; +import com.hedera.node.app.info.GenesisNetworkInfo; +import com.hedera.node.app.info.StateNetworkInfo; import com.hedera.node.app.records.BlockRecordService; import com.hedera.node.app.roster.RosterServiceImpl; import com.hedera.node.app.service.addressbook.impl.AddressBookServiceImpl; @@ -94,6 +95,7 @@ import com.hedera.node.config.Utils; import com.hedera.node.config.data.BlockStreamConfig; import com.hedera.node.config.data.HederaConfig; +import com.hedera.node.config.data.LedgerConfig; import com.hedera.node.config.data.VersionConfig; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.common.constructable.ClassConstructorPair; @@ -126,7 +128,7 @@ import com.swirlds.state.State; import com.swirlds.state.StateChangeListener; import com.swirlds.state.spi.WritableSingletonStateBase; -import com.swirlds.state.spi.info.SelfNodeInfo; +import com.swirlds.state.spi.info.NetworkInfo; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.nio.charset.Charset; @@ -551,8 +553,18 @@ private List onMigrate( .orElse(null)), () -> HapiUtils.toString(version.getPbjSemanticVersion()), () -> trigger); - final var selfNodeInfo = extractSelfNodeInfo(platform); - final var networkInfo = new UnavailableLedgerIdNetworkInfo(selfNodeInfo, platform); + // This is set only when the trigger is genesis. Because, only in those cases + // the migration code is using the network info values. + NetworkInfo genesisNetworkInfo = null; + if (trigger == GENESIS) { + final var config = configProvider.getConfiguration(); + final var ledgerConfig = config.getConfigData(LedgerConfig.class); + final var readableStore = + new ReadablePlatformStateStore(state.getReadableStates(PlatformStateService.NAME)); + final var genesisRoster = createRoster(requireNonNull(readableStore.getAddressBook())); + + genesisNetworkInfo = new GenesisNetworkInfo(genesisRoster, ledgerConfig.id()); + } final List migrationStateChanges = new ArrayList<>(); if (isNotEmbedded()) { if (!(state instanceof MerkleStateRoot merkleStateRoot)) { @@ -570,7 +582,7 @@ private List onMigrate( deserializedVersion, version, configProvider.getConfiguration(), - networkInfo, + genesisNetworkInfo, metrics); migrationStateChanges.addAll(migrationChanges); kvStateChangeListener.reset(); @@ -857,10 +869,6 @@ private void initializeDagger( initialStateHashFuture = new CompletableFuture<>(); notifications.register(ReconnectCompleteListener.class, new ReadReconnectStartingStateHash(notifications)); } - if (initialStateHashFuture == null) { - logger.warn("Starting from Browser is deprecated. Setting initial start hash to empty hash."); - initialStateHashFuture = completedFuture(Bytes.wrap(new byte[48])); - } // For other triggers the initial state hash must have been set already requireNonNull(initialStateHashFuture); final var roundNum = requireNonNull(state.getReadableStates(PlatformStateService.NAME) @@ -869,6 +877,7 @@ private void initializeDagger( .consensusSnapshotOrThrow() .round(); final var initialStateHash = new InitialStateHash(initialStateHashFuture, roundNum); + final var networkInfo = new StateNetworkInfo(state, platform.getSelfId().id(), configProvider); // Fully qualified so as to not confuse javadoc daggerApp = com.hedera.node.app.DaggerHederaInjectionComponent.builder() .configProviderImpl(configProvider) @@ -877,7 +886,7 @@ private void initializeDagger( .contractServiceImpl(contractServiceImpl) .initTrigger(trigger) .softwareVersion(version.getPbjSemanticVersion()) - .self(extractSelfNodeInfo(platform)) + .self(networkInfo.selfNodeInfo()) .platform(platform) .maxSignedTxnSize(MAX_SIGNED_TXN_SIZE) .crypto(CryptographyHolder.get()) @@ -890,6 +899,7 @@ private void initializeDagger( .migrationStateChanges(migrationStateChanges) .tssBaseService(tssBaseServiceSupplier.get()) .initialStateHash(initialStateHash) + .networkInfo(networkInfo) .build(); // Initialize infrastructure for fees, exchange rates, and throttles from the working state daggerApp.initializer().accept(state); @@ -1032,12 +1042,6 @@ private void assertEnvSanityChecks(@NonNull final NodeId nodeId) { } } - private SelfNodeInfo extractSelfNodeInfo(@NonNull final Platform platform) { - final var selfId = platform.getSelfId(); - final var nodeAddress = platform.getAddressBook().getAddress(selfId); - return SelfNodeInfoImpl.of(nodeAddress, hapiVersion); - } - private MerkleStateRoot withListeners(@NonNull final MerkleStateRoot root) { root.registerCommitListener(boundaryStateChangeListener); root.registerCommitListener(kvStateChangeListener); diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/HederaInjectionComponent.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/HederaInjectionComponent.java index be23dc44e8dc..81b260eebbce 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/HederaInjectionComponent.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/HederaInjectionComponent.java @@ -64,7 +64,7 @@ import com.swirlds.platform.system.Platform; import com.swirlds.state.State; import com.swirlds.state.spi.info.NetworkInfo; -import com.swirlds.state.spi.info.SelfNodeInfo; +import com.swirlds.state.spi.info.NodeInfo; import dagger.BindsInstance; import dagger.Component; import java.nio.charset.Charset; @@ -166,7 +166,7 @@ interface Builder { Builder platform(Platform platform); @BindsInstance - Builder self(final SelfNodeInfo self); + Builder self(final NodeInfo self); @BindsInstance Builder maxSignedTxnSize(@MaxSignedTxnSize final int maxSignedTxnSize); @@ -198,6 +198,9 @@ interface Builder { @BindsInstance Builder initialStateHash(InitialStateHash initialStateHash); + @BindsInstance + Builder networkInfo(NetworkInfo networkInfo); + HederaInjectionComponent build(); } } 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 62dfdeeeb7ee..5baf9d0000b1 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 @@ -170,7 +170,7 @@ public static void main(final String... args) throws Exception { // Determine which node to run locally // Load config.txt address book file and parse address book - final AddressBook bootstrapAddressBook = loadAddressBook(DEFAULT_CONFIG_FILE_NAME); + final AddressBook diskAddressBook = loadAddressBook(DEFAULT_CONFIG_FILE_NAME); // parse command line arguments final CommandLineArgs commandLineArgs = CommandLineArgs.parse(args); @@ -185,7 +185,7 @@ public static void main(final String... args) throws Exception { // get the list of configured nodes from the address book // for each node in the address book, check if it has a local IP (local to this computer) // additionally if a command line arg is supplied then limit matching nodes to that node id - final List nodesToRun = getNodesToRun(bootstrapAddressBook, commandLineArgs.localNodesToStart()); + final List nodesToRun = getNodesToRun(diskAddressBook, commandLineArgs.localNodesToStart()); // hard exit if no nodes are configured to run checkNodesToRun(nodesToRun); @@ -196,7 +196,7 @@ public static void main(final String... args) throws Exception { final var configuration = buildConfiguration(); final var keysAndCerts = - initNodeSecurity(bootstrapAddressBook, configuration).get(selfId); + initNodeSecurity(diskAddressBook, configuration).get(selfId); setupGlobalMetrics(configuration); final var metrics = getMetricsProvider().createPlatformMetrics(selfId); @@ -208,7 +208,7 @@ public static void main(final String... args) throws Exception { final var cryptography = CryptographyFactory.create(); CryptographyHolder.set(cryptography); // the AddressBook is not changed after this point, so we calculate the hash now - cryptography.digestSync(bootstrapAddressBook); + cryptography.digestSync(diskAddressBook); // Initialize the Merkle cryptography final var merkleCryptography = MerkleCryptographyFactory.create(configuration, cryptography); @@ -232,21 +232,21 @@ public static void main(final String... args) throws Exception { Hedera.APP_NAME, Hedera.SWIRLD_NAME, selfId, - bootstrapAddressBook); + diskAddressBook); final var initialState = reservedState.state(); final var stateHash = reservedState.hash(); // Initialize the address book and set on platform builder - final var addressBook = - initializeAddressBook(selfId, version, initialState, bootstrapAddressBook, platformContext); + 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 = createRoster(addressBook); final var platformBuilder = PlatformBuilder.create( Hedera.APP_NAME, Hedera.SWIRLD_NAME, version, initialState, selfId) .withPlatformContext(platformContext) .withConfiguration(configuration) .withAddressBook(addressBook) - .withRoster(createRoster(addressBook)) + .withRoster(roster) .withKeysAndCerts(keysAndCerts); hedera.setInitialStateHash(stateHash); diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/blocks/BlockStreamModule.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/blocks/BlockStreamModule.java index 49f18ff2c71a..c753f0664325 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/blocks/BlockStreamModule.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/blocks/BlockStreamModule.java @@ -20,7 +20,7 @@ import com.hedera.node.app.blocks.impl.FileBlockItemWriter; import com.hedera.node.config.ConfigProvider; import com.hedera.node.config.data.BlockStreamConfig; -import com.swirlds.state.spi.info.SelfNodeInfo; +import com.swirlds.state.spi.info.NodeInfo; import dagger.Binds; import dagger.Module; import dagger.Provides; @@ -39,7 +39,7 @@ public interface BlockStreamModule { @Singleton static Supplier bindBlockItemWriterSupplier( @NonNull final ConfigProvider configProvider, - @NonNull final SelfNodeInfo selfNodeInfo, + @NonNull final NodeInfo selfNodeInfo, @NonNull final FileSystem fileSystem) { final var config = configProvider.getConfiguration(); final var blockStreamConfig = config.getConfigData(BlockStreamConfig.class); diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/blocks/impl/FileBlockItemWriter.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/blocks/impl/FileBlockItemWriter.java index 525169b0cdec..789af4d5e4fa 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/blocks/impl/FileBlockItemWriter.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/blocks/impl/FileBlockItemWriter.java @@ -16,6 +16,7 @@ package com.hedera.node.app.blocks.impl; +import static com.swirlds.state.spi.HapiUtils.asAccountString; import static java.util.Objects.requireNonNull; import com.hedera.hapi.block.stream.schema.BlockSchema; @@ -26,7 +27,7 @@ import com.hedera.pbj.runtime.ProtoWriterTools; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.hedera.pbj.runtime.io.stream.WritableStreamingData; -import com.swirlds.state.spi.info.SelfNodeInfo; +import com.swirlds.state.spi.info.NodeInfo; import edu.umd.cs.findbugs.annotations.NonNull; import java.io.BufferedOutputStream; import java.io.IOException; @@ -86,7 +87,7 @@ private enum State { */ public FileBlockItemWriter( @NonNull final ConfigProvider configProvider, - @NonNull final SelfNodeInfo nodeInfo, + @NonNull final NodeInfo nodeInfo, @NonNull final FileSystem fileSystem) { requireNonNull(configProvider, "The supplied argument 'configProvider' cannot be null!"); requireNonNull(nodeInfo, "The supplied argument 'nodeInfo' cannot be null!"); @@ -99,7 +100,7 @@ public FileBlockItemWriter( // Compute directory for block files final Path blockDir = fileSystem.getPath(blockStreamConfig.blockFileDir()); - nodeScopedBlockDir = blockDir.resolve("block-" + nodeInfo.memo()); + nodeScopedBlockDir = blockDir.resolve("block-" + asAccountString(nodeInfo.accountId())); // Create parent directories if needed for the record file itself. try { diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/AbstractNetworkInfoImpl.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/AbstractNetworkInfoImpl.java deleted file mode 100644 index a7d850ecef01..000000000000 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/AbstractNetworkInfoImpl.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2024 Hedera Hashgraph, LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.hedera.node.app.info; - -import static java.util.Objects.requireNonNull; - -import com.swirlds.common.platform.NodeId; -import com.swirlds.platform.system.Platform; -import com.swirlds.state.spi.info.NetworkInfo; -import com.swirlds.state.spi.info.NodeInfo; -import com.swirlds.state.spi.info.SelfNodeInfo; -import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.stream.StreamSupport; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -public abstract class AbstractNetworkInfoImpl implements NetworkInfo { - private static final Logger logger = LogManager.getLogger(AbstractNetworkInfoImpl.class); - - private final SelfNodeInfo selfNode; - private final Platform platform; - - protected AbstractNetworkInfoImpl(@NonNull final SelfNodeInfo selfNode, @NonNull final Platform platform) { - this.selfNode = requireNonNull(selfNode); - this.platform = requireNonNull(platform); - } - - @NonNull - @Override - public SelfNodeInfo selfNodeInfo() { - return selfNode; - } - - @NonNull - @Override - public List addressBook() { - final var platformAddressBook = platform.getAddressBook(); - return StreamSupport.stream(platformAddressBook.spliterator(), false) - .map(NodeInfoImpl::fromAddress) - .toList(); - } - - @Override - public boolean containsNode(long nodeId) { - return platform.getAddressBook().contains(new NodeId(nodeId)); - } - - @Nullable - @Override - public NodeInfo nodeInfo(long nodeId) { - return nodeInfo(new NodeId(nodeId)); - } - - @Nullable - private NodeInfo nodeInfo(@NonNull final NodeId nodeId) { - if (nodeId.id() == selfNode.nodeId()) { - return selfNode; - } - - final var platformAddressBook = platform.getAddressBook(); - if (platformAddressBook == null) return null; - - try { - final var address = platformAddressBook.getAddress(nodeId); - return NodeInfoImpl.fromAddress(address); - } catch (NoSuchElementException e) { - // The node ID is not in the address book; this is a normal condition - // if user error leads to a request for a non-existent node - return null; - } catch (IllegalArgumentException e) { - logger.warn("Unable to parse memo of node with id {} in the platform address book", nodeId, e); - return null; - } - } -} diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/GenesisNetworkInfo.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/GenesisNetworkInfo.java new file mode 100644 index 000000000000..959b7ae2c596 --- /dev/null +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/GenesisNetworkInfo.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hedera.node.app.info; + +import static com.hedera.node.app.service.token.impl.handlers.BaseCryptoHandler.asAccount; + +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.state.State; +import com.swirlds.state.spi.info.NetworkInfo; +import com.swirlds.state.spi.info.NodeInfo; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import javax.inject.Singleton; + +/** + * Provides information about the network, including the ledger ID. This is constructed from the + * genesis roster on disk. + */ +@Singleton +public class GenesisNetworkInfo implements NetworkInfo { + private final Map nodeInfos; + private final Bytes ledgerId; + + /** + * Constructs a new {@link GenesisNetworkInfo} instance. + * + * @param genesisRoster The genesis roster + * @param ledgerId The ledger ID + */ + public GenesisNetworkInfo(final Roster genesisRoster, final Bytes ledgerId) { + this.nodeInfos = buildNodeInfoMap(genesisRoster); + this.ledgerId = ledgerId; + } + + /** + * {@inheritDoc} + */ + @NonNull + @Override + public Bytes ledgerId() { + return ledgerId; + } + + /** + * {@inheritDoc} + */ + @NonNull + @Override + public NodeInfo selfNodeInfo() { + throw new UnsupportedOperationException("Not implemented"); + } + + /** + * {@inheritDoc} + */ + @NonNull + @Override + public List addressBook() { + return List.copyOf(nodeInfos.values()); + } + + /** + * {@inheritDoc} + */ + @Nullable + @Override + public NodeInfo nodeInfo(final long nodeId) { + return nodeInfos.get(nodeId); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean containsNode(final long nodeId) { + return nodeInfos.containsKey(nodeId); + } + + @Override + public void updateFrom(final State state) { + throw new UnsupportedOperationException("Not implemented"); + } + + /** + * Builds a node info from a roster entry from the given roster. + * Since this is only used in the genesis case, the account ID is generated from the node ID + * by adding 3 to it, as a default case. + * + * @param entry The roster entry + * @return The node info + */ + private NodeInfo fromRosterEntry(RosterEntry entry) { + return new NodeInfoImpl( + entry.nodeId(), + asAccount(entry.nodeId() + 3), + entry.weight(), + entry.gossipEndpoint(), + entry.gossipCaCertificate()); + } + + /** + * Builds a map of node information from the given roster. The map is keyed by node ID. + * The node information is retrieved from the roster entry. + * If the node information is not found in the roster entry, it is not included in the map. + * + * @param roster The roster to retrieve the node information from + * @return A map of node information + */ + private Map buildNodeInfoMap(final Roster roster) { + final var nodeInfos = new LinkedHashMap(); + final var rosterEntries = roster.rosterEntries(); + for (final var rosterEntry : rosterEntries) { + nodeInfos.put(rosterEntry.nodeId(), fromRosterEntry(rosterEntry)); + } + return nodeInfos; + } +} diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/InfoInjectionModule.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/InfoInjectionModule.java index b92aa7a9c09f..4f03f047afdf 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/InfoInjectionModule.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/InfoInjectionModule.java @@ -18,24 +18,18 @@ import com.hedera.hapi.node.base.AccountID; import com.hedera.node.app.annotations.NodeSelfId; -import com.swirlds.state.spi.info.NetworkInfo; -import com.swirlds.state.spi.info.SelfNodeInfo; -import dagger.Binds; +import com.swirlds.state.spi.info.NodeInfo; import dagger.Module; import dagger.Provides; import edu.umd.cs.findbugs.annotations.NonNull; -import javax.inject.Singleton; /** A Dagger module for facilities in the {@link com.hedera.node.app.info} package. */ @Module public abstract class InfoInjectionModule { - @Binds - @Singleton - abstract NetworkInfo provideNetworkInfo(@NonNull final NetworkInfoImpl impl); @Provides @NodeSelfId - static AccountID selfAccountID(@NonNull final SelfNodeInfo info) { + static AccountID selfAccountID(@NonNull final NodeInfo info) { return info.accountId(); } } diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/NetworkInfoImpl.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/NetworkInfoImpl.java deleted file mode 100644 index 2ddbbcd2c087..000000000000 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/NetworkInfoImpl.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2023-2024 Hedera Hashgraph, LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.hedera.node.app.info; - -import com.hedera.node.config.ConfigProvider; -import com.hedera.node.config.data.LedgerConfig; -import com.hedera.pbj.runtime.io.buffer.Bytes; -import com.swirlds.platform.system.Platform; -import com.swirlds.state.spi.info.NetworkInfo; -import com.swirlds.state.spi.info.SelfNodeInfo; -import edu.umd.cs.findbugs.annotations.NonNull; -import javax.inject.Inject; -import javax.inject.Singleton; - -/** - * Provides information about the network, including the ledger ID, which may be - * overridden by configuration in state and cannot be used during state migrations - * that precede loading configuration sources from state. - */ -@Singleton -public class NetworkInfoImpl extends AbstractNetworkInfoImpl implements NetworkInfo { - private final Bytes ledgerId; - - @Inject - public NetworkInfoImpl( - @NonNull final SelfNodeInfo selfNode, - @NonNull final Platform platform, - @NonNull final ConfigProvider configProvider) { - super(selfNode, platform); - // Load the ledger ID from configuration - final var config = configProvider.getConfiguration(); - final var ledgerConfig = config.getConfigData(LedgerConfig.class); - ledgerId = ledgerConfig.id(); - } - - @NonNull - @Override - public Bytes ledgerId() { - return ledgerId; - } -} diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/NodeInfoImpl.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/NodeInfoImpl.java index ccc32931862c..851c13b3ad1c 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/NodeInfoImpl.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/NodeInfoImpl.java @@ -16,51 +16,30 @@ package com.hedera.node.app.info; -import static com.hedera.hapi.util.HapiUtils.parseAccount; -import static java.util.Objects.requireNonNull; - import com.hedera.hapi.node.base.AccountID; +import com.hedera.hapi.node.base.ServiceEndpoint; +import com.hedera.hapi.node.state.addressbook.Node; +import com.hedera.hapi.node.state.roster.RosterEntry; import com.hedera.pbj.runtime.io.buffer.Bytes; -import com.swirlds.common.utility.CommonUtils; -import com.swirlds.platform.system.address.Address; import com.swirlds.state.spi.info.NodeInfo; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; -import java.security.cert.CertificateEncodingException; +import java.util.List; public record NodeInfoImpl( long nodeId, @NonNull AccountID accountId, long stake, - @NonNull String externalHostName, - int externalPort, - @NonNull String internalHostName, - int internalPort, - @NonNull String hexEncodedPublicKey, - @NonNull String memo, - @Nullable Bytes sigCertBytes, - @NonNull String selfName) + List gossipEndpoints, + @Nullable Bytes sigCertBytes) implements NodeInfo { @NonNull - public static NodeInfo fromAddress(@NonNull final Address address) { - final var sigCert = address.getSigCert(); - Bytes sigCertBytes; - try { - sigCertBytes = sigCert == null ? Bytes.EMPTY : Bytes.wrap(sigCert.getEncoded()); - } catch (CertificateEncodingException e) { - sigCertBytes = Bytes.EMPTY; - } + public static NodeInfo fromRosterEntry(@NonNull final RosterEntry rosterEntry, @NonNull final Node node) { return new NodeInfoImpl( - address.getNodeId().id(), - parseAccount(address.getMemo()), - address.getWeight(), - requireNonNull(address.getHostnameExternal()), - address.getPortExternal(), - requireNonNull(address.getHostnameInternal()), - address.getPortInternal(), - CommonUtils.hex(requireNonNull(address.getSigPublicKey()).getEncoded()), - address.getMemo(), - sigCertBytes, - address.getSelfName()); + rosterEntry.nodeId(), + node.accountIdOrThrow(), + rosterEntry.weight(), + rosterEntry.gossipEndpoint(), + rosterEntry.gossipCaCertificate()); } } diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/SelfNodeInfoImpl.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/SelfNodeInfoImpl.java deleted file mode 100644 index a3404452b72b..000000000000 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/SelfNodeInfoImpl.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2023-2024 Hedera Hashgraph, LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.hedera.node.app.info; - -import static com.hedera.hapi.util.HapiUtils.parseAccount; -import static java.util.Objects.requireNonNull; - -import com.hedera.hapi.node.base.AccountID; -import com.hedera.hapi.node.base.SemanticVersion; -import com.hedera.pbj.runtime.io.buffer.Bytes; -import com.swirlds.common.utility.CommonUtils; -import com.swirlds.platform.system.address.Address; -import com.swirlds.state.spi.info.SelfNodeInfo; -import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; -import java.security.cert.CertificateEncodingException; - -public record SelfNodeInfoImpl( - long nodeId, - @NonNull AccountID accountId, - long stake, - @NonNull String externalHostName, - int externalPort, - @NonNull String internalHostName, - int internalPort, - @NonNull String hexEncodedPublicKey, - @NonNull String memo, - @Nullable Bytes sigCertBytes, - @NonNull SemanticVersion hapiVersion, - @NonNull String selfName) - implements SelfNodeInfo { - - public SelfNodeInfoImpl { - requireNonNull(accountId); - requireNonNull(memo); - requireNonNull(hapiVersion); - if (nodeId < 0) { - throw new IllegalArgumentException("node ID cannot be less than 0"); - } - } - - @NonNull - public static SelfNodeInfo of(@NonNull final Address address, @NonNull final SemanticVersion hapiVersion) { - requireNonNull(address); - requireNonNull(hapiVersion); - final var sigCert = address.getSigCert(); - Bytes sigCertBytes; - try { - sigCertBytes = sigCert == null ? Bytes.EMPTY : Bytes.wrap(sigCert.getEncoded()); - } catch (CertificateEncodingException e) { - sigCertBytes = Bytes.EMPTY; - } - return new SelfNodeInfoImpl( - address.getNodeId().id(), - parseAccount(address.getMemo()), - address.getWeight(), - requireNonNull(address.getHostnameExternal()), - address.getPortExternal(), - requireNonNull(address.getHostnameInternal()), - address.getPortInternal(), - CommonUtils.hex(requireNonNull(address.getSigPublicKey()).getEncoded()), - address.getMemo(), - sigCertBytes, - hapiVersion, - address.getSelfName()); - } -} diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/StateNetworkInfo.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/StateNetworkInfo.java new file mode 100644 index 000000000000..94672e0b69f2 --- /dev/null +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/StateNetworkInfo.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2023-2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hedera.node.app.info; + +import static com.hedera.node.app.info.NodeInfoImpl.fromRosterEntry; +import static com.hedera.node.app.service.addressbook.AddressBookHelper.NODES_KEY; +import static com.swirlds.platform.roster.RosterRetriever.retrieve; + +import com.hedera.hapi.node.state.addressbook.Node; +import com.hedera.hapi.node.state.common.EntityNumber; +import com.hedera.node.app.service.addressbook.AddressBookService; +import com.hedera.node.config.ConfigProvider; +import com.hedera.node.config.data.LedgerConfig; +import com.hedera.pbj.runtime.io.buffer.Bytes; +import com.swirlds.state.State; +import com.swirlds.state.spi.ReadableKVState; +import com.swirlds.state.spi.info.NetworkInfo; +import com.swirlds.state.spi.info.NodeInfo; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import javax.inject.Singleton; + +/** + * Provides information about the network, including the ledger ID, which may be + * overridden by configuration in state and cannot be used during state migrations + * that precede loading configuration sources from state. + */ +@Singleton +public class StateNetworkInfo implements NetworkInfo { + private final Bytes ledgerId; + private final Map nodeInfos; + private final long selfId; + + public StateNetworkInfo( + @NonNull final State state, final long selfId, @NonNull final ConfigProvider configProvider) { + this.selfId = selfId; + this.nodeInfos = buildNodeInfoMap(state); + // Load the ledger ID from configuration + final var config = configProvider.getConfiguration(); + final var ledgerConfig = config.getConfigData(LedgerConfig.class); + ledgerId = ledgerConfig.id(); + } + + @NonNull + @Override + public Bytes ledgerId() { + return ledgerId; + } + + @NonNull + @Override + public NodeInfo selfNodeInfo() { + return nodeInfos.get(selfId); + } + + @NonNull + @Override + public List addressBook() { + return List.copyOf(nodeInfos.values()); + } + + @Nullable + @Override + public NodeInfo nodeInfo(final long nodeId) { + return nodeInfos.get(nodeId); + } + + @Override + public boolean containsNode(final long nodeId) { + return nodeInfos.containsKey(nodeId); + } + + @Override + public void updateFrom(@NonNull final State state) { + nodeInfos.clear(); + nodeInfos.putAll(buildNodeInfoMap(state)); + } + + /** + * Build a map of node information from the state. The map is keyed by node ID. + * The node information is retrieved from the address book service. If the node + * information is not found in the address book service, it is not included in the map. + * + * @param state the state to retrieve the node information from + * @return a map of node information + */ + private Map buildNodeInfoMap(final State state) { + final var nodeInfos = new LinkedHashMap(); + final var rosterEntries = retrieve(state).rosterEntries(); + final ReadableKVState nodeState = + state.getReadableStates(AddressBookService.NAME).get(NODES_KEY); + for (final var rosterEntry : rosterEntries) { + final var node = nodeState.get( + EntityNumber.newBuilder().number(rosterEntry.nodeId()).build()); + if (node != null) { + nodeInfos.put(rosterEntry.nodeId(), fromRosterEntry(rosterEntry, node)); + } + } + return nodeInfos; + } +} diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/UnavailableLedgerIdNetworkInfo.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/UnavailableLedgerIdNetworkInfo.java deleted file mode 100644 index 564fd5ba2a07..000000000000 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/UnavailableLedgerIdNetworkInfo.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2024 Hedera Hashgraph, LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.hedera.node.app.info; - -import com.hedera.pbj.runtime.io.buffer.Bytes; -import com.swirlds.platform.system.Platform; -import com.swirlds.state.spi.info.NetworkInfo; -import com.swirlds.state.spi.info.SelfNodeInfo; -import edu.umd.cs.findbugs.annotations.NonNull; - -/** - * Provides information about the network, but NOT the ledger ID, which may be - * overridden by configuration in state and cannot be used during state migrations - * that precede loading configuration sources from state. - */ -public class UnavailableLedgerIdNetworkInfo extends AbstractNetworkInfoImpl implements NetworkInfo { - public UnavailableLedgerIdNetworkInfo(@NonNull final SelfNodeInfo selfNode, @NonNull final Platform platform) { - super(selfNode, platform); - } - - @NonNull - @Override - public Bytes ledgerId() { - throw new UnsupportedOperationException("Ledger ID is not available"); - } -} diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/UnavailableNetworkInfo.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/UnavailableNetworkInfo.java index 122d9ca34f03..94be6f118735 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/UnavailableNetworkInfo.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/info/UnavailableNetworkInfo.java @@ -17,9 +17,9 @@ package com.hedera.node.app.info; import com.hedera.pbj.runtime.io.buffer.Bytes; +import com.swirlds.state.State; import com.swirlds.state.spi.info.NetworkInfo; import com.swirlds.state.spi.info.NodeInfo; -import com.swirlds.state.spi.info.SelfNodeInfo; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.List; @@ -40,7 +40,7 @@ public Bytes ledgerId() { @NonNull @Override - public SelfNodeInfo selfNodeInfo() { + public NodeInfo selfNodeInfo() { throw new UnsupportedOperationException("Self node info is not available"); } @@ -60,4 +60,9 @@ public NodeInfo nodeInfo(final long nodeId) { public boolean containsNode(final long nodeId) { throw new UnsupportedOperationException("Node info is not available"); } + + @Override + public void updateFrom(final State state) { + throw new UnsupportedOperationException("Not implemented"); + } } diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/impl/producers/StreamFileProducerConcurrent.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/impl/producers/StreamFileProducerConcurrent.java index 44695548ed08..e6fe3437e03e 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/impl/producers/StreamFileProducerConcurrent.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/impl/producers/StreamFileProducerConcurrent.java @@ -27,7 +27,6 @@ import com.hedera.node.app.records.impl.BlockRecordStreamProducer; import com.hedera.node.app.state.SingleTransactionRecord; import com.hedera.pbj.runtime.io.buffer.Bytes; -import com.swirlds.state.spi.info.SelfNodeInfo; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.time.Instant; @@ -85,20 +84,19 @@ record TwoResults(A a, B b) {} /** * Construct {@link StreamFileProducerConcurrent} * - * @param nodeInfo the current node information - * @param format The format to use for the record stream - * @param writerFactory the factory used to create new {@link BlockRecordWriter} instances + * @param format The format to use for the record stream + * @param writerFactory the factory used to create new {@link BlockRecordWriter} instances * @param executorService The executor service to use for background threads */ @Inject public StreamFileProducerConcurrent( - @NonNull final SelfNodeInfo nodeInfo, @NonNull final BlockRecordFormat format, @NonNull final BlockRecordWriterFactory writerFactory, - @CommonExecutor @NonNull final ExecutorService executorService) { + @CommonExecutor @NonNull final ExecutorService executorService, + @NonNull final SemanticVersion hapiVersion) { this.writerFactory = requireNonNull(writerFactory); this.format = requireNonNull(format); - hapiVersion = nodeInfo.hapiVersion(); + this.hapiVersion = requireNonNull(hapiVersion); this.executorService = requireNonNull(executorService); } diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/impl/producers/StreamFileProducerSingleThreaded.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/impl/producers/StreamFileProducerSingleThreaded.java index 6fd08c4790a9..a649c3c03658 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/impl/producers/StreamFileProducerSingleThreaded.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/impl/producers/StreamFileProducerSingleThreaded.java @@ -25,7 +25,6 @@ import com.hedera.node.app.records.impl.BlockRecordStreamProducer; import com.hedera.node.app.state.SingleTransactionRecord; import com.hedera.pbj.runtime.io.buffer.Bytes; -import com.swirlds.state.spi.info.SelfNodeInfo; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.time.Instant; @@ -67,18 +66,18 @@ public final class StreamFileProducerSingleThreaded implements BlockRecordStream /** * Construct RecordManager and start background thread * - * @param nodeInfo the current node information - * @param format The format to use for the record stream + * @param format The format to use for the record stream * @param writerFactory constructs the writers for the record stream, one per record file + * @param hapiVersion */ @Inject public StreamFileProducerSingleThreaded( - @NonNull final SelfNodeInfo nodeInfo, @NonNull final BlockRecordFormat format, - @NonNull final BlockRecordWriterFactory writerFactory) { + @NonNull final BlockRecordWriterFactory writerFactory, + final SemanticVersion hapiVersion) { this.writerFactory = requireNonNull(writerFactory); this.format = requireNonNull(format); - hapiVersion = nodeInfo.hapiVersion(); + this.hapiVersion = hapiVersion; } // ========================================================================================================================================================================= diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/impl/producers/formats/BlockRecordWriterFactoryImpl.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/impl/producers/formats/BlockRecordWriterFactoryImpl.java index 8720dcbcfb88..dc250fb47b2c 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/impl/producers/formats/BlockRecordWriterFactoryImpl.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/impl/producers/formats/BlockRecordWriterFactoryImpl.java @@ -24,7 +24,7 @@ import com.hedera.node.config.ConfigProvider; import com.hedera.node.config.data.BlockRecordStreamConfig; import com.swirlds.common.stream.Signer; -import com.swirlds.state.spi.info.SelfNodeInfo; +import com.swirlds.state.spi.info.NodeInfo; import edu.umd.cs.findbugs.annotations.NonNull; import java.nio.file.FileSystem; import javax.inject.Inject; @@ -34,7 +34,7 @@ public class BlockRecordWriterFactoryImpl implements BlockRecordWriterFactory { private final ConfigProvider configProvider; private final Signer signer; - private final SelfNodeInfo nodeInfo; + private final NodeInfo selfNodeInfo; private final FileSystem fileSystem; /** @@ -46,12 +46,12 @@ public class BlockRecordWriterFactoryImpl implements BlockRecordWriterFactory { @Inject public BlockRecordWriterFactoryImpl( @NonNull final ConfigProvider configProvider, - @NonNull final SelfNodeInfo nodeInfo, + @NonNull final NodeInfo selfNodeInfo, @NonNull final Signer signer, @NonNull final FileSystem fileSystem) { this.configProvider = requireNonNull(configProvider); this.fileSystem = requireNonNull(fileSystem); - this.nodeInfo = requireNonNull(nodeInfo); + this.selfNodeInfo = requireNonNull(selfNodeInfo); this.signer = requireNonNull(signer); } @@ -66,7 +66,7 @@ public BlockRecordWriter create() throws RuntimeException { return switch (recordFileVersion) { case 6 -> new BlockRecordWriterV6( configProvider.getConfiguration().getConfigData(BlockRecordStreamConfig.class), - nodeInfo, + selfNodeInfo, signer, fileSystem); case 7 -> throw new IllegalArgumentException("Record file version 7 is not yet supported"); diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/impl/producers/formats/v6/BlockRecordWriterV6.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/impl/producers/formats/v6/BlockRecordWriterV6.java index c1ecdbbab2d1..656ed79dd980 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/impl/producers/formats/v6/BlockRecordWriterV6.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/impl/producers/formats/v6/BlockRecordWriterV6.java @@ -29,6 +29,7 @@ import static com.hedera.pbj.runtime.ProtoWriterTools.writeLong; import static com.hedera.pbj.runtime.ProtoWriterTools.writeMessage; import static com.swirlds.common.stream.LinkedObjectStreamUtilities.convertInstantToStringWithPadding; +import static com.swirlds.state.spi.HapiUtils.asAccountString; import static java.util.Objects.requireNonNull; import com.hedera.hapi.node.base.SemanticVersion; @@ -181,7 +182,7 @@ public BlockRecordWriterV6( // Compute directories for record and sidecar files final Path recordDir = fileSystem.getPath(config.logDir()); - nodeScopedRecordDir = recordDir.resolve("record" + nodeInfo.memo()); + nodeScopedRecordDir = recordDir.resolve("record" + asAccountString(nodeInfo.accountId())); nodeScopedSidecarDir = nodeScopedRecordDir.resolve(config.sidecarDir()); // Create parent directories if needed for the record file itself diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/MigrationContextImpl.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/MigrationContextImpl.java index d444c8dc095e..47b2ed7d27fa 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/MigrationContextImpl.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/MigrationContextImpl.java @@ -46,7 +46,7 @@ public record MigrationContextImpl( @NonNull ReadableStates previousStates, @NonNull WritableStates newStates, @NonNull Configuration configuration, - @NonNull NetworkInfo networkInfo, + @Nullable NetworkInfo genesisNetworkInfo, @Nullable WritableEntityIdStore writableEntityIdStore, @Nullable SemanticVersion previousVersion, @NonNull Map sharedValues) @@ -55,7 +55,6 @@ public record MigrationContextImpl( requireNonNull(previousStates); requireNonNull(newStates); requireNonNull(configuration); - requireNonNull(networkInfo); } @Override diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/OrderedServiceMigrator.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/OrderedServiceMigrator.java index df0964a61a6f..41e660116c01 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/OrderedServiceMigrator.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/OrderedServiceMigrator.java @@ -58,7 +58,14 @@ public class OrderedServiceMigrator implements ServiceMigrator { /** * Migrates the services registered with the {@link ServicesRegistry} - * + * @param state The state to migrate + * @param servicesRegistry The services registry to use for the migrations + * @param previousVersion The previous version of the state + * @param currentVersion The current version of the state + * @param config The configuration to use for the migrations + * @param genesisNetworkInfo The network information to use for the migrations. + * This is only used in genesis case + * @param metrics The metrics to use for the migrations * @return The list of state changes that occurred during the migrations */ @Override @@ -68,12 +75,11 @@ public List doMigrations( @Nullable final SoftwareVersion previousVersion, @NonNull final SoftwareVersion currentVersion, @NonNull final Configuration config, - @NonNull final NetworkInfo networkInfo, + @Nullable final NetworkInfo genesisNetworkInfo, @NonNull final Metrics metrics) { requireNonNull(state); requireNonNull(currentVersion); requireNonNull(config); - requireNonNull(networkInfo); requireNonNull(metrics); final Map sharedValues = new HashMap<>(); @@ -92,7 +98,7 @@ public List doMigrations( deserializedPbjVersion, currentVersion.getPbjSemanticVersion(), config, - networkInfo, + genesisNetworkInfo, metrics, // We call with null here because we're migrating the entity ID service itself null, @@ -129,7 +135,7 @@ public List doMigrations( deserializedPbjVersion, currentVersion.getPbjSemanticVersion(), config, - networkInfo, + genesisNetworkInfo, metrics, entityIdStore, sharedValues, diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/ServiceMigrator.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/ServiceMigrator.java index b2f9f09971c9..b8691b489664 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/ServiceMigrator.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/ServiceMigrator.java @@ -41,7 +41,7 @@ public interface ServiceMigrator { * @param previousVersion The previous version of the state * @param currentVersion The current version of the state * @param config The configuration to use for the migrations - * @param networkInfo The network information to use for the migrations + * @param genesisNetworkInfo The network information to use for the migrations * @param metrics The metrics to use for the migrations * @return The list of builders for state changes that occurred during the migrations */ @@ -51,7 +51,7 @@ List doMigrations( @Nullable SoftwareVersion previousVersion, @NonNull SoftwareVersion currentVersion, @NonNull Configuration config, - @NonNull NetworkInfo networkInfo, + @Nullable NetworkInfo genesisNetworkInfo, @NonNull Metrics metrics); /** diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/MerkleSchemaRegistry.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/MerkleSchemaRegistry.java index 360ab8b1b522..79677b7a9b0d 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/MerkleSchemaRegistry.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/MerkleSchemaRegistry.java @@ -178,7 +178,7 @@ private record RedefinedWritableStates(WritableStates beforeStates, WritableStat * @param currentVersion The current version. Never null. Must be newer than {@code * previousVersion}. * @param config The system configuration to use at the time of migration - * @param networkInfo The network information to use at the time of migration + * @param genesisNetworkInfo The network information to use at the time of migration * @param sharedValues A map of shared values for cross-service migration patterns * @param migrationStateChanges Tracker for state changes during migration * @throws IllegalArgumentException if the {@code currentVersion} is not at least the @@ -191,7 +191,7 @@ public void migrate( @Nullable final SemanticVersion previousVersion, @NonNull final SemanticVersion currentVersion, @NonNull final Configuration config, - @NonNull final NetworkInfo networkInfo, + @Nullable final NetworkInfo genesisNetworkInfo, @NonNull final Metrics metrics, @Nullable final WritableEntityIdStore entityIdStore, @NonNull final Map sharedValues, @@ -199,7 +199,6 @@ public void migrate( requireNonNull(state); requireNonNull(currentVersion); requireNonNull(config); - requireNonNull(networkInfo); requireNonNull(metrics); requireNonNull(sharedValues); requireNonNull(migrationStateChanges); @@ -257,7 +256,13 @@ && alreadyIncludesStateDefs(previousVersion, s.getVersion())) } final var migrationContext = new MigrationContextImpl( - previousStates, newStates, config, networkInfo, entityIdStore, previousVersion, sharedValues); + previousStates, + newStates, + config, + genesisNetworkInfo, + entityIdStore, + previousVersion, + sharedValues); if (applications.contains(MIGRATION)) { schema.migrate(migrationContext); } diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/DispatchProcessor.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/DispatchProcessor.java index 5429fa55bcfc..032c2204fead 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/DispatchProcessor.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/DispatchProcessor.java @@ -17,6 +17,7 @@ package com.hedera.node.app.workflows.handle; import static com.hedera.hapi.node.base.HederaFunctionality.ETHEREUM_TRANSACTION; +import static com.hedera.hapi.node.base.HederaFunctionality.NODE_UPDATE; import static com.hedera.hapi.node.base.HederaFunctionality.SYSTEM_DELETE; import static com.hedera.hapi.node.base.ResponseCodeEnum.AUTHORIZATION_FAILED; import static com.hedera.hapi.node.base.ResponseCodeEnum.ENTITY_NOT_ALLOWED_TO_DELETE; @@ -47,6 +48,7 @@ import com.hedera.node.app.workflows.handle.steps.SystemFileUpdates; import com.hedera.node.app.workflows.handle.throttle.DispatchUsageManager; import com.hedera.node.app.workflows.handle.throttle.ThrottleException; +import com.swirlds.state.spi.info.NetworkInfo; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import javax.inject.Inject; @@ -73,6 +75,7 @@ public class DispatchProcessor { private final ExchangeRateManager exchangeRateManager; private final TransactionDispatcher dispatcher; private final EthereumTransactionHandler ethereumTransactionHandler; + private final NetworkInfo networkInfo; @Inject public DispatchProcessor( @@ -84,7 +87,8 @@ public DispatchProcessor( @NonNull final DispatchUsageManager dispatchUsageManager, @NonNull final ExchangeRateManager exchangeRateManager, @NonNull final TransactionDispatcher dispatcher, - @NonNull final EthereumTransactionHandler ethereumTransactionHandler) { + @NonNull final EthereumTransactionHandler ethereumTransactionHandler, + final NetworkInfo networkInfo) { this.authorizer = requireNonNull(authorizer); this.validator = requireNonNull(validator); this.recordFinalizer = requireNonNull(recordFinalizer); @@ -94,6 +98,7 @@ public DispatchProcessor( this.exchangeRateManager = requireNonNull(exchangeRateManager); this.dispatcher = requireNonNull(dispatcher); this.ethereumTransactionHandler = requireNonNull(ethereumTransactionHandler); + this.networkInfo = requireNonNull(networkInfo); } /** @@ -182,6 +187,10 @@ private void handleSystemUpdates(final Dispatch dispatch) { // Notify if platform state was updated platformStateUpdates.handleTxBody(dispatch.stack(), dispatch.txnInfo().txBody()); + + if (dispatch.txnInfo().functionality() == NODE_UPDATE) { + networkInfo.updateFrom(dispatch.stack()); + } } /** diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/ExecutorComponent.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/ExecutorComponent.java index 1eb3dc722971..051b9552093c 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/ExecutorComponent.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/ExecutorComponent.java @@ -31,7 +31,7 @@ import com.hedera.node.app.workflows.prehandle.PreHandleWorkflowInjectionModule; import com.hedera.node.app.workflows.standalone.impl.StandaloneDispatchFactory; import com.hedera.node.app.workflows.standalone.impl.StandaloneModule; -import com.hedera.node.app.workflows.standalone.impl.StateNetworkInfo; +import com.hedera.node.app.workflows.standalone.impl.StandaloneNetworkInfo; import com.swirlds.metrics.api.Metrics; import com.swirlds.state.State; import dagger.BindsInstance; @@ -40,7 +40,7 @@ import javax.inject.Singleton; /** - * A component that provides DI for construction of {@link StandaloneDispatchFactory}, {@link StateNetworkInfo}, and + * A component that provides DI for construction of {@link StandaloneDispatchFactory}, {@link StandaloneNetworkInfo}, and * {@link DispatchProcessor} instances needed to execute standalone transactions against a {@link State}. */ @Singleton @@ -80,7 +80,7 @@ interface Builder { DispatchProcessor dispatchProcessor(); - StateNetworkInfo stateNetworkInfo(); + StandaloneNetworkInfo stateNetworkInfo(); ExchangeRateManager exchangeRateManager(); diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/impl/StandaloneDispatchFactory.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/impl/StandaloneDispatchFactory.java index 539cc9d21e19..6069da2526ef 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/impl/StandaloneDispatchFactory.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/impl/StandaloneDispatchFactory.java @@ -74,6 +74,7 @@ import com.swirlds.state.spi.info.NodeInfo; import edu.umd.cs.findbugs.annotations.NonNull; import java.time.Instant; +import java.util.List; import javax.inject.Inject; import javax.inject.Singleton; @@ -230,7 +231,7 @@ private ConsensusTransaction consensusTransactionFor(@NonNull final TransactionB } private NodeInfo creatorInfoFor(@NonNull final TransactionBody transactionBody) { - return new NodeInfoImpl(0, transactionBody.nodeAccountIDOrThrow(), 0, "", -1, "", -1, "", "", Bytes.EMPTY, ""); + return new NodeInfoImpl(0, transactionBody.nodeAccountIDOrThrow(), 0, List.of(), Bytes.EMPTY); } private PreHandleResult temporaryPreHandleResult() { diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/impl/StandaloneModule.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/impl/StandaloneModule.java index cf29d4d31099..d58b54cb14e9 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/impl/StandaloneModule.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/impl/StandaloneModule.java @@ -39,7 +39,7 @@ public interface StandaloneModule { @Binds @Singleton - NetworkInfo bindNetworkInfo(@NonNull StateNetworkInfo simulatedNetworkInfo); + NetworkInfo bindNetworkInfo(@NonNull StandaloneNetworkInfo simulatedNetworkInfo); @Binds @Singleton diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/impl/StateNetworkInfo.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/impl/StandaloneNetworkInfo.java similarity index 73% rename from hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/impl/StateNetworkInfo.java rename to hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/impl/StandaloneNetworkInfo.java index bb9cb82c5e95..c50012a474b8 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/impl/StateNetworkInfo.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/standalone/impl/StandaloneNetworkInfo.java @@ -24,18 +24,14 @@ import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.base.NodeAddressBook; import com.hedera.node.app.info.NodeInfoImpl; -import com.hedera.node.app.info.SelfNodeInfoImpl; import com.hedera.node.config.ConfigProvider; import com.hedera.node.config.data.FilesConfig; import com.hedera.node.config.data.LedgerConfig; -import com.hedera.node.config.data.VersionConfig; import com.hedera.pbj.runtime.ParseException; import com.hedera.pbj.runtime.io.buffer.Bytes; -import com.swirlds.config.api.Configuration; import com.swirlds.state.State; import com.swirlds.state.spi.info.NetworkInfo; import com.swirlds.state.spi.info.NodeInfo; -import com.swirlds.state.spi.info.SelfNodeInfo; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.List; @@ -51,27 +47,27 @@ * If the executor is to handle such transactions as if on a live network with this state, the node details file * must be present in the state and reflect the network's active address book. *

- * The {@link NetworkInfo#selfNodeInfo()} implementation, however, returns a {@link SelfNodeInfo} that is a complete + * The {@link NetworkInfo#selfNodeInfo()} implementation, however, returns a {@link NodeInfo} that is a complete * mock other than the software version, since the self-identity of the node executing a transaction clearly cannot * change how the transaction is executed. */ @Singleton -public class StateNetworkInfo implements NetworkInfo { - private static final Logger log = LogManager.getLogger(StateNetworkInfo.class); +public class StandaloneNetworkInfo implements NetworkInfo { + private static final Logger log = LogManager.getLogger(StandaloneNetworkInfo.class); private final Bytes ledgerId; private final ConfigProvider configProvider; - private final SelfNodeInfo selfNodeInfo; + private final NodeInfo selfNodeInfo; @Nullable private List nodeInfos; @Inject - public StateNetworkInfo(@NonNull final ConfigProvider configProvider) { + public StandaloneNetworkInfo(@NonNull final ConfigProvider configProvider) { this.configProvider = requireNonNull(configProvider); final var config = configProvider.getConfiguration(); this.ledgerId = config.getConfigData(LedgerConfig.class).id(); - this.selfNodeInfo = mockSelfNodeInfo(config); + this.selfNodeInfo = new NodeInfoImpl(0, AccountID.DEFAULT, 0, List.of(), Bytes.EMPTY); } /** @@ -91,17 +87,7 @@ public void initFrom(@NonNull final State state) { final var nodeAddressBook = NodeAddressBook.PROTOBUF.parse(nodeDetails); nodeInfos = nodeAddressBook.nodeAddress().stream() .map(address -> new NodeInfoImpl( - address.nodeId(), - address.nodeAccountIdOrThrow(), - address.stake(), - "", - -1, - "", - -1, - address.rsaPubKey(), - address.description(), - Bytes.EMPTY, - "")) + address.nodeId(), address.nodeAccountIdOrThrow(), address.stake(), List.of(), Bytes.EMPTY)) .toList(); } catch (ParseException e) { log.warn("Failed to parse node details", e); @@ -117,7 +103,7 @@ public Bytes ledgerId() { @NonNull @Override - public SelfNodeInfo selfNodeInfo() { + public NodeInfo selfNodeInfo() { return selfNodeInfo; } @@ -141,19 +127,12 @@ public boolean containsNode(final long nodeId) { return nodeInfo(nodeId) != null; } - private @NonNull List nodeInfosOrThrow() { - return requireNonNull(nodeInfos, "Not initialized"); + @Override + public void updateFrom(final State state) { + throw new UnsupportedOperationException("Not implemented"); } - /** - * Returns a {@link SelfNodeInfo} that is a complete mock other than the software version present in the - * given configuration. - * @param config the configuration to use - * @return a mock self node info - */ - private static SelfNodeInfo mockSelfNodeInfo(@NonNull final Configuration config) { - final var versionConfig = config.getConfigData(VersionConfig.class); - return new SelfNodeInfoImpl( - 0, AccountID.DEFAULT, 0, "", -1, "", -1, "", "", Bytes.EMPTY, versionConfig.hapiVersion(), ""); + private @NonNull List nodeInfosOrThrow() { + return requireNonNull(nodeInfos, "Not initialized"); } } diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/blocks/impl/FileBlockItemWriterTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/blocks/impl/FileBlockItemWriterTest.java index 4e75d675357e..4712ea99a458 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/blocks/impl/FileBlockItemWriterTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/blocks/impl/FileBlockItemWriterTest.java @@ -22,15 +22,18 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; +import com.hedera.hapi.node.base.AccountID; +import com.hedera.node.app.info.NodeInfoImpl; import com.hedera.node.config.ConfigProvider; import com.hedera.node.config.VersionedConfiguration; import com.hedera.node.config.data.BlockStreamConfig; import com.hedera.pbj.runtime.io.buffer.Bytes; -import com.swirlds.state.spi.info.SelfNodeInfo; +import com.swirlds.state.spi.info.NodeInfo; import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import java.util.zip.GZIPInputStream; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -47,8 +50,8 @@ public class FileBlockItemWriterTest { @Mock private ConfigProvider configProvider; - @Mock - private SelfNodeInfo selfNodeInfo; + private NodeInfo selfNodeInfo = + new NodeInfoImpl(0, AccountID.newBuilder().accountNum(3).build(), 10, List.of(), Bytes.EMPTY); @Mock private BlockStreamConfig blockStreamConfig; @@ -66,12 +69,11 @@ public void testFileBlockItemWriterConstructor() { when(blockStreamConfig.compressFilesOnCreation()).thenReturn(true); when(blockStreamConfig.blockFileDir()).thenReturn("N/A"); when(fileSystem.getPath(anyString())).thenReturn(tempDir); - when(selfNodeInfo.memo()).thenReturn("0"); FileBlockItemWriter fileBlockItemWriter = new FileBlockItemWriter(configProvider, selfNodeInfo, fileSystem); // Assertion to check if the directory is created - Path expectedDirectory = tempDir.resolve("block-0"); + Path expectedDirectory = tempDir.resolve("block-0.0.3"); assertThat(Files.exists(expectedDirectory)).isTrue(); } @@ -82,12 +84,11 @@ public void testOpenBlock() { when(blockStreamConfig.compressFilesOnCreation()).thenReturn(true); when(blockStreamConfig.blockFileDir()).thenReturn("N/A"); when(fileSystem.getPath(anyString())).thenReturn(tempDir); - when(selfNodeInfo.memo()).thenReturn("0"); FileBlockItemWriter fileBlockItemWriter = new FileBlockItemWriter(configProvider, selfNodeInfo, fileSystem); // Assertion to check if the directory is created - Path expectedDirectory = tempDir.resolve("block-0"); + Path expectedDirectory = tempDir.resolve("block-0.0.3"); assertThat(Files.exists(expectedDirectory)).isTrue(); fileBlockItemWriter.openBlock(1); @@ -104,12 +105,11 @@ public void testOpenBlockCannotInitializeTwice() { when(blockStreamConfig.compressFilesOnCreation()).thenReturn(true); when(blockStreamConfig.blockFileDir()).thenReturn("N/A"); when(fileSystem.getPath(anyString())).thenReturn(tempDir); - when(selfNodeInfo.memo()).thenReturn("0"); FileBlockItemWriter fileBlockItemWriter = new FileBlockItemWriter(configProvider, selfNodeInfo, fileSystem); // Assertion to check if the directory is created - Path expectedDirectory = tempDir.resolve("block-0"); + Path expectedDirectory = tempDir.resolve("block-0.0.3"); assertThat(Files.exists(expectedDirectory)).isTrue(); fileBlockItemWriter.openBlock(1); @@ -125,12 +125,11 @@ public void testOpenBlockNegativeBlockNumber() { when(blockStreamConfig.compressFilesOnCreation()).thenReturn(true); when(blockStreamConfig.blockFileDir()).thenReturn("N/A"); when(fileSystem.getPath(anyString())).thenReturn(tempDir); - when(selfNodeInfo.memo()).thenReturn("0"); FileBlockItemWriter fileBlockItemWriter = new FileBlockItemWriter(configProvider, selfNodeInfo, fileSystem); // Assertion to check if the directory is created - Path expectedDirectory = tempDir.resolve("block-0"); + Path expectedDirectory = tempDir.resolve("block-0.0.3"); assertThat(Files.exists(expectedDirectory)).isTrue(); assertThatThrownBy(() -> fileBlockItemWriter.openBlock(-1), "Block number must be non-negative") @@ -144,7 +143,6 @@ public void testWriteItem() throws IOException { when(blockStreamConfig.compressFilesOnCreation()).thenReturn(true); when(blockStreamConfig.blockFileDir()).thenReturn("N/A"); when(fileSystem.getPath(anyString())).thenReturn(tempDir); - when(selfNodeInfo.memo()).thenReturn("0"); FileBlockItemWriter fileBlockItemWriter = new FileBlockItemWriter(configProvider, selfNodeInfo, fileSystem); @@ -160,7 +158,7 @@ public void testWriteItem() throws IOException { fileBlockItemWriter.closeBlock(); // Read the contents of the file - Path expectedBlockFile = tempDir.resolve("block-0").resolve("000000000000000000000000000000000001.blk.gz"); + Path expectedBlockFile = tempDir.resolve("block-0.0.3").resolve("000000000000000000000000000000000001.blk.gz"); // Ungzip the file try (GZIPInputStream gzis = new GZIPInputStream(Files.newInputStream(expectedBlockFile))) { @@ -179,7 +177,6 @@ public void testWriteItemBeforeOpen() { when(blockStreamConfig.compressFilesOnCreation()).thenReturn(true); when(blockStreamConfig.blockFileDir()).thenReturn("N/A"); when(fileSystem.getPath(anyString())).thenReturn(tempDir); - when(selfNodeInfo.memo()).thenReturn("0"); FileBlockItemWriter fileBlockItemWriter = new FileBlockItemWriter(configProvider, selfNodeInfo, fileSystem); @@ -197,7 +194,6 @@ public void testCloseBlock() { when(blockStreamConfig.compressFilesOnCreation()).thenReturn(true); when(blockStreamConfig.blockFileDir()).thenReturn("N/A"); when(fileSystem.getPath(anyString())).thenReturn(tempDir); - when(selfNodeInfo.memo()).thenReturn("0"); FileBlockItemWriter fileBlockItemWriter = new FileBlockItemWriter(configProvider, selfNodeInfo, fileSystem); @@ -208,7 +204,7 @@ public void testCloseBlock() { fileBlockItemWriter.closeBlock(); // Read the contents of the file - Path expectedBlockFile = tempDir.resolve("block-0").resolve("000000000000000000000000000000000001.blk.gz"); + Path expectedBlockFile = tempDir.resolve("block-0.0.3").resolve("000000000000000000000000000000000001.blk.gz"); assertThat(Files.exists(expectedBlockFile)).isTrue(); } @@ -220,7 +216,6 @@ public void testCloseBlockNotOpen() { when(blockStreamConfig.compressFilesOnCreation()).thenReturn(true); when(blockStreamConfig.blockFileDir()).thenReturn("N/A"); when(fileSystem.getPath(anyString())).thenReturn(tempDir); - when(selfNodeInfo.memo()).thenReturn("0"); FileBlockItemWriter fileBlockItemWriter = new FileBlockItemWriter(configProvider, selfNodeInfo, fileSystem); @@ -235,7 +230,6 @@ public void testCloseBlockAlreadyClosed() { when(blockStreamConfig.compressFilesOnCreation()).thenReturn(true); when(blockStreamConfig.blockFileDir()).thenReturn("N/A"); when(fileSystem.getPath(anyString())).thenReturn(tempDir); - when(selfNodeInfo.memo()).thenReturn("0"); FileBlockItemWriter fileBlockItemWriter = new FileBlockItemWriter(configProvider, selfNodeInfo, fileSystem); diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/components/IngestComponentTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/components/IngestComponentTest.java index 433d7be06eea..04a66c12164e 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/components/IngestComponentTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/components/IngestComponentTest.java @@ -17,6 +17,7 @@ package com.hedera.node.app.components; import static com.hedera.node.app.fixtures.AppTestBase.DEFAULT_CONFIG; +import static com.swirlds.platform.system.address.AddressBookUtils.endpointFor; import static java.util.concurrent.CompletableFuture.completedFuture; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.lenient; @@ -32,7 +33,7 @@ import com.hedera.node.app.config.BootstrapConfigProviderImpl; import com.hedera.node.app.config.ConfigProviderImpl; import com.hedera.node.app.fixtures.state.FakeState; -import com.hedera.node.app.info.SelfNodeInfoImpl; +import com.hedera.node.app.info.NodeInfoImpl; import com.hedera.node.app.service.contract.impl.ContractServiceImpl; import com.hedera.node.app.service.file.impl.FileServiceImpl; import com.hedera.node.app.services.AppContextImpl; @@ -53,6 +54,7 @@ import com.swirlds.platform.system.InitTrigger; import com.swirlds.platform.system.Platform; import com.swirlds.platform.system.status.PlatformStatus; +import com.swirlds.state.spi.info.NetworkInfo; import java.time.InstantSource; import java.util.ArrayDeque; import java.util.List; @@ -81,19 +83,12 @@ void setUp() { final Metrics metrics = new NoOpMetrics(); lenient().when(platformContext.getConfiguration()).thenReturn(configuration); - final var selfNodeInfo = new SelfNodeInfoImpl( + final var selfNodeInfo = new NodeInfoImpl( 1L, AccountID.newBuilder().accountNum(1001).build(), 10, - "127.0.0.1", - 50211, - "127.0.0.4", - 23456, - "0123456789012345678901234567890123456789012345678901234567890123", - "Node7", - Bytes.wrap("cert7"), - SemanticVersion.newBuilder().major(1).build(), - "Node7"); + List.of(endpointFor("127.0.0.1", 50211), endpointFor("127.0.0.1", 23456)), + Bytes.wrap("cert7")); final var configProvider = new ConfigProviderImpl(false); final var appContext = new AppContextImpl( @@ -122,6 +117,7 @@ void setUp() { .migrationStateChanges(List.of()) .tssBaseService(tssBaseService) .initialStateHash(new InitialStateHash(completedFuture(Bytes.EMPTY), 0)) + .networkInfo(mock(NetworkInfo.class)) .build(); final var state = new FakeState(); diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/info/SelfNodeInfoImplTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/info/SelfNodeInfoImplTest.java deleted file mode 100644 index 0bccd2c87edb..000000000000 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/info/SelfNodeInfoImplTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2024 Hedera Hashgraph, LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.hedera.node.app.info; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.BDDMockito.given; - -import com.hedera.hapi.node.base.SemanticVersion; -import com.hedera.pbj.runtime.io.buffer.Bytes; -import com.swirlds.common.platform.NodeId; -import com.swirlds.common.utility.CommonUtils; -import com.swirlds.platform.system.address.Address; -import com.swirlds.state.spi.info.SelfNodeInfo; -import edu.umd.cs.findbugs.annotations.NonNull; -import java.security.PublicKey; -import java.security.cert.CertificateEncodingException; -import java.security.cert.X509Certificate; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -class SelfNodeInfoImplTest { - private static final NodeId NODE_ID = new NodeId(123); - private static final String MEMO = "0.0.3"; - private static final String SELF_NAME = "SELF_NAME"; - private static final String EXTERNAL_HOSTNAME = "ext.com"; - private static final String INTERNAL_HOSTNAME = "127.0.0.1"; - private static final long WEIGHT = 321L; - private static final int INTERNAL_PORT = 456; - private static final int EXTERNAL_PORT = 789; - private static final byte[] ENCODED_PUB_KEY = new byte[32]; - private static final byte[] ENCODED_X509_CERT = new byte[256]; - private static final SemanticVersion HAPI_VERSION = new SemanticVersion(1, 2, 3, "alpha.4", "5"); - - @Mock - private Address address; - - @Mock - private X509Certificate x509Certificate; - - @Mock - private PublicKey publicKey; - - @BeforeEach - void setUp() { - given(address.getSigCert()).willReturn(x509Certificate); - given(address.getNodeId()).willReturn(NODE_ID); - given(address.getMemo()).willReturn(MEMO); - given(address.getWeight()).willReturn(WEIGHT); - given(address.getHostnameExternal()).willReturn(EXTERNAL_HOSTNAME); - given(address.getHostnameInternal()).willReturn(INTERNAL_HOSTNAME); - given(address.getPortInternal()).willReturn(INTERNAL_PORT); - given(address.getPortExternal()).willReturn(EXTERNAL_PORT); - given(address.getSigPublicKey()).willReturn(publicKey); - given(address.getSelfName()).willReturn(SELF_NAME); - given(publicKey.getEncoded()).willReturn(ENCODED_PUB_KEY); - } - - @Test - void usesEmptyBytesForBadCert() throws CertificateEncodingException { - given(x509Certificate.getEncoded()).willThrow(CertificateEncodingException.class); - - final var subject = SelfNodeInfoImpl.of(address, HAPI_VERSION); - - assertNonSigCertFieldsAsExpected(subject); - assertEquals(Bytes.EMPTY, subject.sigCertBytes()); - } - - @Test - void usesEncodedBytesForGoodCert() throws CertificateEncodingException { - given(x509Certificate.getEncoded()).willReturn(ENCODED_X509_CERT); - - final var subject = SelfNodeInfoImpl.of(address, HAPI_VERSION); - - assertNonSigCertFieldsAsExpected(subject); - assertEquals(Bytes.wrap(ENCODED_X509_CERT), subject.sigCertBytes()); - } - - private void assertNonSigCertFieldsAsExpected(@NonNull final SelfNodeInfo subject) { - assertEquals(NODE_ID.id(), subject.nodeId()); - assertEquals(MEMO, subject.memo()); - assertEquals(SELF_NAME, subject.selfName()); - assertEquals(WEIGHT, subject.stake()); - assertEquals(EXTERNAL_HOSTNAME, subject.externalHostName()); - assertEquals(EXTERNAL_PORT, subject.externalPort()); - assertEquals(INTERNAL_HOSTNAME, subject.internalHostName()); - assertEquals(INTERNAL_PORT, subject.internalPort()); - assertEquals(HAPI_VERSION, subject.hapiVersion()); - assertEquals(CommonUtils.hex(ENCODED_PUB_KEY), subject.hexEncodedPublicKey()); - assertEquals(HAPI_VERSION, subject.hapiVersion()); - } -} diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/DependencyMigrationTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/DependencyMigrationTest.java index b33aee8bd169..73ec70f97697 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/DependencyMigrationTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/DependencyMigrationTest.java @@ -127,20 +127,6 @@ void versionedConfigRequired() { .isInstanceOf(NullPointerException.class); } - @Test - void networkInfoRequired() { - final var subject = new OrderedServiceMigrator(); - Assertions.assertThatThrownBy(() -> subject.doMigrations( - merkleTree, - servicesRegistry, - null, - new ServicesSoftwareVersion(CURRENT_VERSION), - VERSIONED_CONFIG, - null, - mock(Metrics.class))) - .isInstanceOf(NullPointerException.class); - } - @Test void metricsRequired() { final var subject = new OrderedServiceMigrator(); diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/MerkleSchemaRegistryTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/MerkleSchemaRegistryTest.java index 9b234c245f59..e4f19b5a3e32 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/MerkleSchemaRegistryTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/MerkleSchemaRegistryTest.java @@ -257,23 +257,6 @@ void nullConfigVersionThrows() { .isInstanceOf(NullPointerException.class); } - @Test - @DisplayName("Calling migrate with a null networkInfo throws NPE") - void nullNetworkInfoThrows() { - //noinspection ConstantConditions - assertThatThrownBy(() -> schemaRegistry.migrate( - merkleTree, - versions[0], - versions[1], - config, - null, - mock(Metrics.class), - mock(WritableEntityIdStore.class), - new HashMap<>(), - migrationStateChanges)) - .isInstanceOf(NullPointerException.class); - } - @Test @DisplayName("Calling migrate with a null metrics throws NPE") void nullMetricsThrows() { diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/DispatchProcessorTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/DispatchProcessorTest.java index 763b1f09825d..03bd9e46dea4 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/DispatchProcessorTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/DispatchProcessorTest.java @@ -80,6 +80,7 @@ import com.hedera.node.app.workflows.handle.throttle.DispatchUsageManager; import com.hedera.node.app.workflows.handle.throttle.ThrottleException; import com.hedera.pbj.runtime.io.buffer.Bytes; +import com.swirlds.state.spi.info.NetworkInfo; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Set; import org.junit.jupiter.api.BeforeEach; @@ -164,6 +165,9 @@ class DispatchProcessorTest { @Mock private FeeAccumulator feeAccumulator; + @Mock + private NetworkInfo networkInfo; + private DispatchProcessor subject; @BeforeEach @@ -177,7 +181,8 @@ void setUp() { dispatchUsageManager, exchangeRateManager, dispatcher, - ethereumTransactionHandler); + ethereumTransactionHandler, + networkInfo); given(dispatch.stack()).willReturn(stack); given(dispatch.recordBuilder()).willReturn(recordBuilder); } diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/BlockRecordManagerTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/BlockRecordManagerTest.java index 8d5c09601d74..0a3951496de0 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/BlockRecordManagerTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/BlockRecordManagerTest.java @@ -28,16 +28,19 @@ import static com.hedera.node.app.records.schemas.V0490BlockRecordSchema.BLOCK_INFO_STATE_KEY; import static com.hedera.node.app.records.schemas.V0490BlockRecordSchema.RUNNING_HASHES_STATE_KEY; import static com.swirlds.platform.state.service.PlatformStateService.PLATFORM_STATE_SERVICE; +import static com.swirlds.state.spi.HapiUtils.asAccountString; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; +import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.base.Timestamp; import com.hedera.hapi.node.state.blockrecords.BlockInfo; import com.hedera.hapi.node.state.blockrecords.RunningHashes; import com.hedera.node.app.blocks.impl.BlockStreamManagerImpl; import com.hedera.node.app.fixtures.AppTestBase; +import com.hedera.node.app.info.NodeInfoImpl; import com.hedera.node.app.records.BlockRecordService; import com.hedera.node.app.records.impl.BlockRecordManagerImpl; import com.hedera.node.app.records.impl.BlockRecordStreamProducer; @@ -82,13 +85,18 @@ final class BlockRecordManagerTest extends AppTestBase { private static final Timestamp CONSENSUS_TIME = Timestamp.newBuilder().seconds(1_234_567L).nanos(13579).build(); - /** Make it small enough to trigger roll over code with the number of test blocks we have */ + /** + * Make it small enough to trigger roll over code with the number of test blocks we have + */ private static final int NUM_BLOCK_HASHES_TO_KEEP = 4; private static final Timestamp FIRST_CONS_TIME_OF_LAST_BLOCK = new Timestamp(1682899224, 38693760); private static final Instant FORCED_BLOCK_SWITCH_TIME = Instant.ofEpochSecond(1682899224L, 38693760); - - /** Temporary in memory file system used for testing */ + private static final NodeInfoImpl NODE_INFO = + new NodeInfoImpl(0, AccountID.newBuilder().accountNum(3).build(), 10, List.of(), Bytes.EMPTY); + /** + * Temporary in memory file system used for testing + */ private FileSystem fs; private App app; @@ -135,8 +143,7 @@ RUNNING_HASHES_STATE_KEY, new RunningHashes(STARTING_RUNNING_HASH_OBJ.hash(), nu V0540PlatformStateSchema.PLATFORM_STATE_KEY, V0540PlatformStateSchema.GENESIS_PLATFORM_STATE) .commit(); - blockRecordWriterFactory = new BlockRecordWriterFactoryImpl( - app.configProvider(), app.networkInfo().selfNodeInfo(), SIGNER, fs); + blockRecordWriterFactory = new BlockRecordWriterFactoryImpl(app.configProvider(), NODE_INFO, SIGNER, fs); } @AfterEach @@ -182,12 +189,8 @@ void testRecordStreamProduction(final String startMode, final boolean concurrent final var merkleState = app.workingStateAccessor().getState(); final var producer = concurrent ? new StreamFileProducerConcurrent( - app.networkInfo().selfNodeInfo(), - blockRecordFormat, - blockRecordWriterFactory, - ForkJoinPool.commonPool()) - : new StreamFileProducerSingleThreaded( - app.networkInfo().selfNodeInfo(), blockRecordFormat, blockRecordWriterFactory); + blockRecordFormat, blockRecordWriterFactory, ForkJoinPool.commonPool(), app.hapiVersion()) + : new StreamFileProducerSingleThreaded(blockRecordFormat, blockRecordWriterFactory, app.hapiVersion()); Bytes finalRunningHash; try (final var blockRecordManager = new BlockRecordManagerImpl( app.configProvider(), app.workingStateAccessor().getState(), producer)) { @@ -239,8 +242,7 @@ void testRecordStreamProduction(final String startMode, final boolean concurrent final var recordStreamConfig = app.configProvider().getConfiguration().getConfigData(BlockRecordStreamConfig.class); validateRecordStreamFiles( - fs.getPath(recordStreamConfig.logDir()) - .resolve("record" + app.networkInfo().selfNodeInfo().memo()), + fs.getPath(recordStreamConfig.logDir()).resolve("record" + asAccountString(NODE_INFO.accountId())), recordStreamConfig, USER_PUBLIC_KEY, TEST_BLOCKS, @@ -272,8 +274,8 @@ void testBlockInfoMethods() throws Exception { final Random random = new Random(82792874); final var merkleState = app.workingStateAccessor().getState(); - final var producer = new StreamFileProducerSingleThreaded( - app.networkInfo().selfNodeInfo(), blockRecordFormat, blockRecordWriterFactory); + final var producer = + new StreamFileProducerSingleThreaded(blockRecordFormat, blockRecordWriterFactory, app.hapiVersion()); Bytes finalRunningHash; try (final var blockRecordManager = new BlockRecordManagerImpl( app.configProvider(), app.workingStateAccessor().getState(), producer)) { @@ -368,8 +370,7 @@ void testBlockInfoMethods() throws Exception { final var recordStreamConfig = app.configProvider().getConfiguration().getConfigData(BlockRecordStreamConfig.class); validateRecordStreamFiles( - fs.getPath(recordStreamConfig.logDir()) - .resolve("record" + app.networkInfo().selfNodeInfo().memo()), + fs.getPath(recordStreamConfig.logDir()).resolve("record" + asAccountString(NODE_INFO.accountId())), recordStreamConfig, USER_PUBLIC_KEY, TEST_BLOCKS, diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/impl/producers/StreamFileProducerConcurrentTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/impl/producers/StreamFileProducerConcurrentTest.java index 92680606bb64..f40d75245ded 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/impl/producers/StreamFileProducerConcurrentTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/impl/producers/StreamFileProducerConcurrentTest.java @@ -16,6 +16,8 @@ package com.hedera.node.app.workflows.handle.record.impl.producers; +import static com.hedera.node.app.records.RecordTestData.VERSION; + import com.hedera.node.app.records.impl.BlockRecordStreamProducer; import com.hedera.node.app.records.impl.producers.BlockRecordWriterFactory; import com.hedera.node.app.records.impl.producers.StreamFileProducerConcurrent; @@ -27,6 +29,6 @@ final class StreamFileProducerConcurrentTest extends StreamFileProducerTest { @Override BlockRecordStreamProducer createStreamProducer(@NonNull final BlockRecordWriterFactory factory) { return new StreamFileProducerConcurrent( - selfNodeInfo, BlockRecordFormatV6.INSTANCE, factory, ForkJoinPool.commonPool()); + BlockRecordFormatV6.INSTANCE, factory, ForkJoinPool.commonPool(), VERSION); } } diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/impl/producers/StreamFileProducerSingleThreadedTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/impl/producers/StreamFileProducerSingleThreadedTest.java index edf626283375..a96d8374c6b4 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/impl/producers/StreamFileProducerSingleThreadedTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/impl/producers/StreamFileProducerSingleThreadedTest.java @@ -16,6 +16,8 @@ package com.hedera.node.app.workflows.handle.record.impl.producers; +import static com.hedera.node.app.records.RecordTestData.VERSION; + import com.hedera.node.app.records.impl.BlockRecordStreamProducer; import com.hedera.node.app.records.impl.producers.BlockRecordWriterFactory; import com.hedera.node.app.records.impl.producers.StreamFileProducerSingleThreaded; @@ -26,6 +28,6 @@ final class StreamFileProducerSingleThreadedTest extends StreamFileProducerTest @Override BlockRecordStreamProducer createStreamProducer(@NonNull final BlockRecordWriterFactory factory) { - return new StreamFileProducerSingleThreaded(selfNodeInfo, BlockRecordFormatV6.INSTANCE, factory); + return new StreamFileProducerSingleThreaded(BlockRecordFormatV6.INSTANCE, factory, VERSION); } } diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/impl/producers/formats/v6/BlockRecordWriterV6Test.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/impl/producers/formats/v6/BlockRecordWriterV6Test.java index fdff1c756b16..5fa0ca9f968e 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/impl/producers/formats/v6/BlockRecordWriterV6Test.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/impl/producers/formats/v6/BlockRecordWriterV6Test.java @@ -22,6 +22,7 @@ import static com.hedera.node.app.records.RecordTestData.STARTING_RUNNING_HASH_OBJ; import static com.hedera.node.app.records.RecordTestData.TEST_BLOCKS; import static com.hedera.node.app.records.RecordTestData.VERSION; +import static com.swirlds.state.spi.HapiUtils.asAccountString; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -104,7 +105,8 @@ void createApp(final boolean compress) throws IOException { hapiVersion = app.hapiVersion(); writer = new BlockRecordWriterV6(config, selfNodeInfo, SIGNER, fileSystem); final var ext = compress ? ".rcd.gz" : ".rcd"; - final var recordDir = fileSystem.getPath(config.logDir(), "record" + selfNodeInfo.memo() + "/"); + final var recordDir = + fileSystem.getPath(config.logDir(), "record" + asAccountString(selfNodeInfo.accountId()) + "/"); recordPath = recordDir.resolve("2018-08-24T16_25_42.000000890Z" + ext); sigPath = recordDir.resolve("2018-08-24T16_25_42.000000890Z.rcd_sig"); } @@ -167,7 +169,8 @@ void badRecordPath() { void recordDirectoryCouldNotBeCreated() throws IOException { // Given a "logDir" in the config that points not to a directory, but to a pre-existing FILE (!!!) final var config = buildAndGetConfig(); - final var recordDir = fileSystem.getPath(config.logDir(), "record" + selfNodeInfo.memo() + "/"); + final var recordDir = + fileSystem.getPath(config.logDir(), "record" + asAccountString(selfNodeInfo.accountId()) + "/"); Files.createDirectories(recordDir.getParent()); Files.createFile(recordDir); @@ -218,7 +221,8 @@ void recordFileInExistingLocation() throws IOException { app = appBuilder.build(); config = app.configProvider().getConfiguration().getConfigData(BlockRecordStreamConfig.class); hapiVersion = app.hapiVersion(); - final var recordDir = fileSystem.getPath(config.logDir(), "record" + selfNodeInfo.memo() + "/"); + final var recordDir = + fileSystem.getPath(config.logDir(), "record" + asAccountString(selfNodeInfo.accountId()) + "/"); Files.createDirectories(recordDir); // When we create a new writer and initialize it diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/throttle/DispatchUsageManagerTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/throttle/DispatchUsageManagerTest.java index 699ad9972fb3..2472fd3ae76b 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/throttle/DispatchUsageManagerTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/throttle/DispatchUsageManagerTest.java @@ -63,7 +63,7 @@ import com.swirlds.config.api.Configuration; import com.swirlds.state.spi.ReadableStates; import com.swirlds.state.spi.info.NetworkInfo; -import com.swirlds.state.spi.info.SelfNodeInfo; +import com.swirlds.state.spi.info.NodeInfo; import java.time.Instant; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -132,7 +132,7 @@ class DispatchUsageManagerTest { private NetworkInfo networkInfo; @Mock - private SelfNodeInfo selfNodeInfo; + private NodeInfo selfNodeInfo; @Mock private ReadableStoreFactory readableStoreFactory; diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/standalone/TransactionExecutorsTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/standalone/TransactionExecutorsTest.java index 56ed77c6bda0..48549ab406e1 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/standalone/TransactionExecutorsTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/standalone/TransactionExecutorsTest.java @@ -66,9 +66,11 @@ import com.hedera.node.config.data.HederaConfig; import com.hedera.node.config.data.VersionConfig; import com.hedera.pbj.runtime.io.buffer.Bytes; +import com.swirlds.common.crypto.internal.CryptoUtils; import com.swirlds.common.metrics.noop.NoOpMetrics; import com.swirlds.common.platform.NodeId; import com.swirlds.config.api.Configuration; +import com.swirlds.platform.crypto.CryptoStatic; import com.swirlds.platform.system.address.AddressBook; import com.swirlds.platform.test.fixtures.addressbook.RandomAddressBookBuilder; import com.swirlds.state.State; @@ -76,13 +78,17 @@ import com.swirlds.state.spi.WritableStates; import com.swirlds.state.spi.info.NetworkInfo; import com.swirlds.state.spi.info.NodeInfo; -import com.swirlds.state.spi.info.SelfNodeInfo; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.io.UncheckedIOException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; import java.time.Instant; import java.time.InstantSource; import java.util.List; @@ -303,28 +309,32 @@ public Bytes ledgerId() { @NonNull @Override - public SelfNodeInfo selfNodeInfo() { - throw new UnsupportedOperationException("Not implemented"); + public NodeInfo selfNodeInfo() { + return new NodeInfoImpl(0, AccountID.DEFAULT, 0, List.of(), getCertBytes(randomX509Certificate())); } @NonNull @Override public List addressBook() { - return StreamSupport.stream(addressBook.spliterator(), false) - .map(NodeInfoImpl::fromAddress) - .toList(); + return List.of( + new NodeInfoImpl(0, AccountID.DEFAULT, 0, List.of(), getCertBytes(randomX509Certificate()))); } @Nullable @Override public NodeInfo nodeInfo(final long nodeId) { - throw new UnsupportedOperationException("Not implemented"); + return new NodeInfoImpl(0, AccountID.DEFAULT, 0, List.of(), Bytes.EMPTY); } @Override public boolean containsNode(final long nodeId) { return addressBook.contains(new NodeId(nodeId)); } + + @Override + public void updateFrom(final State state) { + throw new UnsupportedOperationException("Not implemented"); + } }; } @@ -338,4 +348,27 @@ private Bytes resourceAsBytes(@NonNull final String loc) { throw new UncheckedIOException(e); } } + + public static X509Certificate randomX509Certificate() { + try { + final SecureRandom secureRandom = CryptoUtils.getDetRandom(); + + final KeyPairGenerator rsaKeyGen = KeyPairGenerator.getInstance("RSA"); + rsaKeyGen.initialize(3072, secureRandom); + final KeyPair rsaKeyPair1 = rsaKeyGen.generateKeyPair(); + + final String name = "CN=Bob"; + return CryptoStatic.generateCertificate(name, rsaKeyPair1, name, rsaKeyPair1, secureRandom); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static Bytes getCertBytes(X509Certificate certificate) { + try { + return Bytes.wrap(certificate.getEncoded()); + } catch (CertificateEncodingException e) { + throw new RuntimeException(e); + } + } } 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 28f3169c5c95..714108232234 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,6 +16,8 @@ package com.hedera.node.app.fixtures; +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; @@ -26,8 +28,8 @@ import com.hedera.node.app.fixtures.state.FakePlatform; import com.hedera.node.app.fixtures.state.FakeSchemaRegistry; import com.hedera.node.app.fixtures.state.FakeState; -import com.hedera.node.app.info.NetworkInfoImpl; -import com.hedera.node.app.info.SelfNodeInfoImpl; +import com.hedera.node.app.info.GenesisNetworkInfo; +import com.hedera.node.app.info.NodeInfoImpl; import com.hedera.node.app.service.token.TokenService; import com.hedera.node.app.spi.fixtures.Scenarios; import com.hedera.node.app.spi.fixtures.TransactionFactory; @@ -57,12 +59,12 @@ import com.swirlds.state.spi.WritableStates; import com.swirlds.state.spi.info.NetworkInfo; import com.swirlds.state.spi.info.NodeInfo; -import com.swirlds.state.spi.info.SelfNodeInfo; import com.swirlds.state.test.fixtures.MapWritableKVState; import com.swirlds.state.test.fixtures.TestBase; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -145,19 +147,12 @@ public WritableStates getWritableStates(@NonNull String serviceName) { .declineReward(true) .build(); - protected final SelfNodeInfo selfNodeInfo = new SelfNodeInfoImpl( + protected final NodeInfo selfNodeInfo = new NodeInfoImpl( 7, nodeSelfAccountId, 10, - "127.0.0.1", - 50211, - "127.0.0.4", - 23456, - "0123456789012345678901234567890123456789012345678901234567890123", - "Node7", - Bytes.wrap("cert7"), - hapiVersion, - "Node7"); + List.of(endpointFor("127.0.0.1", 50211), endpointFor("127.0.0.1", 23456)), + Bytes.wrap("cert7")); /** * The gRPC system has extensive metrics. This object allows us to inspect them and make sure they are being set @@ -241,7 +236,8 @@ public static final class TestAppBuilder { private SemanticVersion hapiVersion = CURRENT_VERSION; private Set services = new LinkedHashSet<>(); private TestConfigBuilder configBuilder = HederaTestConfigBuilder.create(); - private NodeInfo selfNodeInfo = null; + private NodeInfo selfNodeInfo = new NodeInfoImpl( + 0, AccountID.newBuilder().shardNum(0).realmNum(0).accountNum(8).build(), 10, List.of(), Bytes.EMPTY); private Set nodes = new LinkedHashSet<>(); private TestAppBuilder() {} @@ -314,40 +310,26 @@ public TestAppBuilder withSelfNode(@NonNull final NodeInfo selfNodeInfo) { } public App build() { - final SelfNodeInfo realSelfNodeInfo; + final NodeInfo realSelfNodeInfo; if (this.selfNodeInfo == null) { final var nodeSelfAccountId = AccountID.newBuilder() .shardNum(0) .realmNum(0) .accountNum(8) .build(); - realSelfNodeInfo = new SelfNodeInfoImpl( + realSelfNodeInfo = new NodeInfoImpl( 7, nodeSelfAccountId, 10, - "127.0.0.1", - 50211, - "127.0.0.4", - 23456, - "0123456789012345678901234567890123456789012345678901234567890123", - "Node7", - Bytes.wrap("cert7"), - hapiVersion, - "Node7"); + List.of(endpointFor("127.0.0.1", 50211), endpointFor("127.0.0.4", 23456)), + Bytes.wrap("cert7")); } else { - realSelfNodeInfo = new SelfNodeInfoImpl( + realSelfNodeInfo = new NodeInfoImpl( selfNodeInfo.nodeId(), selfNodeInfo.accountId(), selfNodeInfo.stake(), - selfNodeInfo.externalHostName(), - selfNodeInfo.externalPort(), - selfNodeInfo.internalHostName(), - selfNodeInfo.internalPort(), - selfNodeInfo.hexEncodedPublicKey(), - selfNodeInfo.memo(), - selfNodeInfo.sigCertBytes(), - hapiVersion, - selfNodeInfo.selfName()); + selfNodeInfo.gossipEndpoints(), + selfNodeInfo.sigCertBytes()); } final var workingStateAccessor = new WorkingStateAccessor(); @@ -356,14 +338,12 @@ public App build() { final var addresses = nodes.stream() .map(nodeInfo -> new Address() .copySetNodeId(new NodeId(nodeInfo.nodeId())) - .copySetMemo(nodeInfo.memo()) .copySetWeight(nodeInfo.zeroStake() ? 0 : 10)) .toList(); - - final var platform = new FakePlatform(realSelfNodeInfo.nodeId(), new AddressBook(addresses)); - final var networkInfo = new NetworkInfoImpl(realSelfNodeInfo, platform, configProvider); - + final var addressBook = new AddressBook(addresses); + final var platform = new FakePlatform(realSelfNodeInfo.nodeId(), addressBook); final var initialState = new FakeState(); + 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/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeSchemaRegistry.java b/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeSchemaRegistry.java index 15075510d5bc..90073caddc54 100644 --- a/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeSchemaRegistry.java +++ b/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeSchemaRegistry.java @@ -204,7 +204,7 @@ public Configuration configuration() { } @Override - public NetworkInfo networkInfo() { + public NetworkInfo genesisNetworkInfo() { return networkInfo; } diff --git a/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeState.java b/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeState.java index c0f84b95202c..66e73e5edb35 100644 --- a/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeState.java +++ b/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeState.java @@ -66,7 +66,11 @@ public class FakeState implements State { */ public FakeState addService(@NonNull final String serviceName, @NonNull final Map dataSources) { final var serviceStates = this.states.computeIfAbsent(serviceName, k -> new ConcurrentHashMap<>()); - serviceStates.putAll(dataSources); + dataSources.forEach((k, b) -> { + if (!serviceStates.containsKey(k)) { + serviceStates.put(k, b); + } + }); // Purge any readable or writable states whose state definitions are now stale, // since they don't include the new data sources we just added readableStates.remove(serviceName); diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/V0490TokenSchema.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/V0490TokenSchema.java index 0d03bc21087f..6cc08b20dd0f 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/V0490TokenSchema.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/V0490TokenSchema.java @@ -240,7 +240,7 @@ private void initializeStakingNodeInfo(@NonNull final MigrationContext ctx) { final var config = ctx.configuration(); final var ledgerConfig = config.getConfigData(LedgerConfig.class); final var stakingConfig = config.getConfigData(StakingConfig.class); - final var addressBook = ctx.networkInfo().addressBook(); + final var addressBook = ctx.genesisNetworkInfo().addressBook(); final var numberOfNodes = addressBook.size(); final long maxStakePerNode = ledgerConfig.totalTinyBarFloat() / numberOfNodes; diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/V0530TokenSchema.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/V0530TokenSchema.java index 7d70c74511ea..918263f4e129 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/V0530TokenSchema.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/V0530TokenSchema.java @@ -58,7 +58,7 @@ public void migrate(@NonNull final MigrationContext ctx) { private void setMinStakeToZero(final MigrationContext ctx) { final var stakingInfoState = ctx.newStates().get(STAKING_INFO_KEY); - final var addressBook = ctx.networkInfo().addressBook(); + final var addressBook = ctx.genesisNetworkInfo().addressBook(); logger.info("Setting minStake to 0 for all nodes in the address book"); for (final var node : addressBook) { final var nodeNumber = 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 5db6bdd82fbe..b939ce5ff3eb 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,6 +19,8 @@ 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.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.status.PlatformStatus.ACTIVE; import static com.swirlds.platform.system.status.PlatformStatus.FREEZE_COMPLETE; @@ -28,6 +30,7 @@ import static java.util.stream.StreamSupport.stream; import com.hedera.hapi.node.base.SemanticVersion; +import com.hedera.hapi.platform.state.PlatformState; import com.hedera.node.app.Hedera; import com.hedera.node.app.fixtures.state.FakeServiceMigrator; import com.hedera.node.app.fixtures.state.FakeServicesRegistry; @@ -48,10 +51,13 @@ import com.swirlds.common.platform.NodeId; import com.swirlds.platform.config.legacy.LegacyConfigPropertiesLoader; import com.swirlds.platform.listeners.PlatformStatusChangeNotification; +import com.swirlds.platform.state.service.PlatformStateService; import com.swirlds.platform.system.SoftwareVersion; import com.swirlds.platform.system.address.Address; import com.swirlds.platform.system.address.AddressBook; import com.swirlds.platform.test.fixtures.addressbook.RandomAddressBookBuilder; +import com.swirlds.state.spi.CommittableWritableStates; +import com.swirlds.state.spi.WritableSingletonState; import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.io.UncheckedIOException; @@ -122,6 +128,15 @@ protected AbstractEmbeddedHedera(@NonNull final EmbeddedNode node) { @Override public void start() { hedera.initPlatformState(state); + final var writableStates = state.getWritableStates(PlatformStateService.NAME); + final WritableSingletonState platformState = writableStates.getSingleton(PLATFORM_STATE_KEY); + final var currentState = requireNonNull(platformState.get()); + platformState.put(currentState + .copyBuilder() + .addressBook(toPbjAddressBook(addressBook)) + .build()); + ((CommittableWritableStates) writableStates).commit(); + hedera.setInitialStateHash(FAKE_START_OF_STATE_HASH); hedera.onStateInitialized(state, fakePlatform(), GENESIS, null); hedera.init(fakePlatform(), defaultNodeId); diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/support/validators/block/StateChangesValidator.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/support/validators/block/StateChangesValidator.java index b13b56edfefe..1c3649117de4 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/support/validators/block/StateChangesValidator.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/support/validators/block/StateChangesValidator.java @@ -31,6 +31,7 @@ import static com.hedera.services.bdd.spec.TargetNetworkType.SUBPROCESS_NETWORK; import static com.swirlds.platform.state.GenesisStateBuilder.initGenesisPlatformState; import static com.swirlds.platform.state.service.PlatformStateService.PLATFORM_STATE_SERVICE; +import static com.swirlds.platform.system.address.AddressBookUtils.createRoster; import static java.util.Objects.requireNonNull; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -59,7 +60,7 @@ import com.hedera.node.app.config.ConfigProviderImpl; import com.hedera.node.app.fees.FeeService; import com.hedera.node.app.ids.EntityIdService; -import com.hedera.node.app.info.NodeInfoImpl; +import com.hedera.node.app.info.GenesisNetworkInfo; import com.hedera.node.app.records.BlockRecordService; import com.hedera.node.app.roster.RosterServiceImpl; import com.hedera.node.app.service.addressbook.impl.AddressBookServiceImpl; @@ -80,6 +81,7 @@ import com.hedera.node.app.throttle.CongestionThrottleService; import com.hedera.node.app.version.ServicesSoftwareVersion; import com.hedera.node.config.VersionedConfiguration; +import com.hedera.node.config.converter.BytesConverter; import com.hedera.node.config.data.HederaConfig; import com.hedera.node.config.data.VersionConfig; import com.hedera.pbj.runtime.io.buffer.Bytes; @@ -109,9 +111,6 @@ import com.swirlds.state.State; import com.swirlds.state.spi.CommittableWritableStates; import com.swirlds.state.spi.Service; -import com.swirlds.state.spi.info.NetworkInfo; -import com.swirlds.state.spi.info.NodeInfo; -import com.swirlds.state.spi.info.SelfNodeInfo; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.io.IOException; @@ -128,7 +127,6 @@ import java.util.concurrent.Executors; import java.util.function.Function; import java.util.regex.Pattern; -import java.util.stream.StreamSupport; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.Assertions; @@ -164,7 +162,8 @@ public static void main(String[] args) { "f31a2b563cbe3fef1242bd94bc610fc5134267faa7f3fefc5de176cc1f4032f28d5b27f084bbc388c5a766e4d057acdd"), node0Dir.resolve("output/swirlds.log"), node0Dir.resolve("genesis-config.txt"), - node0Dir.resolve("data/config/application.properties")); + node0Dir.resolve("data/config/application.properties"), + Bytes.fromHex("03")); final var blocks = BlockStreamAccess.BLOCK_STREAM_ACCESS.readBlocks(node0Dir.resolve("data/block-streams/block-0.0.3")); validator.validateBlocks(blocks); @@ -212,7 +211,9 @@ public static StateChangesValidator newValidatorFor(@NonNull final HapiSpec spec rootHash, node0.getExternalPath(SWIRLDS_LOG), genesisConfigTxt, - node0.getExternalPath(APPLICATION_PROPERTIES)); + node0.getExternalPath(APPLICATION_PROPERTIES), + requireNonNull(new BytesConverter() + .convert(spec.startupProperties().get("ledger.id")))); } catch (IOException e) { throw new UncheckedIOException(e); } @@ -222,7 +223,8 @@ public StateChangesValidator( @NonNull final Bytes expectedRootHash, @NonNull final Path pathToNode0SwirldsLog, @NonNull final Path pathToAddressBook, - @NonNull final Path pathToOverrideProperties) { + @NonNull final Path pathToOverrideProperties, + @NonNull final Bytes ledgerId) { this.expectedRootHash = requireNonNull(expectedRootHash); this.pathToNode0SwirldsLog = requireNonNull(pathToNode0SwirldsLog); @@ -237,7 +239,8 @@ public StateChangesValidator( final var versionConfig = bootstrapConfig.getConfigData(VersionConfig.class); final var servicesVersion = versionConfig.servicesVersion(); final var addressBook = loadAddressBookWithDeterministicCerts(pathToAddressBook); - final var networkInfo = fakeNetworkInfoFrom(addressBook); + final var roster = createRoster(addressBook); + final var networkInfo = new GenesisNetworkInfo(roster, ledgerId); final var migrator = new OrderedServiceMigrator(); final var configVersion = @@ -536,41 +539,6 @@ private void registerServices( .forEach(servicesRegistry::register); } - private NetworkInfo fakeNetworkInfoFrom(@NonNull final AddressBook addressBook) { - return new NetworkInfo() { - @NonNull - @Override - public Bytes ledgerId() { - throw new UnsupportedOperationException("Not implemented"); - } - - @NonNull - @Override - public SelfNodeInfo selfNodeInfo() { - throw new UnsupportedOperationException("Not implemented"); - } - - @NonNull - @Override - public List addressBook() { - return StreamSupport.stream(addressBook.spliterator(), false) - .map(NodeInfoImpl::fromAddress) - .toList(); - } - - @Nullable - @Override - public NodeInfo nodeInfo(final long nodeId) { - throw new UnsupportedOperationException("Not implemented"); - } - - @Override - public boolean containsNode(final long nodeId) { - return addressBook.contains(new NodeId(nodeId)); - } - }; - } - private SignatureVerifier fakeSignatureVerifier() { return new SignatureVerifier() { @Override diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/hip993/SystemFileExportsTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/hip993/SystemFileExportsTest.java index 5242f737f7f5..6a38526151bd 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/hip993/SystemFileExportsTest.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/hip993/SystemFileExportsTest.java @@ -71,7 +71,6 @@ import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; -import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.base.FileID; import com.hedera.hapi.node.base.NodeAddressBook; import com.hedera.hapi.node.base.ServiceEndpoint; @@ -131,7 +130,6 @@ final Stream syntheticNodeDetailsUpdateHappensAtUpgradeBoundary() { cryptoCreate("firstUser"), overriding("nodes.updateAccountIdAllowed", "true"), sourcing(() -> blockingOrder(nOps(CLASSIC_HAPI_TEST_NETWORK_SIZE, i -> nodeUpdate("" + i) - .accountId("0.0." + (i + ACCOUNT_ID_OFFSET)) .description(DESCRIPTION_PREFIX + i) .serviceEndpoint(endpointsFor(i)) .grpcCertificateHash(grpcCertHashes[i]) @@ -157,7 +155,6 @@ final Stream syntheticAddressBookUpdateHappensAtUpgradeBoundary() { cryptoCreate("firstUser"), overriding("nodes.updateAccountIdAllowed", "true"), sourcing(() -> blockingOrder(nOps(CLASSIC_HAPI_TEST_NETWORK_SIZE, i -> nodeUpdate("" + i) - .accountId("0.0." + (i + ACCOUNT_ID_OFFSET)) .description(DESCRIPTION_PREFIX + i) .serviceEndpoint(endpointsFor(i)) .grpcCertificateHash(grpcCertHashes[i]) @@ -457,11 +454,6 @@ private static VisibleItemsValidator nodeDetailsExportValidator( actualCertHash, "node" + address.nodeId() + " has wrong cert hash"); - final var expectedAccountID = AccountID.newBuilder() - .accountNum(address.nodeId() + ACCOUNT_ID_OFFSET) - .build(); - assertEquals(expectedAccountID, address.nodeAccountId()); - final var expectedDescription = DESCRIPTION_PREFIX + address.nodeId(); assertEquals(expectedDescription, address.description()); @@ -504,11 +496,6 @@ private static VisibleItemsValidator addressBookExportValidator( actualCertHash, "node" + address.nodeId() + " has wrong cert hash"); - final var expectedAccountID = AccountID.newBuilder() - .accountNum(address.nodeId() + ACCOUNT_ID_OFFSET) - .build(); - assertEquals(expectedAccountID, address.nodeAccountId()); - final var expectedServiceEndpoint = endpointsFor((int) address.nodeId()); assertEquals(expectedServiceEndpoint, address.serviceEndpoint()); } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/regression/system/DabEnabledUpgradeTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/regression/system/DabEnabledUpgradeTest.java index d2551ff89b5c..13046fe64281 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/regression/system/DabEnabledUpgradeTest.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/regression/system/DabEnabledUpgradeTest.java @@ -288,7 +288,6 @@ final Stream exportedAddressBookReflectsOnlyEditsBeforePrepareUpgra // Now make some changes that should not be incorporated in this upgrade nodeDelete("5"), nodeDelete("2"), - nodeUpdate("0").accountId(classicFeeCollectorIdLiteralFor(900)), validateUpgradeAddressBooks(NodeSelector.allNodes(), DabEnabledUpgradeTest::validateMultipartEdits), upgradeToNextConfigVersion( FakeNmt.removeNode(NodeSelector.byNodeId(4L), DAB_GENERATED), diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/roster/RosterRetriever.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/roster/RosterRetriever.java index dc8851fa5b1e..76117432be2f 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/roster/RosterRetriever.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/roster/RosterRetriever.java @@ -16,6 +16,8 @@ package com.swirlds.platform.roster; +import static java.util.Objects.requireNonNull; + import com.hedera.hapi.node.base.ServiceEndpoint; import com.hedera.hapi.node.state.primitives.ProtoBytes; import com.hedera.hapi.node.state.roster.Roster; @@ -72,7 +74,7 @@ public static Roster retrieve(@NonNull final State state) { // So use an AddressBook from the PlatformState to build a roster: final ReadablePlatformStateStore readablePlatformStateStore = new ReadablePlatformStateStore(state.getReadableStates(PlatformStateService.NAME)); - return buildRoster(readablePlatformStateStore.getAddressBook()); + return buildRoster(requireNonNull(readablePlatformStateStore.getAddressBook())); } /** 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 d8738b47f8d9..fa9fe0db2a6e 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 @@ -126,6 +126,7 @@ exports com.swirlds.platform.state.service; exports com.swirlds.platform.builder.internal; exports com.swirlds.platform.config.internal; + exports com.swirlds.platform.roster; requires transitive com.hedera.node.hapi; requires transitive com.swirlds.base; diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/roster/RosterRetrieverTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/roster/RosterRetrieverTests.java index 51671c0705b6..410a460d4c1f 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/roster/RosterRetrieverTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/roster/RosterRetrieverTests.java @@ -295,7 +295,7 @@ void testRetrieveAddressBook() { assertEquals(ROSTER_FROM_ADDRESS_BOOK, RosterRetriever.retrieve(state)); } - private static X509Certificate randomX509Certificate() { + public static X509Certificate randomX509Certificate() { try { final SecureRandom secureRandom = CryptoUtils.getDetRandom(); diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/HapiUtils.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/HapiUtils.java index c70e37b1ddd3..070a3409aaac 100644 --- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/HapiUtils.java +++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/HapiUtils.java @@ -16,6 +16,7 @@ package com.swirlds.state.spi; +import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.base.SemanticVersion; import com.swirlds.common.io.streams.SerializableDataInputStream; import com.swirlds.common.io.streams.SerializableDataOutputStream; @@ -96,4 +97,8 @@ private static void serializeIfUsed(final String semVerPart, final SerializableD out.writeNormalisedString(semVerPart); } } + + public static String asAccountString(final AccountID accountID) { + return String.format("%d.%d.%d", accountID.shardNum(), accountID.realmNum(), accountID.accountNum()); + } } diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/MigrationContext.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/MigrationContext.java index 4ec7bc970483..779df37300a7 100644 --- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/MigrationContext.java +++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/MigrationContext.java @@ -63,7 +63,8 @@ public interface MigrationContext { * * @return The {@link NetworkInfo} of the network at the time of migration. */ - NetworkInfo networkInfo(); + @Nullable + NetworkInfo genesisNetworkInfo(); /** * Consumes and returns the next entity number. For use by migrations that need to create entities. diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/NetworkInfo.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/NetworkInfo.java index 001041b4af99..10e3a898aff3 100644 --- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/NetworkInfo.java +++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/NetworkInfo.java @@ -17,6 +17,7 @@ package com.swirlds.state.spi.info; import com.hedera.pbj.runtime.io.buffer.Bytes; +import com.swirlds.state.State; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.List; @@ -35,7 +36,7 @@ public interface NetworkInfo { Bytes ledgerId(); @NonNull - SelfNodeInfo selfNodeInfo(); + NodeInfo selfNodeInfo(); @NonNull List addressBook(); @@ -45,8 +46,17 @@ public interface NetworkInfo { /** * Returns true if the network contains a node with the given ID. + * * @param nodeId the ID of the node to check for * @return true if the network contains a node with the given ID */ boolean containsNode(long nodeId); + + /** + * Updates the network information from the given state. This method is called when the + * state is updated using node updates. + * + * @param state the state to update from + */ + void updateFrom(State state); } diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/NodeInfo.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/NodeInfo.java index 6df1bdb866eb..8f6b2155e0ff 100644 --- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/NodeInfo.java +++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/NodeInfo.java @@ -17,7 +17,14 @@ package com.swirlds.state.spi.info; import com.hedera.hapi.node.base.AccountID; +import com.hedera.hapi.node.base.ServiceEndpoint; import com.hedera.pbj.runtime.io.buffer.Bytes; +import com.swirlds.common.utility.CommonUtils; +import java.io.ByteArrayInputStream; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.List; /** * Summarizes useful information about the nodes in the AddressBook from the Platform. In @@ -54,32 +61,6 @@ default boolean zeroStake() { */ AccountID accountId(); - /** - * Convenience method to get the memo of this node's account which is in the address book. - * - * @return this node's account memo - */ - String memo(); - - /** - * The host name of this node, as known by the external world. This is an IP address. - * - * @return The host name (IP Address) of this node - */ - String externalHostName(); - - /** - * The port the node is listening on. - * @return the port. Non-negative. - */ - int externalPort(); - - /** - * The public key of this node, as a hex-encoded string. - * @return the public key - */ - String hexEncodedPublicKey(); - /** * The stake weight of this node. * @return the stake weight @@ -93,21 +74,30 @@ default boolean zeroStake() { Bytes sigCertBytes(); /** - * The host name of this node, as known by the internal world. This is an IP address. + * The list of service endpoints of this node, as known by the internal and external worlds. + * This has an IP address and port. * * @return The host name (IP Address) of this node */ - String internalHostName(); + List gossipEndpoints(); /** - * The internal port the node is listening on. - * @return the port. Non-negative. + * The public key of this node, as a hex-encoded string. It is extracted from the certificate bytes. + * + * @return the public key */ - int internalPort(); + default String hexEncodedPublicKey() { + try { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); - /** - * The "self name" of this node. - * @return the self name - */ - String selfName(); + // Convert the byte array to an InputStream and generate the X509Certificate object + X509Certificate certificate = (X509Certificate) certificateFactory.generateCertificate( + new ByteArrayInputStream(sigCertBytes().toByteArray())); + + // Return the public key from the certificate + return CommonUtils.hex(certificate.getPublicKey().getEncoded()); + } catch (CertificateException e) { + throw new IllegalStateException("Error extracting public key from certificate", e); + } + } } diff --git a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/SelfNodeInfo.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/SelfNodeInfo.java deleted file mode 100644 index 2a561a7e5e0f..000000000000 --- a/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/SelfNodeInfo.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2023-2024 Hedera Hashgraph, LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.swirlds.state.spi.info; - -import com.hedera.hapi.node.base.SemanticVersion; -import edu.umd.cs.findbugs.annotations.NonNull; - -/** - * Additional information specific to this node, including information not necessarily available in the address - * book itself. - */ -public interface SelfNodeInfo extends NodeInfo { - /** - * The version of HAPI supported by this operating node. - * - * @return The version of HAPI - */ - @NonNull - SemanticVersion hapiVersion(); -}