diff --git a/.github/workflows/node-zxcron-release-fsts-regression.yaml b/.github/workflows/node-zxcron-release-fsts-regression.yaml index 1d0f0eb0eea5..20f4c39da1fd 100644 --- a/.github/workflows/node-zxcron-release-fsts-regression.yaml +++ b/.github/workflows/node-zxcron-release-fsts-regression.yaml @@ -51,7 +51,7 @@ jobs: major="${BASH_REMATCH[1]}" minor="${BASH_REMATCH[2]}" - if [[ "${major}" -eq 0 && "${minor}" -lt 48 ]]; then + if [[ "${major}" -eq 0 && "${minor}" -lt 50 ]]; then continue fi diff --git a/.github/workflows/platform-zxcron-release-jrs-regression.yaml b/.github/workflows/platform-zxcron-release-jrs-regression.yaml index cc29ff6a462b..47eb02884065 100644 --- a/.github/workflows/platform-zxcron-release-jrs-regression.yaml +++ b/.github/workflows/platform-zxcron-release-jrs-regression.yaml @@ -51,7 +51,7 @@ jobs: major="${BASH_REMATCH[1]}" minor="${BASH_REMATCH[2]}" - if [[ "${major}" -eq 0 && "${minor}" -lt 48 ]]; then + if [[ "${major}" -eq 0 && "${minor}" -lt 50 ]]; then continue fi diff --git a/block-node/blocknode-core/src/main/java/module-info.java b/block-node/blocknode-core/src/main/java/module-info.java index 7813bfcf3868..4e4aeb1d357d 100644 --- a/block-node/blocknode-core/src/main/java/module-info.java +++ b/block-node/blocknode-core/src/main/java/module-info.java @@ -3,15 +3,14 @@ exports com.hedera.node.blocknode.core to com.hedera.storage.blocknode.core.test; - // Require the modules needed for compilation. - requires com.hedera.storage.blocknode.filesystem.local; - requires com.hedera.storage.blocknode.filesystem.s3; - - // Require modules which are needed for compilation and should be available to all modules that depend on this - // module (including tests and other source sets). + // 'requires transitive' - modules which are needed for compilation and should be available to + // all modules that depend on this module (including tests and other source sets) + // 'require' - modules needed only for compilation of this module + requires transitive com.hedera.node.hapi; requires transitive com.hedera.storage.blocknode.core.spi; requires transitive com.hedera.storage.blocknode.filesystem.api; requires transitive com.hedera.storage.blocknode.grpc.api; requires transitive com.hedera.storage.blocknode.state; - requires transitive com.hedera.node.hapi; + requires com.hedera.storage.blocknode.filesystem.local; + requires com.hedera.storage.blocknode.filesystem.s3; } diff --git a/block-node/blocknode-filesystem-local/src/main/java/module-info.java b/block-node/blocknode-filesystem-local/src/main/java/module-info.java index 105019e078ea..ed3de72bfd66 100644 --- a/block-node/blocknode-filesystem-local/src/main/java/module-info.java +++ b/block-node/blocknode-filesystem-local/src/main/java/module-info.java @@ -4,10 +4,9 @@ com.hedera.storage.blocknode.filesystem.local.test, com.hedera.storage.blocknode.core; - // Require the modules needed for compilation. - requires com.hedera.storage.blocknode.core.spi; - - // Require modules which are needed for compilation and should be available to all modules that depend on this - // module (including tests and other source sets). + // 'requires transitive' - modules which are needed for compilation and should be available to + // all modules that depend on this module (including tests and other source sets) + // 'require' - modules needed only for compilation of this module requires transitive com.hedera.storage.blocknode.filesystem.api; + requires com.hedera.storage.blocknode.core.spi; } diff --git a/block-node/blocknode-filesystem-s3/src/main/java/module-info.java b/block-node/blocknode-filesystem-s3/src/main/java/module-info.java index 8ebea648d375..f41644c64b64 100644 --- a/block-node/blocknode-filesystem-s3/src/main/java/module-info.java +++ b/block-node/blocknode-filesystem-s3/src/main/java/module-info.java @@ -4,10 +4,9 @@ com.hedera.storage.blocknode.filesystem.s3.test, com.hedera.storage.blocknode.core; - // Require the modules needed for compilation. - requires com.hedera.storage.blocknode.core.spi; - - // Require modules which are needed for compilation and should be available to all modules that depend on this - // module (including tests and other source sets). + // 'requires transitive' - modules which are needed for compilation and should be available to + // all modules that depend on this module (including tests and other source sets) + // 'require' - modules needed only for compilation of this module requires transitive com.hedera.storage.blocknode.filesystem.api; + requires com.hedera.storage.blocknode.core.spi; } diff --git a/block-node/blocknode-state/src/main/java/module-info.java b/block-node/blocknode-state/src/main/java/module-info.java index f5c47ff784e3..90b4b7959aae 100644 --- a/block-node/blocknode-state/src/main/java/module-info.java +++ b/block-node/blocknode-state/src/main/java/module-info.java @@ -2,10 +2,9 @@ // Export the packages that should be available to other modules. exports com.hedera.node.blocknode.state; - // Require the modules needed for compilation. - requires com.hedera.storage.blocknode.core.spi; - - // Require modules which are needed for compilation and should be available to all modules that depend on this - // module (including tests and other source sets + // 'requires transitive' - modules which are needed for compilation and should be available to + // all modules that depend on this module (including tests and other source sets) + // 'require' - modules needed only for compilation of this module requires transitive com.swirlds.platform.core; + requires com.hedera.storage.blocknode.core.spi; } diff --git a/gradle/plugins/src/main/kotlin/com.hedera.gradle.blocknode.gradle.kts b/gradle/plugins/src/main/kotlin/com.hedera.gradle.blocknode.gradle.kts index 97d2db967e4d..0060698d7037 100644 --- a/gradle/plugins/src/main/kotlin/com.hedera.gradle.blocknode.gradle.kts +++ b/gradle/plugins/src/main/kotlin/com.hedera.gradle.blocknode.gradle.kts @@ -20,5 +20,3 @@ plugins { } group = "com.hedera.storage" - -tasks.checkModuleInfo { moduleNamePrefix = "com.hedera.storage" } diff --git a/gradle/plugins/src/main/kotlin/com.hedera.gradle.java.gradle.kts b/gradle/plugins/src/main/kotlin/com.hedera.gradle.java.gradle.kts index 0df9c05990a4..89b2670d4ba8 100644 --- a/gradle/plugins/src/main/kotlin/com.hedera.gradle.java.gradle.kts +++ b/gradle/plugins/src/main/kotlin/com.hedera.gradle.java.gradle.kts @@ -19,6 +19,7 @@ import com.autonomousapps.AbstractExtension import com.autonomousapps.DependencyAnalysisSubExtension import com.hedera.gradle.services.TaskLockService import com.hedera.gradle.utils.Utils.versionTxt +import org.gradlex.javamodule.dependencies.tasks.ModuleDirectivesOrderingCheck plugins { id("java") @@ -211,6 +212,8 @@ tasks.withType().configureEach { "implSpec:a:Implementation Requirements:", "implNote:a:Implementation Note:" ) + options.windowTitle = "Hedera Consensus Node" + options.memberLevel = JavadocMemberLevel.PACKAGE } } @@ -345,6 +348,9 @@ tasks.check { dependsOn(tasks.jacocoTestReport) } tasks.named("qualityGate") { dependsOn(tasks.checkAllModuleInfo) } +// ordering check is done by SortModuleInfoRequiresStep +tasks.withType { enabled = false } + tasks.withType() { // When ding a 'qualityGate' run, make sure spotlessApply is done before doing compilation and // other checks based on compiled code diff --git a/gradle/plugins/src/main/kotlin/com.hedera.gradle.platform.gradle.kts b/gradle/plugins/src/main/kotlin/com.hedera.gradle.platform.gradle.kts index 95ad1b5a011a..1b5a6afda026 100644 --- a/gradle/plugins/src/main/kotlin/com.hedera.gradle.platform.gradle.kts +++ b/gradle/plugins/src/main/kotlin/com.hedera.gradle.platform.gradle.kts @@ -21,8 +21,6 @@ plugins { group = "com.swirlds" -tasks.checkModuleInfo { moduleNamePrefix = "com.swirlds" } - // All below configuration should eventually be removed once all 'sdk' tests in 'src/test' // are able to run in parallel without restrictions. tasks.test { diff --git a/gradle/plugins/src/main/kotlin/com.hedera.gradle.root.gradle.kts b/gradle/plugins/src/main/kotlin/com.hedera.gradle.root.gradle.kts index 8923fdf65928..4077603044b7 100644 --- a/gradle/plugins/src/main/kotlin/com.hedera.gradle.root.gradle.kts +++ b/gradle/plugins/src/main/kotlin/com.hedera.gradle.root.gradle.kts @@ -27,7 +27,14 @@ plugins { id("com.autonomousapps.dependency-analysis") } -spotless { kotlinGradle { target("gradle/plugins/**/*.gradle.kts") } } +spotless { + kotlinGradle { target("gradle/plugins/**/*.gradle.kts") } + kotlin { + // For the Kotlin classes (*.kt files) + ktfmt().kotlinlangStyle() + target("gradle/plugins/**/*.kt") + } +} val productVersion = layout.projectDirectory.versionTxt().asFile.readText().trim() diff --git a/gradle/plugins/src/main/kotlin/com.hedera.gradle.spotless-java.gradle.kts b/gradle/plugins/src/main/kotlin/com.hedera.gradle.spotless-java.gradle.kts index 030d31049379..984b2cece001 100644 --- a/gradle/plugins/src/main/kotlin/com.hedera.gradle.spotless-java.gradle.kts +++ b/gradle/plugins/src/main/kotlin/com.hedera.gradle.spotless-java.gradle.kts @@ -15,19 +15,22 @@ */ import com.hedera.gradle.spotless.RepairDashedCommentsFormatterStep +import com.hedera.gradle.spotless.SortModuleInfoRequiresStep import com.hedera.gradle.spotless.StripOldLicenseFormatterStep plugins { id("com.hedera.gradle.spotless") } spotless { java { - targetExclude("build/generated/sources/**/*.java") - targetExclude("build/generated/source/**/*.java") + targetExclude("build/generated/sources/**/*.java", "build/generated/source/**/*.java") + // fix errors due to dashed comment blocks (eg: /*-, /*--, etc) addStep(RepairDashedCommentsFormatterStep.create()) // Remove the old license headers as the spotless licenseHeader formatter // cannot find them if they are located between the package and import statements. addStep(StripOldLicenseFormatterStep.create()) + // Sort the 'requires' entries in Module Info files + addStep(SortModuleInfoRequiresStep.create()) // enable toggle comment support toggleOffOn() // don't need to set target, it is inferred from java diff --git a/gradle/plugins/src/main/kotlin/com/hedera/gradle/services/TaskLockService.kt b/gradle/plugins/src/main/kotlin/com/hedera/gradle/services/TaskLockService.kt index 212fe41ffd26..7b9b5c77aee0 100644 --- a/gradle/plugins/src/main/kotlin/com/hedera/gradle/services/TaskLockService.kt +++ b/gradle/plugins/src/main/kotlin/com/hedera/gradle/services/TaskLockService.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022-2023 Hedera Hashgraph, LLC + * Copyright (C) 2022-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. diff --git a/gradle/plugins/src/main/kotlin/com/hedera/gradle/spotless/RepairDashedCommentsFormatterStep.kt b/gradle/plugins/src/main/kotlin/com/hedera/gradle/spotless/RepairDashedCommentsFormatterStep.kt index 39b9770b4f7f..5a3efd96c1c2 100644 --- a/gradle/plugins/src/main/kotlin/com/hedera/gradle/spotless/RepairDashedCommentsFormatterStep.kt +++ b/gradle/plugins/src/main/kotlin/com/hedera/gradle/spotless/RepairDashedCommentsFormatterStep.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022-2023 Hedera Hashgraph, LLC + * Copyright (C) 2022-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. @@ -20,8 +20,8 @@ import com.diffplug.spotless.FormatterFunc import com.diffplug.spotless.FormatterStep /** - * Adds self-correcting behavior as spotless step which properly removes the comments which causes the - * google-java-formatter plugin to rupture (eg: \/\*-). + * Adds self-correcting behavior as spotless step which properly removes the comments which causes + * the google-java-formatter plugin to rupture (eg: \/\*-). */ class RepairDashedCommentsFormatterStep { companion object { @@ -42,10 +42,6 @@ class RepairDashedCommentsFormatterStep { private class State(val openingCommentRegex: Regex, val closingCommentRegex: Regex) : java.io.Serializable { - companion object { - private const val serialVersionUID = -113 - } - fun toFormatter(): FormatterFunc { return FormatterFunc { unixStr -> diff --git a/gradle/plugins/src/main/kotlin/com/hedera/gradle/spotless/SortModuleInfoRequiresStep.kt b/gradle/plugins/src/main/kotlin/com/hedera/gradle/spotless/SortModuleInfoRequiresStep.kt new file mode 100644 index 000000000000..e744208eb681 --- /dev/null +++ b/gradle/plugins/src/main/kotlin/com/hedera/gradle/spotless/SortModuleInfoRequiresStep.kt @@ -0,0 +1,102 @@ +/* + * 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.gradle.spotless + +import com.diffplug.spotless.FormatterFunc +import com.diffplug.spotless.FormatterStep + +class SortModuleInfoRequiresStep { + companion object { + private const val NAME = "SortModuleInfoRequires" + private val OWN_PACKAGES = listOf("com.swirlds.", "com.hedera.node.", "com.hedera.storage.") + + fun create(): FormatterStep { + return FormatterStep.create(NAME, State(), State::toFormatter) + } + } + + private class State : java.io.Serializable { + + fun toFormatter(): FormatterFunc { + return FormatterFunc { unixStr -> + val lines = unixStr.split('\n') + val blockStartIndex = lines.indexOfFirst { it.trim().startsWith("requires") } + val blockEndIndex = lines.indexOfLast { it.trim().startsWith("requires") } + + if (blockStartIndex == -1) { + unixStr // not a module-info.java or no 'requires' + } else { + val nonRequiresLines = mutableListOf() + + val requiresTransitive = mutableListOf() + val requires = mutableListOf() + val requiresStaticTransitive = mutableListOf() + val requiresStatic = mutableListOf() + + lines.subList(blockStartIndex, blockEndIndex + 1).forEach { line -> + when { + line.trim().startsWith("requires static transitive") -> + requiresStaticTransitive.add(line) + line.trim().startsWith("requires static") -> requiresStatic.add(line) + line.trim().startsWith("requires transitive") -> + requiresTransitive.add(line) + line.trim().startsWith("requires") -> requires.add(line) + line.isNotBlank() && !line.trim().startsWith("requires") -> + nonRequiresLines.add(line) + } + } + + val comparator = + Comparator { a, b -> + val nameA = a.split(" ").first { it.endsWith(";") } + val nameB = b.split(" ").first { it.endsWith(";") } + if ( + OWN_PACKAGES.any { nameA.startsWith(it) } && + OWN_PACKAGES.none { nameB.startsWith(it) } + ) { + -1 + } else if ( + OWN_PACKAGES.none { nameA.startsWith(it) } && + OWN_PACKAGES.any { nameB.startsWith(it) } + ) { + 1 + } else { + nameA.compareTo(nameB) + } + } + + requiresTransitive.sortWith(comparator) + requires.sortWith(comparator) + requiresStaticTransitive.sortWith(comparator) + requiresStatic.sortWith(comparator) + + val blockStart = lines.subList(0, blockStartIndex) + val blockEnd = lines.subList(blockEndIndex + 1, lines.size) + + (blockStart + + nonRequiresLines + + requiresTransitive + + requires + + requiresStaticTransitive + + requiresStatic + + blockEnd) + .joinToString("\n") + } + } + } + } +} diff --git a/gradle/plugins/src/main/kotlin/com/hedera/gradle/spotless/StripOldLicenseFormatterStep.kt b/gradle/plugins/src/main/kotlin/com/hedera/gradle/spotless/StripOldLicenseFormatterStep.kt index c4b435d47542..e9aedfac0bdb 100644 --- a/gradle/plugins/src/main/kotlin/com/hedera/gradle/spotless/StripOldLicenseFormatterStep.kt +++ b/gradle/plugins/src/main/kotlin/com/hedera/gradle/spotless/StripOldLicenseFormatterStep.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022-2023 Hedera Hashgraph, LLC + * Copyright (C) 2022-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. @@ -19,28 +19,22 @@ package com.hedera.gradle.spotless import com.diffplug.spotless.FormatterFunc import com.diffplug.spotless.FormatterStep -/* - Removes the old copyright statements which were incorrectly located between the package and import statements. - These legacy copyright blocks also uses with an unexpected opening comment tag. This FormatterStep removes those - comment blocks using a very conservative approach to avoid mutilating actual code. +/** + * Removes the old copyright statements which were incorrectly located between the package and + * import statements. These legacy copyright blocks also uses with an unexpected opening comment + * tag. This FormatterStep removes those comment blocks using a very conservative approach to avoid + * mutilating actual code. */ class StripOldLicenseFormatterStep { companion object { private const val NAME = "StripOldLicense" fun create(): FormatterStep { - return FormatterStep.create( - NAME, - State(), - State::toFormatter - ) + return FormatterStep.create(NAME, State(), State::toFormatter) } } - private class State() : java.io.Serializable { - companion object { - private const val serialVersionUID = -113 - } + private class State : java.io.Serializable { fun toFormatter(): FormatterFunc { return FormatterFunc { unixStr -> @@ -57,8 +51,7 @@ class StripOldLicenseFormatterStep { } } - val finalStr = result.joinToString("\n") - finalStr + result.joinToString("\n") } } } diff --git a/gradle/plugins/src/main/kotlin/com/hedera/gradle/tasks/GitClone.kt b/gradle/plugins/src/main/kotlin/com/hedera/gradle/tasks/GitClone.kt index d4356938f738..343318a1a996 100644 --- a/gradle/plugins/src/main/kotlin/com/hedera/gradle/tasks/GitClone.kt +++ b/gradle/plugins/src/main/kotlin/com/hedera/gradle/tasks/GitClone.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022-2023 Hedera Hashgraph, LLC + * Copyright (C) 2022-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. @@ -16,6 +16,7 @@ package com.hedera.gradle.tasks +import javax.inject.Inject import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property @@ -24,29 +25,20 @@ import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction import org.gradle.process.ExecOperations -import javax.inject.Inject abstract class GitClone : DefaultTask() { - @get:Input - abstract val url: Property + @get:Input abstract val url: Property - @get:Input - @get:Optional - abstract val tag: Property + @get:Input @get:Optional abstract val tag: Property - @get:Input - @get:Optional - abstract val branch: Property + @get:Input @get:Optional abstract val branch: Property - @get:Input - abstract val offline: Property + @get:Input abstract val offline: Property - @get:OutputDirectory - abstract val localCloneDirectory: DirectoryProperty + @get:OutputDirectory abstract val localCloneDirectory: DirectoryProperty - @get:Inject - protected abstract val exec: ExecOperations + @get:Inject protected abstract val exec: ExecOperations init { // If a 'branch' is configured, the task is never up-to-date as it may change @@ -96,4 +88,4 @@ abstract class GitClone : DefaultTask() { } } } -} \ No newline at end of file +} diff --git a/gradle/plugins/src/main/kotlin/com/hedera/gradle/utils/Utils.kt b/gradle/plugins/src/main/kotlin/com/hedera/gradle/utils/Utils.kt index 94bba28d8947..948ce171e8b6 100644 --- a/gradle/plugins/src/main/kotlin/com/hedera/gradle/utils/Utils.kt +++ b/gradle/plugins/src/main/kotlin/com/hedera/gradle/utils/Utils.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022-2023 Hedera Hashgraph, LLC + * Copyright (C) 2022-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. @@ -16,16 +16,10 @@ package com.hedera.gradle.utils -import org.gradle.api.Task -import org.gradle.api.file.Directory -import org.gradle.api.file.RegularFile -import org.gradle.api.tasks.testing.TestDescriptor -import org.gradle.api.tasks.testing.TestListener -import org.gradle.api.tasks.testing.TestResult import java.io.OutputStream import java.io.PrintStream -import java.text.SimpleDateFormat -import java.util.Date +import org.gradle.api.file.Directory +import org.gradle.api.file.RegularFile object Utils { // Find the version.txt in the root of the repository, independent of diff --git a/hapi/build.gradle.kts b/hapi/build.gradle.kts index 70b0fb875f4f..1844d5a7cdf5 100644 --- a/hapi/build.gradle.kts +++ b/hapi/build.gradle.kts @@ -30,10 +30,9 @@ tasks.withType().configureEach { options.compilerArgs.add("-Xlint:- // Add downloaded HAPI repo protobuf files into build directory and add to sources to build them tasks.cloneHederaProtobufs { // uncomment below to use a specific tag - tag = "v0.51.0" - + // tag = "v0.51.0" // uncomment below to use a specific branch - // branch = "main" + branch = "main" } sourceSets { diff --git a/hapi/src/main/java/com/hedera/hapi/util/HapiUtils.java b/hapi/src/main/java/com/hedera/hapi/util/HapiUtils.java index ad3a40474aaf..3587c5685f9e 100644 --- a/hapi/src/main/java/com/hedera/hapi/util/HapiUtils.java +++ b/hapi/src/main/java/com/hedera/hapi/util/HapiUtils.java @@ -97,15 +97,6 @@ public class HapiUtils { public static final Comparator TIMESTAMP_COMPARATOR = Comparator.comparingLong(Timestamp::seconds).thenComparingInt(Timestamp::nanos); - /** A {@link Comparator} for {@link SemanticVersion}s that ignores - * any semver part that cannot be parsed as an integer. */ - public static final Comparator SEMANTIC_VERSION_COMPARATOR = Comparator.comparingInt( - SemanticVersion::major) - .thenComparingInt(SemanticVersion::minor) - .thenComparingInt(SemanticVersion::patch) - .thenComparingInt(semVer -> parsedIntOrZero(semVer.pre())) - .thenComparingInt(semVer -> parsedIntOrZero(semVer.build())); - private HapiUtils() {} /** @@ -187,7 +178,8 @@ public static int countOfCryptographicKeys(@NonNull final Key key) { HederaFunctionality.TOKEN_GET_NFT_INFOS, HederaFunctionality.TOKEN_GET_ACCOUNT_NFT_INFOS, HederaFunctionality.NETWORK_GET_EXECUTION_TIME, - HederaFunctionality.GET_ACCOUNT_DETAILS); + HederaFunctionality.GET_ACCOUNT_DETAILS, + HederaFunctionality.NODE_GET_INFO); public static HederaFunctionality functionOf(final TransactionBody txn) throws UnknownHederaFunctionality { return switch (txn.data().kind()) { @@ -237,6 +229,9 @@ public static HederaFunctionality functionOf(final TransactionBody txn) throws U case TOKEN_WIPE -> HederaFunctionality.TOKEN_ACCOUNT_WIPE; case UTIL_PRNG -> HederaFunctionality.UTIL_PRNG; case UNCHECKED_SUBMIT -> HederaFunctionality.UNCHECKED_SUBMIT; + case NODE_CREATE -> HederaFunctionality.NODE_CREATE; + case NODE_UPDATE -> HederaFunctionality.NODE_UPDATE; + case NODE_DELETE -> HederaFunctionality.NODE_DELETE; case UNSET -> throw new UnknownHederaFunctionality(); }; } @@ -268,6 +263,7 @@ public static HederaFunctionality functionOf(final Query txn) throws UnknownHede case TRANSACTION_GET_RECEIPT -> HederaFunctionality.TRANSACTION_GET_RECEIPT; case TRANSACTION_GET_RECORD -> HederaFunctionality.TRANSACTION_GET_RECORD; case TRANSACTION_GET_FAST_RECORD -> HederaFunctionality.TRANSACTION_GET_FAST_RECORD; + case NODE_GET_INFO -> HederaFunctionality.NODE_GET_INFO; case UNSET -> throw new UnknownHederaFunctionality(); }; } diff --git a/hapi/src/main/java/module-info.java b/hapi/src/main/java/module-info.java index 335c55d52a1d..b2e12c954e49 100644 --- a/hapi/src/main/java/module-info.java +++ b/hapi/src/main/java/module-info.java @@ -32,6 +32,9 @@ exports com.hedera.hapi.streams; exports com.hedera.hapi.streams.codec; exports com.hedera.hapi.streams.schema; + exports com.hedera.hapi.node.addressbook; + exports com.hedera.hapi.node.state.addressbook.codec; + exports com.hedera.hapi.node.state.addressbook; exports com.hedera.hapi.node.state.consensus.codec; exports com.hedera.hapi.node.state.consensus; exports com.hedera.hapi.node.state.token; diff --git a/hedera-node/cli-clients/src/main/java/com/hedera/services/cli/contracts/assembly/Opcodes.java b/hedera-node/cli-clients/src/main/java/com/hedera/services/cli/contracts/assembly/Opcodes.java index ff5dc3a00273..778097163162 100644 --- a/hedera-node/cli-clients/src/main/java/com/hedera/services/cli/contracts/assembly/Opcodes.java +++ b/hedera-node/cli-clients/src/main/java/com/hedera/services/cli/contracts/assembly/Opcodes.java @@ -279,7 +279,7 @@ public String toString() { byMnemonic = ImmutableSortedMap.copyOf(descrs.stream().collect(toMap(Descr::mnemonic, d -> d))); } - /** Returns a Stream of a range of integers, inclusive of both ends */ + /** Returns a Stream<Integer> of a range of integers, inclusive of both ends */ private static Stream intRange(int low, int high) { return IntStream.rangeClosed(low, high).boxed(); } diff --git a/hedera-node/cli-clients/src/main/java/com/hedera/services/cli/signedstate/DumpContractStoresSubcommand.java b/hedera-node/cli-clients/src/main/java/com/hedera/services/cli/signedstate/DumpContractStoresSubcommand.java index 545b0f69bdf9..a90a053dbf5b 100644 --- a/hedera-node/cli-clients/src/main/java/com/hedera/services/cli/signedstate/DumpContractStoresSubcommand.java +++ b/hedera-node/cli-clients/src/main/java/com/hedera/services/cli/signedstate/DumpContractStoresSubcommand.java @@ -28,7 +28,6 @@ import com.hedera.node.app.service.mono.state.migration.ContractStateMigrator; import com.hedera.node.app.service.mono.state.virtual.ContractKey; import com.hedera.node.app.service.mono.utils.NonAtomicReference; -import com.hedera.node.app.spi.state.StateDefinition; import com.hedera.node.app.state.merkle.StateMetadata; import com.hedera.services.cli.signedstate.DumpStateCommand.EmitSummary; import com.hedera.services.cli.signedstate.DumpStateCommand.WithMigration; @@ -40,6 +39,7 @@ import com.swirlds.platform.state.merkle.memory.InMemoryValue; import com.swirlds.platform.state.merkle.memory.InMemoryWritableKVState; import com.swirlds.platform.state.spi.WritableKVStateBase; +import com.swirlds.state.spi.StateDefinition; import com.swirlds.state.spi.WritableKVState; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; diff --git a/hedera-node/cli-clients/src/main/java/module-info.java b/hedera-node/cli-clients/src/main/java/module-info.java index 452e77df3506..38683d5c0cbe 100644 --- a/hedera-node/cli-clients/src/main/java/module-info.java +++ b/hedera-node/cli-clients/src/main/java/module-info.java @@ -10,13 +10,8 @@ requires com.hedera.node.app.hapi.utils; requires com.hedera.node.app.service.contract.impl; requires com.hedera.node.app.service.contract; - requires com.hedera.node.app.spi; requires com.hedera.node.app; requires com.hedera.node.hapi; - requires com.google.common; - requires com.google.protobuf; - requires com.hedera.evm; - requires com.hedera.pbj.runtime; requires com.swirlds.base; requires com.swirlds.config.api; requires com.swirlds.config.extensions; @@ -24,6 +19,10 @@ requires com.swirlds.merkle; requires com.swirlds.state.api; requires com.swirlds.virtualmap; + requires com.google.common; + requires com.google.protobuf; + requires com.hedera.evm; + requires com.hedera.pbj.runtime; requires io.github.classgraph; requires org.apache.commons.lang3; requires org.apache.logging.log4j.core; diff --git a/hedera-node/configuration/mainnet/application.properties b/hedera-node/configuration/mainnet/application.properties index f4a1eac9185f..4971c147fc44 100644 --- a/hedera-node/configuration/mainnet/application.properties +++ b/hedera-node/configuration/mainnet/application.properties @@ -3,3 +3,4 @@ # hedera-config), this requirement will no longer be valid. # It's used by modular code for property overrides, taking hedera-config/ as the base, # with overrides from this file (configuration/mainnet/application.properties). +ledger.id=0x00 \ No newline at end of file diff --git a/hedera-node/docs/design/service-modules.md b/hedera-node/docs/design/service-modules.md index e0120167b498..9c013600ae47 100644 --- a/hedera-node/docs/design/service-modules.md +++ b/hedera-node/docs/design/service-modules.md @@ -69,12 +69,12 @@ pattern that defines the entry point directly as a static method in the service ``` package com.hedera.node.app.service.foo; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.ServiceFactory; +import com.hedera.node.app.spi.RpcService; +import com.hedera.node.app.spi.RpcServiceFactory; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ServiceLoader; -public interface FooService extends Service { +public interface FooService extends RpcService { @NonNull @Override diff --git a/hedera-node/docs/design/services/token-service/airdrops/token-claim-airdrop.md b/hedera-node/docs/design/services/token-service/airdrops/token-claim-airdrop.md index 80d7f3b3e45a..1f3404ce75c6 100644 --- a/hedera-node/docs/design/services/token-service/airdrops/token-claim-airdrop.md +++ b/hedera-node/docs/design/services/token-service/airdrops/token-claim-airdrop.md @@ -113,6 +113,7 @@ An update into the `feeSchedule` file would be needed to specify that. ### Services updates +- Update `ApiPermissionConfig` class to include a `0-* PermissionedAccountsRange` for the new `TokenClaimAirdrop` transaction type - Update `TokenServiceDefinition` class to include the new RPC method definition for claiming airdrops - Implement new `TokenClaimAirdropHandler` class which should be invoked when the gRPC server handles `TokenClaimAirdrop` transactions. The class should be responsible for: - Pure checks: validation logic based only on the transaction body itself in order to verify if the transaction is valid one @@ -120,23 +121,27 @@ An update into the `feeSchedule` file would be needed to specify that. - Verify that the pending airdrops list does not have any duplicate entries - Pre-handle: - The transaction must be signed by the account referenced by a `receiver_id` for each entry in the pending airdrops list - - Confirm that for the given pending airdrops ids in the transaction there are corresponding pending transfers existing in state - - Check if the sender has sufficient balance to fulfill the airdrop - Handle: + - Confirm that for the given pending airdrops ids in the transaction there are corresponding pending transfers existing in state + - Check if the sender has sufficient amount or has enough approved allowance of the tokens being claimed to fulfill the airdrop + - Check if the token is not frozen, paused, or deleted + - If the token is frozen or paused the claim transaction should fail + - For deleted tokens, the claim transaction should fail, but also we should remove the pending airdrop from state - Any additional validation depending on config or state i.e. semantics checks - The business logic for claiming pending airdrops - - We need to create a token association between each `receiver_id` and `token_reference`, future rents for token association slot should be paid by `receiver_id` + - If token association between each `receiver_id` and `token_reference` does not exist, we need to create it; future rents for token association slot should be paid by `receiver_id` - Since we would have the signature of the receiver, even if it's an account with `receiver_sig_required=true`, the claim would implicitly work properly + - The token association is free at this point because the sender already paid for it when submitting the `TokenAirdrop` transaction - Then we should transfer the claimed tokens to each `receiver_id` - - We can dispatch synthetic crypto transfer for this, but we must skip the assessment of custom fees - - In case of a fungible token claim - - Create a synthetic `CryptoTransfer` with the corresponding `sender`, `receiver`, `token` and `amount` based on the `PendingAirdropId` and the corresponding `PendingAirdropValue` - - Then delegate it to the `CryptoTransfer.handler()` to execute the transfer - - In case of an NFT claim - - Create a synthetic `CryptoTransfer` with the corresponding `sender`, `receiver`, `token` and `serial number` based on the based on the `PendingAirdropId` - - Then delegate it to `CryptoTransfer.handler()` to execute the transfer - - Token transfers and associations should be externalized using the `tokenTransferLists` and `automatic_token_associations` fields in the transaction record + - Reuse any existing logic from `CryptoTransferHandler`, extracting common code into a separate class + - We must skip the assessment of custom fees + - Token transfers should be externalized using the `tokenTransferLists` field in the transaction record - Fees calculation +- Update throttle definitions to include the new `TokenClaimAirdrop` transaction type + - Throttle definitions are specified in `throttles.json` files + - There are different configurations containing throttle definitions under `hedera-node/configuration/` for the different environments e.g. testnet, previewnet, mainnet + - There is also a default throttle definition file in `resources/genesis/throttles.json` that is used during the genesis + - Add the new `TokenClaimAirdrop` transaction type to the `ThroughputLimits` bucket ### Zero-Balance accounts @@ -155,11 +160,15 @@ All of the expected behaviour described below should be present only if the new - the tokens being claimed should be automatically associated with the `receiver_id` account - the tokens being claimed should be transferred to the `receiver_id` account - the pending airdrop should be removed from state -- Given a successful `TokenClaimAirdrop` transaction having a hollow account as `receiver_id` should also complete the account without modifying its `maxAutoAssociations` value + - the transaction record should contain the transferred tokens in `tokenTransferLists` field +- Given existing pending airdrop in state when valid `TokenClaimAirdrop` transaction containing entry for the same pending airdrop is performed and the token in the pending airdrop is frozen or paused then the `TokenClaimAirdrop` should fail without modifying the pending airdrop state +- Given existing pending airdrop in state when valid `TokenClaimAirdrop` transaction containing entry for the same pending airdrop is performed and the token in the pending airdrop is deleted then the `TokenClaimAirdrop` should fail and the pending airdrop should be removed from state +- Given a successful `TokenClaimAirdrop` transaction having a hollow account as `receiver_id` should also complete the account without modifying its `maxAutoAssociations` value - Given successful `TokenClaimAirdrop` when another `TokenClaimAirdrop` for the same airdrop is performed then the second `TokenClaimAirdrop` should fail - `TokenClaimAirdrop` transaction with no pending airdrops entries should fail - `TokenClaimAirdrop` transaction with more than 10 pending airdrops entries should fail - `TokenClaimAirdrop` transaction containing duplicate entries should fail - `TokenClaimAirdrop` transaction containing pending airdrops entries which do not exist in state should fail - `TokenClaimAirdrop` transaction not signed by the account referenced by a `receiver_id` for each entry in the pending airdrops list should fail -- `TokenClaimAirdrop` transaction with a `sender_id` account that does not have sufficient balance of the claimed token should fail +- `TokenClaimAirdrop` transaction with a `sender_id` account that does not have sufficient balance or not enough allowance of the claimed token should fail +- Given the feature flag for `TokenClaimAirdrop` is disabled then any `TokenClaimAirdrop` transaction should fail with `NOT_SUPPORTED` diff --git a/hedera-node/hapi-utils/src/main/java/com/hedera/node/app/hapi/utils/CommonUtils.java b/hedera-node/hapi-utils/src/main/java/com/hedera/node/app/hapi/utils/CommonUtils.java index 931f7c1e377e..cbdcc3aafb08 100644 --- a/hedera-node/hapi-utils/src/main/java/com/hedera/node/app/hapi/utils/CommonUtils.java +++ b/hedera-node/hapi-utils/src/main/java/com/hedera/node/app/hapi/utils/CommonUtils.java @@ -108,6 +108,21 @@ public static byte[] extractTransactionBodyBytes(final TransactionOrBuilder tran return unwrapUnsafelyIfPossible(extractTransactionBodyByteString(transaction)); } + /** + * Extracts the {@link TransactionBody} from a {@link TransactionOrBuilder} and throws an unchecked exception if + * the extraction fails. + * + * @param transaction the {@link TransactionOrBuilder} from which to extract the {@link TransactionBody} + * @return the extracted {@link TransactionBody} + */ + public static TransactionBody extractTransactionBodyUnchecked(final TransactionOrBuilder transaction) { + try { + return TransactionBody.parseFrom(extractTransactionBodyByteString(transaction)); + } catch (InvalidProtocolBufferException e) { + throw new IllegalArgumentException(e); + } + } + public static TransactionBody extractTransactionBody(final TransactionOrBuilder transaction) throws InvalidProtocolBufferException { return TransactionBody.parseFrom(extractTransactionBodyByteString(transaction)); diff --git a/hedera-node/hapi-utils/src/main/java/module-info.java b/hedera-node/hapi-utils/src/main/java/module-info.java index eb99fb9fd321..cee0a14f7a78 100644 --- a/hedera-node/hapi-utils/src/main/java/module-info.java +++ b/hedera-node/hapi-utils/src/main/java/module-info.java @@ -17,18 +17,18 @@ exports com.hedera.node.app.hapi.utils.sysfiles.validation; requires transitive com.hedera.node.hapi; - requires transitive com.google.protobuf; requires transitive com.swirlds.common; + requires transitive com.google.protobuf; requires transitive dagger; requires transitive headlong; requires transitive javax.inject; requires transitive net.i2p.crypto.eddsa; requires transitive org.apache.commons.lang3; + requires com.swirlds.base; requires com.fasterxml.jackson.databind; requires com.google.common; requires com.hedera.evm; requires com.sun.jna; - requires com.swirlds.base; requires org.apache.commons.codec; requires org.apache.logging.log4j.core; requires org.apache.logging.log4j; diff --git a/hedera-node/hapi-utils/src/test/java/com/hedera/node/app/hapi/utils/throttles/DeterministicThrottleTest.java b/hedera-node/hapi-utils/src/test/java/com/hedera/node/app/hapi/utils/throttles/DeterministicThrottleTest.java index f8f76d9c2deb..3394d113a5e6 100644 --- a/hedera-node/hapi-utils/src/test/java/com/hedera/node/app/hapi/utils/throttles/DeterministicThrottleTest.java +++ b/hedera-node/hapi-utils/src/test/java/com/hedera/node/app/hapi/utils/throttles/DeterministicThrottleTest.java @@ -31,6 +31,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.time.Instant; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; class DeterministicThrottleTest { @@ -142,6 +143,7 @@ void canGetInstantaneousPercentUsed() { } @Test + @Disabled("Test is flaky. See https://github.com/hashgraph/hedera-services/issues/13667") void throttlesWithinPermissibleTolerance() throws InterruptedException { final long mtps = 123_456L; final var subject = DeterministicThrottle.withMtps(mtps); diff --git a/hedera-node/hedera-addressbook-service-impl/build.gradle.kts b/hedera-node/hedera-addressbook-service-impl/build.gradle.kts new file mode 100644 index 000000000000..6953be2c662d --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/build.gradle.kts @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020-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. + */ + +plugins { + id("com.hedera.gradle.services") + id("com.hedera.gradle.services-publish") +} + +description = "Default Hedera AddressBook Service Implementation" + +// Remove the following line to enable all 'javac' lint checks that we have turned on by default +// and then fix the reported issues. +tasks.withType().configureEach { options.compilerArgs.add("-Xlint:-exports,-static") } + +mainModuleInfo { annotationProcessor("dagger.compiler") } + +testModuleInfo { + requires("com.hedera.node.app.service.addressbook.impl") + requires("com.hedera.node.app.service.mono") + requires("com.hedera.node.config.test.fixtures") + requires("com.swirlds.config.extensions.test.fixtures") + requires("com.swirlds.common") + requires("com.swirlds.platform.core.test.fixtures") + requires("org.assertj.core") + requires("org.junit.jupiter.api") + requires("org.mockito") + requires("org.mockito.junit.jupiter") + requiresStatic("com.github.spotbugs.annotations") +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/AddressBookServiceImpl.java b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/AddressBookServiceImpl.java new file mode 100644 index 000000000000..077353cf827e --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/AddressBookServiceImpl.java @@ -0,0 +1,35 @@ +/* + * 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.service.addressbook.impl; + +import com.hedera.node.app.service.addressbook.AddressBookService; +import com.hedera.node.app.service.addressbook.impl.schemas.V050AddressBookSchema; +import com.hedera.node.app.spi.RpcService; +import com.swirlds.state.spi.SchemaRegistry; +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Standard implementation of the {@link AddressBookService} {@link RpcService}. + */ +public final class AddressBookServiceImpl implements AddressBookService { + public static final String NODES_KEY = "NODES"; + + @Override + public void registerSchemas(@NonNull SchemaRegistry registry) { + registry.register(new V050AddressBookSchema()); + } +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/AddressBookServiceInjectionModule.java b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/AddressBookServiceInjectionModule.java new file mode 100644 index 000000000000..70ecd4456e59 --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/AddressBookServiceInjectionModule.java @@ -0,0 +1,41 @@ +/* + * 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.service.addressbook.impl; + +import com.hedera.node.app.service.addressbook.impl.handlers.AddressBookHandlers; +import com.hedera.node.app.service.addressbook.impl.handlers.NodeCreateHandler; +import com.hedera.node.app.service.addressbook.impl.handlers.NodeDeleteHandler; +import com.hedera.node.app.service.addressbook.impl.handlers.NodeGetInfoHandler; +import com.hedera.node.app.service.addressbook.impl.handlers.NodeUpdateHandler; +import dagger.Module; + +/** + * Dagger module for the addressbook service. + */ +@Module +public interface AddressBookServiceInjectionModule { + + NodeCreateHandler nodeCreateHandler(); + + NodeDeleteHandler nodeDeleteHandler(); + + NodeGetInfoHandler nodeGetInfoHandler(); + + NodeUpdateHandler nodeUpdateHandler(); + + AddressBookHandlers addressBookHandlers(); +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/ReadableNodeStoreImpl.java b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/ReadableNodeStoreImpl.java new file mode 100644 index 000000000000..ab488c8ad319 --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/ReadableNodeStoreImpl.java @@ -0,0 +1,74 @@ +/* + * 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.service.addressbook.impl; + +import static com.hedera.node.app.service.addressbook.impl.AddressBookServiceImpl.NODES_KEY; +import static java.util.Objects.requireNonNull; + +import com.hedera.hapi.node.state.addressbook.Node; +import com.hedera.hapi.node.state.common.EntityNumber; +import com.hedera.node.app.service.addressbook.ReadableNodeStore; +import com.swirlds.state.spi.ReadableKVState; +import com.swirlds.state.spi.ReadableStates; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +/** + * Provides read-only methods for interacting with the underlying data storage mechanisms for + * working with Nodes. + * + *

This class is not exported from the module. It is an internal implementation detail. + */ +public class ReadableNodeStoreImpl implements ReadableNodeStore { + /** The underlying data storage class that holds the node data. */ + private final ReadableKVState nodesState; + + /** + * Create a new {@link ReadableNodeStoreImpl} instance. + * + * @param states The state to use. + */ + public ReadableNodeStoreImpl(@NonNull final ReadableStates states) { + requireNonNull(states); + + this.nodesState = states.get(NODES_KEY); + } + + /** + * Returns the node needed. + * + * @param nodeId node id being looked up + * @return node + */ + @Override + @Nullable + public Node get(final long nodeId) { + return nodesState.get(EntityNumber.newBuilder().number(nodeId).build()); + } + + /** + * Returns the number of topics in the state. + * @return the number of topics in the state. + */ + public long sizeOfState() { + return nodesState.size(); + } + + protected > T nodesState() { + return (T) nodesState; + } +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/WritableNodeStore.java b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/WritableNodeStore.java new file mode 100644 index 000000000000..848d37235d6e --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/WritableNodeStore.java @@ -0,0 +1,109 @@ +/* + * 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.service.addressbook.impl; + +import static java.util.Objects.requireNonNull; + +import com.hedera.hapi.node.state.addressbook.Node; +import com.hedera.hapi.node.state.common.EntityNumber; +import com.hedera.node.app.spi.metrics.StoreMetricsService; +import com.hedera.node.app.spi.metrics.StoreMetricsService.StoreType; +import com.hedera.node.config.data.NodesConfig; +import com.swirlds.config.api.Configuration; +import com.swirlds.state.spi.WritableKVState; +import com.swirlds.state.spi.WritableStates; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Iterator; +import java.util.Set; + +/** + * Provides write methods for modifying underlying data storage mechanisms for + * working with Nodes. + * + *

This class is not exported from the module. It is an internal implementation detail. + * This class is not complete, it will be extended with other methods like remove, update etc., + */ +public class WritableNodeStore extends ReadableNodeStoreImpl { + /** + * Create a new {@link WritableNodeStore} instance. + * + * @param states The state to use. + * @param configuration The configuration used to read the maximum capacity. + * @param storeMetricsService Service that provides utilization metrics. + */ + public WritableNodeStore( + @NonNull final WritableStates states, + @NonNull final Configuration configuration, + @NonNull final StoreMetricsService storeMetricsService) { + super(states); + + final long maxCapacity = configuration.getConfigData(NodesConfig.class).maxNumber(); + final var storeMetrics = storeMetricsService.get(StoreType.NODE, maxCapacity); + nodesState().setMetrics(storeMetrics); + } + + @Override + protected WritableKVState nodesState() { + return super.nodesState(); + } + + /** + * Persists a new {@link Node} into the state, as well as exporting its ID to the transaction + * receipt. + * + * @param node - the node to be mapped onto a new {@link Node} + */ + public void put(@NonNull final Node node) { + requireNonNull(node); + nodesState().put(EntityNumber.newBuilder().number(node.nodeId()).build(), node); + } + + /** + * Returns the {@link Node} with the given number using {@link WritableKVState#getForModify}. + * If no such node exists, returns {@code Optional.empty()} + * @param nodeId - the id of the node to be retrieved. + */ + public Node getForModify(final long nodeId) { + return nodesState() + .getForModify(EntityNumber.newBuilder().number(nodeId).build()); + } + + /** + * Returns the number of nodes in the state. + * @return the number of nodes in the state. + */ + @Override + public long sizeOfState() { + return nodesState().size(); + } + + /** + * Returns the set of nodes modified in existing state. + * @return the set of nodes modified in existing state + */ + public Set modifiedNodes() { + return nodesState().modifiedKeys(); + } + + /** + * Returns an iterator over the keys in the state. + * @return + */ + public Iterator keys() { + return nodesState().keys(); + } +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/handlers/AddressBookHandlers.java b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/handlers/AddressBookHandlers.java new file mode 100644 index 000000000000..93f42e55ff7b --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/handlers/AddressBookHandlers.java @@ -0,0 +1,88 @@ +/* + * 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.service.addressbook.impl.handlers; + +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Objects; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Class to hold all the addressbook handlers + */ +@Singleton +public class AddressBookHandlers { + + private final NodeCreateHandler nodeCreateHandler; + + private final NodeDeleteHandler nodeDeleteHandler; + + private final NodeGetInfoHandler nodeGetInfoHandler; + + private final NodeUpdateHandler nodeUpdateHandler; + + /** + * Constructor for AddressBookHandlers + */ + @Inject + public AddressBookHandlers( + @NonNull final NodeCreateHandler nodeCreateHandler, + @NonNull final NodeDeleteHandler nodeDeleteHandler, + @NonNull final NodeGetInfoHandler nodeGetInfoHandler, + @NonNull final NodeUpdateHandler nodeUpdateHandler) { + this.nodeCreateHandler = Objects.requireNonNull(nodeCreateHandler, "nodeCreateHandler must not be null"); + this.nodeDeleteHandler = Objects.requireNonNull(nodeDeleteHandler, "nodeDeleteHandler must not be null"); + this.nodeGetInfoHandler = Objects.requireNonNull(nodeGetInfoHandler, "nodeGetInfoHandler must not be null"); + this.nodeUpdateHandler = Objects.requireNonNull(nodeUpdateHandler, "nodeUpdateHandler must not be null"); + } + + /** + * Get the nodeCreateHandler + * + * @return the nodeCreateHandler + */ + public NodeCreateHandler nodeCreateHandler() { + return nodeCreateHandler; + } + + /** + * Get the nodeDeleteHandler + * + * @return the nodeDeleteHandler + */ + public NodeDeleteHandler nodeDeleteHandler() { + return nodeDeleteHandler; + } + + /** + * Get the nodeGetInfoHandler + * + * @return the nodeGetInfoHandler + */ + public NodeGetInfoHandler nodeGetInfoHandler() { + return nodeGetInfoHandler; + } + + /** + * Get the nodeUpdateHandler + * + * @return the nodeUpdateHandler + */ + public NodeUpdateHandler nodeUpdateHandler() { + return nodeUpdateHandler; + } +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/handlers/NodeCreateHandler.java b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/handlers/NodeCreateHandler.java new file mode 100644 index 000000000000..860eab7bdcaa --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/handlers/NodeCreateHandler.java @@ -0,0 +1,126 @@ +/* + * 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.service.addressbook.impl.handlers; + +import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_GOSSIP_CAE_CERTIFICATE; +import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_GOSSIP_ENDPOINT; +import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_NODE_ACCOUNT_ID; +import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_SERVICE_ENDPOINT; +import static com.hedera.hapi.node.base.ResponseCodeEnum.MAX_NODES_CREATED; +import static com.hedera.node.app.spi.validation.Validations.validateAccountID; +import static com.hedera.node.app.spi.workflows.HandleException.validateFalse; +import static com.hedera.node.app.spi.workflows.HandleException.validateTrue; +import static com.hedera.node.app.spi.workflows.PreCheckException.validateFalsePreCheck; +import static java.util.Objects.requireNonNull; + +import com.hedera.hapi.node.base.AccountID; +import com.hedera.hapi.node.base.HederaFunctionality; +import com.hedera.hapi.node.state.addressbook.Node; +import com.hedera.hapi.node.transaction.TransactionBody; +import com.hedera.node.app.service.addressbook.impl.WritableNodeStore; +import com.hedera.node.app.service.addressbook.impl.records.NodeCreateRecordBuilder; +import com.hedera.node.app.service.addressbook.impl.validators.AddressBookValidator; +import com.hedera.node.app.service.token.ReadableAccountStore; +import com.hedera.node.app.spi.workflows.HandleContext; +import com.hedera.node.app.spi.workflows.PreCheckException; +import com.hedera.node.app.spi.workflows.PreHandleContext; +import com.hedera.node.app.spi.workflows.TransactionHandler; +import com.hedera.node.config.data.NodesConfig; +import com.hedera.pbj.runtime.io.buffer.Bytes; +import edu.umd.cs.findbugs.annotations.NonNull; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * This class contains all workflow-related functionality regarding {@link HederaFunctionality#NODE_CREATE}. + * This is a privileged(Needs signatures from 2-50 ) + */ +@Singleton +public class NodeCreateHandler implements TransactionHandler { + private final AddressBookValidator addressBookValidator; + + /** + * Constructs a {@link NodeCreateHandler} with the given {@link AddressBookValidator}. + * @param addressBookValidator the validator for the crypto create transaction + */ + @Inject + public NodeCreateHandler(@NonNull final AddressBookValidator addressBookValidator) { + this.addressBookValidator = + requireNonNull(addressBookValidator, "The supplied argument 'addressBookValidator' must not be null"); + } + + @Override + public void pureChecks(@NonNull final TransactionBody txn) throws PreCheckException { + final var op = txn.nodeCreate(); + final var accountId = validateAccountID(op.accountIdOrElse(AccountID.DEFAULT), INVALID_NODE_ACCOUNT_ID); + validateFalsePreCheck(!accountId.hasAccountNum() && accountId.hasAlias(), INVALID_NODE_ACCOUNT_ID); + validateFalsePreCheck(op.gossipEndpoint().isEmpty(), INVALID_GOSSIP_ENDPOINT); + validateFalsePreCheck(op.serviceEndpoint().isEmpty(), INVALID_SERVICE_ENDPOINT); + validateFalsePreCheck(op.gossipCaCertificate().equals(Bytes.EMPTY), INVALID_GOSSIP_CAE_CERTIFICATE); + } + + @Override + public void preHandle(@NonNull final PreHandleContext context) { + requireNonNull(context); + } + + @Override + public void handle(@NonNull final HandleContext handleContext) { + requireNonNull(handleContext); + + final var op = handleContext.body().nodeCreate(); + + final var configuration = handleContext.configuration(); + final var nodeConfig = configuration.getConfigData(NodesConfig.class); + final var nodeStore = handleContext.writableStore(WritableNodeStore.class); + final var accountStore = handleContext.readableStore(ReadableAccountStore.class); + final var accountId = op.accountIdOrElse(AccountID.DEFAULT); + + validateFalse(nodeStore.sizeOfState() >= nodeConfig.maxNumber(), MAX_NODES_CREATED); + validateTrue(accountStore.contains(accountId), INVALID_NODE_ACCOUNT_ID); + addressBookValidator.validateDescription(op.description(), nodeConfig); + addressBookValidator.validateGossipEndpoint(op.gossipEndpoint(), nodeConfig); + addressBookValidator.validateServiceEndpoint(op.serviceEndpoint(), nodeConfig); + + final var nodeBuilder = new Node.Builder() + .accountId(op.accountId()) + .description(op.description()) + .gossipEndpoint(op.gossipEndpoint()) + .serviceEndpoint(op.serviceEndpoint()) + .gossipEndpoint(op.gossipEndpoint()) + .serviceEndpoint(op.serviceEndpoint()) + .gossipCaCertificate(op.gossipCaCertificate()) + .grpcCertificateHash(op.grpcCertificateHash()); + final var node = nodeBuilder.nodeId(getNextNodeID(nodeStore)).build(); + + nodeStore.put(node); + + final var recordBuilder = handleContext.recordBuilder(NodeCreateRecordBuilder.class); + + recordBuilder.nodeID(node.nodeId()); + } + + private long getNextNodeID(WritableNodeStore nodeStore) { + final var nodeIds = nodeStore.keys(); + long max = 0; + while (nodeIds.hasNext()) { + max = Math.max(max, nodeIds.next().number()); + } + + return max + 1; + } +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/handlers/NodeDeleteHandler.java b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/handlers/NodeDeleteHandler.java new file mode 100644 index 000000000000..1bfebd9adcac --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/handlers/NodeDeleteHandler.java @@ -0,0 +1,96 @@ +/* + * 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.service.addressbook.impl.handlers; + +import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_NODE_ID; +import static com.hedera.hapi.node.base.ResponseCodeEnum.NODE_DELETED; +import static com.hedera.node.app.spi.workflows.HandleException.validateFalse; +import static com.hedera.node.app.spi.workflows.PreCheckException.validateFalsePreCheck; +import static java.util.Objects.requireNonNull; + +import com.hedera.hapi.node.addressbook.NodeDeleteTransactionBody; +import com.hedera.hapi.node.base.HederaFunctionality; +import com.hedera.hapi.node.base.SubType; +import com.hedera.hapi.node.state.addressbook.Node; +import com.hedera.hapi.node.transaction.TransactionBody; +import com.hedera.node.app.service.addressbook.impl.WritableNodeStore; +import com.hedera.node.app.spi.fees.FeeContext; +import com.hedera.node.app.spi.fees.Fees; +import com.hedera.node.app.spi.workflows.HandleContext; +import com.hedera.node.app.spi.workflows.PreCheckException; +import com.hedera.node.app.spi.workflows.PreHandleContext; +import com.hedera.node.app.spi.workflows.TransactionHandler; +import edu.umd.cs.findbugs.annotations.NonNull; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * This class contains all workflow-related functionality regarding {@link HederaFunctionality#NODE_DELETE}. + */ +@Singleton +public class NodeDeleteHandler implements TransactionHandler { + @Inject + public NodeDeleteHandler() { + // Exists for injection + } + + @Override + public void pureChecks(@NonNull final TransactionBody txn) throws PreCheckException { + final NodeDeleteTransactionBody transactionBody = txn.nodeDeleteOrThrow(); + final long nodeId = transactionBody.nodeId(); + + validateFalsePreCheck(nodeId < 0, INVALID_NODE_ID); + } + + @Override + public void preHandle(@NonNull final PreHandleContext context) throws PreCheckException {} + + /** + * Given the appropriate context, deletes a node. + * + * @param context the {@link HandleContext} of the active transaction + * @throws NullPointerException if one of the arguments is {@code null} + */ + @Override + public void handle(@NonNull final HandleContext context) { + requireNonNull(context); + + final NodeDeleteTransactionBody transactionBody = context.body().nodeDeleteOrThrow(); + var nodeId = transactionBody.nodeId(); + + final var nodeStore = context.writableStore(WritableNodeStore.class); + + Node node = nodeStore.get(nodeId); + + validateFalse(node == null, INVALID_NODE_ID); + + validateFalse(node.deleted(), NODE_DELETED); + + /* Copy all the fields from existing, and mark deleted flag */ + final var nodeBuilder = node.copyBuilder().deleted(true); + + /* --- Put the modified node. It will be in underlying state's modifications map. + It will not be committed to state until commit is called on the state.--- */ + nodeStore.put(nodeBuilder.build()); + } + + @NonNull + @Override + public Fees calculateFees(@NonNull final FeeContext feeContext) { + return feeContext.feeCalculator(SubType.DEFAULT).calculate(); + } +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/handlers/NodeGetInfoHandler.java b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/handlers/NodeGetInfoHandler.java new file mode 100644 index 000000000000..e4fddddf01a8 --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/handlers/NodeGetInfoHandler.java @@ -0,0 +1,88 @@ +/* + * 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.service.addressbook.impl.handlers; + +import static java.util.Objects.requireNonNull; + +import com.hedera.hapi.node.addressbook.NodeGetInfoQuery; +import com.hedera.hapi.node.addressbook.NodeGetInfoResponse; +import com.hedera.hapi.node.base.HederaFunctionality; +import com.hedera.hapi.node.base.QueryHeader; +import com.hedera.hapi.node.base.ResponseHeader; +import com.hedera.hapi.node.transaction.Query; +import com.hedera.hapi.node.transaction.Response; +import com.hedera.node.app.service.addressbook.ReadableNodeStore; +import com.hedera.node.app.spi.fees.Fees; +import com.hedera.node.app.spi.workflows.PaidQueryHandler; +import com.hedera.node.app.spi.workflows.PreCheckException; +import com.hedera.node.app.spi.workflows.QueryContext; +import com.hedera.node.config.data.LedgerConfig; +import edu.umd.cs.findbugs.annotations.NonNull; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * This class contains all workflow-related functionality regarding {@link HederaFunctionality#NODE_GET_INFO}. + */ +@Singleton +public class NodeGetInfoHandler extends PaidQueryHandler { + + @Inject + public NodeGetInfoHandler() { + // Dagger 2 + } + + @Override + public QueryHeader extractHeader(@NonNull final Query query) { + requireNonNull(query); + return query.nodeGetInfoOrThrow().header(); + } + + @Override + public Response createEmptyResponse(@NonNull final ResponseHeader header) { + requireNonNull(header); + final var response = NodeGetInfoResponse.newBuilder().header(header); + return Response.newBuilder().nodeGetInfo(response).build(); + } + + @Override + public void validate(@NonNull final QueryContext context) throws PreCheckException { + requireNonNull(context); + final var query = context.query(); + final var store = context.createStore(ReadableNodeStore.class); + final NodeGetInfoQuery op = query.nodeGetInfoOrThrow(); + throw new UnsupportedOperationException("need implementation"); + } + + @Override + public Response findResponse(@NonNull final QueryContext context, @NonNull final ResponseHeader header) { + requireNonNull(context); + requireNonNull(header); + final var query = context.query(); + final var config = context.configuration().getConfigData(LedgerConfig.class); + throw new UnsupportedOperationException("need implementation"); + } + + @NonNull + @Override + public Fees computeFees(@NonNull QueryContext queryContext) { + final var query = queryContext.query(); + final var nodeStore = queryContext.createStore(ReadableNodeStore.class); + final var op = query.nodeGetInfoOrThrow(); + throw new UnsupportedOperationException("need implementation"); + } +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/handlers/NodeUpdateHandler.java b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/handlers/NodeUpdateHandler.java new file mode 100644 index 000000000000..953e9bdd4875 --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/handlers/NodeUpdateHandler.java @@ -0,0 +1,82 @@ +/* + * 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.service.addressbook.impl.handlers; + +import static java.util.Objects.requireNonNull; + +import com.hedera.hapi.node.base.HederaFunctionality; +import com.hedera.hapi.node.transaction.TransactionBody; +import com.hedera.node.app.service.addressbook.impl.WritableNodeStore; +import com.hedera.node.app.spi.fees.FeeContext; +import com.hedera.node.app.spi.fees.Fees; +import com.hedera.node.app.spi.workflows.HandleContext; +import com.hedera.node.app.spi.workflows.PreCheckException; +import com.hedera.node.app.spi.workflows.PreHandleContext; +import com.hedera.node.app.spi.workflows.TransactionHandler; +import edu.umd.cs.findbugs.annotations.NonNull; +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * This class contains all workflow-related functionality regarding {@link HederaFunctionality#NODE_UPDATE}. + */ +@Singleton +public class NodeUpdateHandler implements TransactionHandler { + + @Inject + public NodeUpdateHandler() { + // Exists for injection + } + + @Override + public void pureChecks(@NonNull final TransactionBody txn) throws PreCheckException { + final var op = txn.nodeUpdate(); + throw new UnsupportedOperationException("need implementation"); + } + + @Override + public void preHandle(@NonNull final PreHandleContext context) throws PreCheckException { + requireNonNull(context); + final var op = context.body().nodeUpdate(); + throw new UnsupportedOperationException("need implementation"); + } + + /** + * Given the appropriate context, updates a topic. + * + * @param handleContext the {@link HandleContext} for the active transaction + * @throws NullPointerException if one of the arguments is {@code null} + */ + @Override + public void handle(@NonNull final HandleContext handleContext) { + requireNonNull(handleContext); + + final var txn = handleContext.body(); + final var op = txn.nodeUpdate(); + + final var nodeStore = handleContext.writableStore(WritableNodeStore.class); + throw new UnsupportedOperationException("need implementation"); + } + + @NonNull + @Override + public Fees calculateFees(@NonNull final FeeContext feeContext) { + requireNonNull(feeContext); + final var op = feeContext.body(); + throw new UnsupportedOperationException("need implementation"); + } +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/package-info.java b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/package-info.java new file mode 100644 index 000000000000..bb178333b5ef --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/package-info.java @@ -0,0 +1,5 @@ +/** + * This package is the base package of the default addressbook service implementation. This module + * must not contain any sources outside this base package + */ +package com.hedera.node.app.service.addressbook.impl; diff --git a/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/records/NodeCreateRecordBuilder.java b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/records/NodeCreateRecordBuilder.java new file mode 100644 index 000000000000..cfb470759d5f --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/records/NodeCreateRecordBuilder.java @@ -0,0 +1,33 @@ +/* + * 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.service.addressbook.impl.records; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * A {@code RecordBuilder} specialization for tracking the side-effects of a {@code NodeCreate} transaction. + */ +public interface NodeCreateRecordBuilder { + /** + * Tracks creation of a new node by {@link nodeID}. + * + * @param nodeID the new node + * @return this builder + */ + @NonNull + NodeCreateRecordBuilder nodeID(long nodeID); +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/schemas/V050AddressBookSchema.java b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/schemas/V050AddressBookSchema.java new file mode 100644 index 000000000000..86cba4a8d366 --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/schemas/V050AddressBookSchema.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2020-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.service.addressbook.impl.schemas; + +import static com.hedera.node.app.service.addressbook.impl.AddressBookServiceImpl.NODES_KEY; + +import com.hedera.hapi.node.base.SemanticVersion; +import com.hedera.hapi.node.state.addressbook.Node; +import com.hedera.hapi.node.state.common.EntityNumber; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.StateDefinition; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Set; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * General schema for the addressbook service + * (FUTURE) When mod-service release is finalized, rename this class to e.g. + * {@code Release47addressbookSchema} as it will no longer be appropriate to assume + * this schema is always correct for the current version of the software. + */ +public class V050AddressBookSchema extends Schema { + private static final Logger log = LogManager.getLogger(V050AddressBookSchema.class); + + private static final long MAX_NODES = 100L; + + private static final SemanticVersion VERSION = + SemanticVersion.newBuilder().major(0).minor(50).patch(0).build(); + + public V050AddressBookSchema() { + super(VERSION); + } + + @NonNull + @Override + public Set statesToCreate() { + return Set.of(StateDefinition.onDisk(NODES_KEY, EntityNumber.PROTOBUF, Node.PROTOBUF, MAX_NODES)); + } + + @Override + public void migrate(@NonNull final MigrationContext ctx) { + throw new UnsupportedOperationException("need implementation"); + } +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/validators/AddressBookValidator.java b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/validators/AddressBookValidator.java new file mode 100644 index 000000000000..78505c39f0ec --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/main/java/com/hedera/node/app/service/addressbook/impl/validators/AddressBookValidator.java @@ -0,0 +1,124 @@ +/* + * 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.service.addressbook.impl.validators; + +import static com.hedera.hapi.node.base.ResponseCodeEnum.FQDN_SIZE_TOO_LARGE; +import static com.hedera.hapi.node.base.ResponseCodeEnum.GOSSIP_ENDPOINTS_EXCEEDED_LIMIT; +import static com.hedera.hapi.node.base.ResponseCodeEnum.GOSSIP_ENDPOINT_CANNOT_HAVE_FQDN; +import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_ENDPOINT; +import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_GOSSIP_ENDPOINT; +import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_NODE_DESCRIPTION; +import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_SERVICE_ENDPOINT; +import static com.hedera.hapi.node.base.ResponseCodeEnum.IP_FQDN_CANNOT_BE_SET_FOR_SAME_ENDPOINT; +import static com.hedera.node.app.spi.workflows.HandleException.validateFalse; + +import com.hedera.hapi.node.base.ServiceEndpoint; +import com.hedera.node.config.data.NodesConfig; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.nio.charset.StandardCharsets; +import java.util.List; +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class AddressBookValidator { + /** + * Default constructor for injection. + */ + @Inject + public AddressBookValidator() { + // Dagger2 + } + + /** + * Validates the node description. + * + * @param description The description to validate + * @param nodesConfig The nodes configuration + */ + public void validateDescription(@Nullable final String description, @NonNull final NodesConfig nodesConfig) { + if (description == null || description.isEmpty()) { + return; + } + final var raw = description.getBytes(StandardCharsets.UTF_8); + final var maxUtf8Bytes = nodesConfig.nodeMaxDescriptionUtf8Bytes(); + validateFalse(raw.length > maxUtf8Bytes, INVALID_NODE_DESCRIPTION); + validateFalse(containsZeroByte(raw), INVALID_NODE_DESCRIPTION); + } + + private boolean containsZeroByte(@NonNull final byte[] bytes) { + boolean ret = false; + for (final byte b : bytes) { + if (b == 0) { + ret = true; + break; + } + } + return ret; + } + + /** + * Validates the gossip endpoint. + * + * @param endpointList The list of GossipEndpoint to validate + * @param nodesConfig The nodes configuration + */ + public void validateGossipEndpoint( + @Nullable final List endpointList, @NonNull final NodesConfig nodesConfig) { + validateFalse(endpointList == null || endpointList.isEmpty(), INVALID_GOSSIP_ENDPOINT); + validateFalse(endpointList.size() > nodesConfig.maxGossipEndpoint(), GOSSIP_ENDPOINTS_EXCEEDED_LIMIT); + // for phase 2: The first in the list is used as the Internal IP address in config.txt, + // the second in the list is used as the External IP address in config.txt + validateFalse(endpointList.size() < 2, INVALID_GOSSIP_ENDPOINT); + + for (final var endpoint : endpointList) { + validateFalse( + nodesConfig.gossipFqdnRestricted() && !endpoint.domainName().isEmpty(), + GOSSIP_ENDPOINT_CANNOT_HAVE_FQDN); + validateEndpoint(endpoint, nodesConfig); + } + } + + /** + * Validates the service endpoint. + * + * @param endpointList The list of ServiceEndpoint to validate + * @param nodesConfig The nodes configuration + */ + public void validateServiceEndpoint( + @Nullable final List endpointList, @NonNull final NodesConfig nodesConfig) { + validateFalse(endpointList == null || endpointList.isEmpty(), INVALID_SERVICE_ENDPOINT); + validateFalse(endpointList.size() > nodesConfig.maxServiceEndpoint(), INVALID_SERVICE_ENDPOINT); + for (final var endpoint : endpointList) { + validateEndpoint(endpoint, nodesConfig); + } + } + + private void validateEndpoint(@NonNull final ServiceEndpoint endpoint, @NonNull final NodesConfig nodesConfig) { + validateFalse(endpoint.port() == 0, INVALID_ENDPOINT); + validateFalse( + endpoint.ipAddressV4().length() == 0 + && endpoint.domainName().trim().isEmpty(), + INVALID_ENDPOINT); + validateFalse( + endpoint.ipAddressV4().length() != 0 + && !endpoint.domainName().trim().isEmpty(), + IP_FQDN_CANNOT_BE_SET_FOR_SAME_ENDPOINT); + validateFalse(endpoint.domainName().trim().length() > nodesConfig.maxFqdnSize(), FQDN_SIZE_TOO_LARGE); + } +} 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 new file mode 100644 index 000000000000..d0b9940cc97d --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/main/java/module-info.java @@ -0,0 +1,25 @@ +import com.hedera.node.app.service.addressbook.impl.AddressBookServiceImpl; + +module com.hedera.node.app.service.addressbook.impl { + requires transitive com.hedera.node.app.service.addressbook; + requires transitive com.hedera.node.app.spi; + requires transitive com.hedera.node.hapi; + requires transitive com.swirlds.config.api; + requires transitive com.swirlds.state.api; + requires transitive dagger; + requires transitive javax.inject; + requires com.hedera.node.app.service.token; + requires com.hedera.node.config; + requires com.hedera.pbj.runtime; + requires org.apache.logging.log4j; + requires static com.github.spotbugs.annotations; + requires static java.compiler; + + provides com.hedera.node.app.service.addressbook.AddressBookService with + AddressBookServiceImpl; + + exports com.hedera.node.app.service.addressbook.impl to + com.hedera.node.app; + exports com.hedera.node.app.service.addressbook.impl.handlers; + exports com.hedera.node.app.service.addressbook.impl.records; +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/AddressBookServiceImplTest.java b/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/AddressBookServiceImplTest.java new file mode 100644 index 000000000000..9ed5858690c7 --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/AddressBookServiceImplTest.java @@ -0,0 +1,49 @@ +/* + * 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.service.addressbook.impl.test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import com.hedera.node.app.service.addressbook.impl.AddressBookServiceImpl; +import com.swirlds.state.spi.SchemaRegistry; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class AddressBookServiceImplTest { + private AddressBookServiceImpl subject; + + @BeforeEach + void setUp() { + subject = new AddressBookServiceImpl(); + } + + @Test + void registerSchemasNullArgsThrow() { + assertThatThrownBy(() -> subject.registerSchemas(null)).isInstanceOf(NullPointerException.class); + } + + @Test + void registerSchemasRegistersNodesSchema() { + final var schemaRegistry = mock(SchemaRegistry.class); + + subject.registerSchemas(schemaRegistry); + verify(schemaRegistry).register(notNull()); + } +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/ReadableNodeStoreImplTest.java b/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/ReadableNodeStoreImplTest.java new file mode 100644 index 000000000000..78057a55245c --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/ReadableNodeStoreImplTest.java @@ -0,0 +1,86 @@ +/* + * 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.service.addressbook.impl.test; + +import static com.hedera.node.app.service.addressbook.impl.AddressBookServiceImpl.NODES_KEY; +import static com.hedera.node.app.service.mono.pbj.PbjConverter.asBytes; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.BDDMockito.given; + +import com.hedera.hapi.node.state.addressbook.Node; +import com.hedera.hapi.node.state.common.EntityNumber; +import com.hedera.node.app.service.addressbook.ReadableNodeStore; +import com.hedera.node.app.service.addressbook.impl.ReadableNodeStoreImpl; +import com.hedera.node.app.service.addressbook.impl.test.handlers.AddressBookTestBase; +import com.swirlds.platform.test.fixtures.state.MapReadableKVState; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class ReadableNodeStoreImplTest extends AddressBookTestBase { + private ReadableNodeStore subject; + + @BeforeEach + void setUp() { + subject = new ReadableNodeStoreImpl(readableStates); + } + + @Test + void getsNodeIfNodeExists() { + givenValidNode(); + final var node = subject.get(nodeId.number()); + + assertNotNull(node); + + assertEquals(1L, node.nodeId()); + assertEquals(accountId, node.accountId()); + assertEquals("description", node.description()); + assertArrayEquals(gossipCaCertificate, asBytes(node.gossipCaCertificate())); + assertArrayEquals(grpcCertificateHash, asBytes(node.grpcCertificateHash())); + } + + @Test + void missingNodeIsNull() { + readableNodeState.reset(); + final var state = + MapReadableKVState.builder(NODES_KEY).build(); + given(readableStates.get(NODES_KEY)).willReturn(state); + subject = new ReadableNodeStoreImpl(readableStates); + + assertThat(subject.get(nodeId.number())).isNull(); + } + + @Test + void constructorCreatesNodeState() { + final var store = new ReadableNodeStoreImpl(readableStates); + assertNotNull(store); + } + + @Test + void nullArgsFail() { + assertThrows(NullPointerException.class, () -> new ReadableNodeStoreImpl(null)); + } + + @Test + void getSizeOfState() { + final var store = new ReadableNodeStoreImpl(readableStates); + assertEquals(readableStates.get(NODES_KEY).size(), store.sizeOfState()); + } +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/WritableNodeStoreTest.java b/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/WritableNodeStoreTest.java new file mode 100644 index 000000000000..6fbc899944cd --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/WritableNodeStoreTest.java @@ -0,0 +1,94 @@ +/* + * 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.service.addressbook.impl.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.hedera.hapi.node.state.addressbook.Node; +import com.hedera.node.app.service.addressbook.impl.WritableNodeStore; +import com.hedera.node.app.service.addressbook.impl.test.handlers.AddressBookTestBase; +import com.hedera.node.app.spi.metrics.StoreMetricsService; +import com.hedera.node.config.testfixtures.HederaTestConfigBuilder; +import com.swirlds.config.api.Configuration; +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 WritableNodeStoreTest extends AddressBookTestBase { + + private static final Configuration CONFIGURATION = HederaTestConfigBuilder.createConfig(); + + @Mock + private StoreMetricsService storeMetricsService; + + private Node node; + + @Test + void throwsIfNullValuesAsArgs() { + assertThrows(NullPointerException.class, () -> new WritableNodeStore(null, CONFIGURATION, storeMetricsService)); + assertThrows( + NullPointerException.class, () -> new WritableNodeStore(writableStates, null, storeMetricsService)); + assertThrows(NullPointerException.class, () -> new WritableNodeStore(writableStates, CONFIGURATION, null)); + assertThrows(NullPointerException.class, () -> writableStore.put(null)); + } + + @Test + void constructorCreatesNodeState() { + final var store = new WritableNodeStore(writableStates, CONFIGURATION, storeMetricsService); + assertNotNull(store); + } + + @Test + void commitsNodeChanges() { + node = createNode(); + assertFalse(writableNodeState.contains(nodeId)); + + writableStore.put(node); + + assertTrue(writableNodeState.contains(nodeId)); + final var writtenNode = writableNodeState.get(nodeId); + assertEquals(node, writtenNode); + } + + @Test + void getReturnsNode() { + node = createNode(); + writableStore.put(node); + + final var maybeReadNode = writableStore.get(nodeId.number()); + + assertNotNull(maybeReadNode); + assertEquals(node, maybeReadNode); + } + + @Test + void getForModifyReturnsNode() { + node = createNode(); + writableStore.put(node); + + final var maybeReadNode = writableStore.getForModify(nodeId.number()); + + assertNotNull(maybeReadNode); + assertEquals(node, maybeReadNode); + } +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/handlers/AddressBookHandlersTest.java b/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/handlers/AddressBookHandlersTest.java new file mode 100644 index 000000000000..6aed0d29f4ee --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/handlers/AddressBookHandlersTest.java @@ -0,0 +1,83 @@ +/* + * 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.service.addressbook.impl.test.handlers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; + +import com.hedera.node.app.service.addressbook.impl.handlers.AddressBookHandlers; +import com.hedera.node.app.service.addressbook.impl.handlers.NodeCreateHandler; +import com.hedera.node.app.service.addressbook.impl.handlers.NodeDeleteHandler; +import com.hedera.node.app.service.addressbook.impl.handlers.NodeGetInfoHandler; +import com.hedera.node.app.service.addressbook.impl.handlers.NodeUpdateHandler; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class AddressBookHandlersTest { + private NodeCreateHandler nodeCreateHandler; + + private NodeDeleteHandler nodeDeleteHandler; + + private NodeGetInfoHandler nodeGetInfoHandler; + + private NodeUpdateHandler nodeUpdateHandler; + + private AddressBookHandlers addressBookHandlers; + + @BeforeEach + public void setUp() { + nodeCreateHandler = mock(NodeCreateHandler.class); + nodeDeleteHandler = mock(NodeDeleteHandler.class); + nodeGetInfoHandler = mock(NodeGetInfoHandler.class); + nodeUpdateHandler = mock(NodeUpdateHandler.class); + + addressBookHandlers = + new AddressBookHandlers(nodeCreateHandler, nodeDeleteHandler, nodeGetInfoHandler, nodeUpdateHandler); + } + + @Test + void nodeCreateHandlerReturnsCorrectInstance() { + assertEquals( + nodeCreateHandler, + addressBookHandlers.nodeCreateHandler(), + "nodeCreateHandler does not return correct instance"); + } + + @Test + void nodeDeleteHandlerReturnsCorrectInstance() { + assertEquals( + nodeDeleteHandler, + addressBookHandlers.nodeDeleteHandler(), + "nodeDeleteHandler does not return correct instance"); + } + + @Test + void nodeGetInfoHandlerReturnsCorrectInstance() { + assertEquals( + nodeGetInfoHandler, + addressBookHandlers.nodeGetInfoHandler(), + "nodeGetInfoHandler does not return correct instance"); + } + + @Test + void nodeUpdateHandlerReturnsCorrectInstance() { + assertEquals( + nodeUpdateHandler, + addressBookHandlers.nodeUpdateHandler(), + "nodeUpdateHandler does not return correct instance"); + } +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/handlers/AddressBookTestBase.java b/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/handlers/AddressBookTestBase.java new file mode 100644 index 000000000000..6b05887c1175 --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/handlers/AddressBookTestBase.java @@ -0,0 +1,201 @@ +/* + * 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.service.addressbook.impl.test.handlers; + +import static com.hedera.node.app.service.addressbook.impl.AddressBookServiceImpl.NODES_KEY; +import static com.hedera.node.app.service.mono.pbj.PbjConverter.asBytes; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +import com.hedera.hapi.node.base.AccountID; +import com.hedera.hapi.node.base.Key; +import com.hedera.hapi.node.base.ServiceEndpoint; +import com.hedera.hapi.node.base.Timestamp; +import com.hedera.hapi.node.state.addressbook.Node; +import com.hedera.hapi.node.state.common.EntityNumber; +import com.hedera.hapi.node.state.primitives.ProtoBytes; +import com.hedera.node.app.service.addressbook.ReadableNodeStore; +import com.hedera.node.app.service.addressbook.impl.ReadableNodeStoreImpl; +import com.hedera.node.app.service.addressbook.impl.WritableNodeStore; +import com.hedera.node.app.spi.metrics.StoreMetricsService; +import com.hedera.node.config.testfixtures.HederaTestConfigBuilder; +import com.hedera.pbj.runtime.io.buffer.Bytes; +import com.swirlds.common.utility.CommonUtils; +import com.swirlds.platform.test.fixtures.state.MapReadableKVState; +import com.swirlds.platform.test.fixtures.state.MapWritableKVState; +import com.swirlds.state.spi.ReadableStates; +import com.swirlds.state.spi.WritableStates; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class AddressBookTestBase { + protected final AccountID accountId = AccountID.newBuilder().accountNum(3).build(); + + protected final AccountID payerId = AccountID.newBuilder().accountNum(2).build(); + protected final byte[] grpcCertificateHash = "grpcCertificateHash".getBytes(); + protected final byte[] gossipCaCertificate = "gossipCaCertificate".getBytes(); + protected final long WELL_KNOWN_NODE_ID = 1L; + protected final EntityNumber nodeId = + EntityNumber.newBuilder().number(WELL_KNOWN_NODE_ID).build(); + protected final EntityNumber nodeId2 = EntityNumber.newBuilder().number(3L).build(); + protected final Timestamp consensusTimestamp = + Timestamp.newBuilder().seconds(1_234_567L).build(); + + protected static final Key aPrimitiveKey = Key.newBuilder() + .ed25519(Bytes.wrap("01234567890123456789012345678901")) + .build(); + protected static final ProtoBytes edKeyAlias = new ProtoBytes(Bytes.wrap(asBytes(Key.PROTOBUF, aPrimitiveKey))); + protected final AccountID alias = + AccountID.newBuilder().alias(edKeyAlias.value()).build(); + protected final byte[] evmAddress = CommonUtils.unhex("6aea3773ea468a814d954e6dec795bfee7d76e26"); + + protected final ServiceEndpoint endpoint1 = new ServiceEndpoint(Bytes.wrap("127.0.0.1"), 1234, null); + + protected final ServiceEndpoint endpoint2 = new ServiceEndpoint(Bytes.wrap("127.0.0.2"), 2345, null); + + protected final ServiceEndpoint endpoint3 = new ServiceEndpoint(Bytes.EMPTY, 3456, "test.domain.com"); + + protected final ServiceEndpoint endpoint4 = new ServiceEndpoint(Bytes.wrap("127.0.0.2"), 2345, "test.domain.com"); + + protected final ServiceEndpoint endpoint5 = new ServiceEndpoint(Bytes.EMPTY, 2345, null); + + protected final ServiceEndpoint endpoint6 = new ServiceEndpoint(Bytes.EMPTY, 0, null); + + protected Node node; + + @Mock + protected ReadableStates readableStates; + + @Mock + protected WritableStates writableStates; + + @Mock + private StoreMetricsService storeMetricsService; + + protected MapReadableKVState readableNodeState; + protected MapWritableKVState writableNodeState; + + protected ReadableNodeStore readableStore; + protected WritableNodeStore writableStore; + + @BeforeEach + void commonSetUp() { + givenValidNode(); + refreshStoresWithCurrentNodeInReadable(); + } + + protected void refreshStoresWithCurrentNodeInReadable() { + readableNodeState = readableNodeState(); + writableNodeState = emptyWritableNodeState(); + given(readableStates.get(NODES_KEY)).willReturn(readableNodeState); + given(writableStates.get(NODES_KEY)).willReturn(writableNodeState); + readableStore = new ReadableNodeStoreImpl(readableStates); + final var configuration = HederaTestConfigBuilder.createConfig(); + writableStore = new WritableNodeStore(writableStates, configuration, storeMetricsService); + } + + protected void refreshStoresWithCurrentNodeInBothReadableAndWritable() { + readableNodeState = readableNodeState(); + writableNodeState = writableNodeStateWithOneKey(); + given(readableStates.get(NODES_KEY)).willReturn(readableNodeState); + given(writableStates.get(NODES_KEY)).willReturn(writableNodeState); + readableStore = new ReadableNodeStoreImpl(readableStates); + final var configuration = HederaTestConfigBuilder.createConfig(); + writableStore = new WritableNodeStore(writableStates, configuration, storeMetricsService); + } + + protected void refreshStoresWithCurrentNodeInWritable() { + writableNodeState = writableNodeStateWithOneKey(); + given(writableStates.get(NODES_KEY)).willReturn(writableNodeState); + final var configuration = HederaTestConfigBuilder.createConfig(); + writableStore = new WritableNodeStore(writableStates, configuration, storeMetricsService); + } + + protected void refreshStoresWithMoreNodeInWritable() { + writableNodeState = writableNodeStateWithMoreKeys(); + given(writableStates.get(NODES_KEY)).willReturn(writableNodeState); + final var configuration = HederaTestConfigBuilder.createConfig(); + writableStore = new WritableNodeStore(writableStates, configuration, storeMetricsService); + } + + @NonNull + protected MapWritableKVState emptyWritableNodeState() { + return MapWritableKVState.builder(NODES_KEY).build(); + } + + @NonNull + protected MapWritableKVState writableNodeStateWithOneKey() { + return MapWritableKVState.builder(NODES_KEY) + .value(nodeId, node) + .build(); + } + + @NonNull + protected MapWritableKVState writableNodeStateWithMoreKeys() { + return MapWritableKVState.builder(NODES_KEY) + .value(nodeId, node) + .value(nodeId2, mock(Node.class)) + .build(); + } + + @NonNull + protected MapReadableKVState readableNodeState() { + return MapReadableKVState.builder(NODES_KEY) + .value(nodeId, node) + .build(); + } + + @NonNull + protected MapReadableKVState emptyReadableNodeState() { + return MapReadableKVState.builder(NODES_KEY).build(); + } + + protected void givenValidNode() { + givenValidNode(false); + } + + protected void givenValidNode(boolean deleted) { + node = new Node( + nodeId.number(), + accountId, + "description", + null, + null, + Bytes.wrap(gossipCaCertificate), + Bytes.wrap(grpcCertificateHash), + 0, + deleted); + } + + protected Node createNode() { + return new Node.Builder() + .nodeId(nodeId.number()) + .accountId(accountId) + .description("description") + .gossipEndpoint((List) null) + .serviceEndpoint((List) null) + .gossipCaCertificate(Bytes.wrap(gossipCaCertificate)) + .grpcCertificateHash(Bytes.wrap(grpcCertificateHash)) + .weight(0) + .build(); + } +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/handlers/NodeCreateHandlerTest.java b/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/handlers/NodeCreateHandlerTest.java new file mode 100644 index 000000000000..1996bb999024 --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/handlers/NodeCreateHandlerTest.java @@ -0,0 +1,521 @@ +/* + * 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.service.addressbook.impl.test.handlers; + +import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_GOSSIP_CAE_CERTIFICATE; +import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_GOSSIP_ENDPOINT; +import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_NODE_ACCOUNT_ID; +import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_SERVICE_ENDPOINT; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mock.Strictness.LENIENT; +import static org.mockito.Mockito.verify; + +import com.hedera.hapi.node.addressbook.NodeCreateTransactionBody; +import com.hedera.hapi.node.base.AccountID; +import com.hedera.hapi.node.base.ResponseCodeEnum; +import com.hedera.hapi.node.base.ServiceEndpoint; +import com.hedera.hapi.node.base.TransactionID; +import com.hedera.hapi.node.transaction.TransactionBody; +import com.hedera.node.app.service.addressbook.impl.WritableNodeStore; +import com.hedera.node.app.service.addressbook.impl.handlers.NodeCreateHandler; +import com.hedera.node.app.service.addressbook.impl.records.NodeCreateRecordBuilder; +import com.hedera.node.app.service.addressbook.impl.validators.AddressBookValidator; +import com.hedera.node.app.service.token.ReadableAccountStore; +import com.hedera.node.app.spi.fees.FeeAccumulator; +import com.hedera.node.app.spi.fees.FeeCalculator; +import com.hedera.node.app.spi.metrics.StoreMetricsService; +import com.hedera.node.app.spi.workflows.HandleContext; +import com.hedera.node.app.spi.workflows.HandleException; +import com.hedera.node.app.spi.workflows.PreCheckException; +import com.hedera.node.config.testfixtures.HederaTestConfigBuilder; +import com.hedera.pbj.runtime.io.buffer.Bytes; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +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 NodeCreateHandlerTest extends AddressBookTestBase { + + @Mock(strictness = LENIENT) + private HandleContext handleContext; + + @Mock + private NodeCreateRecordBuilder recordBuilder; + + @Mock + private ReadableAccountStore accountStore; + + @Mock + private FeeCalculator feeCalculator; + + @Mock + private FeeAccumulator feeAccumulator; + + @Mock + private StoreMetricsService storeMetricsService; + + private TransactionBody txn; + private NodeCreateHandler subject; + + private AddressBookValidator addressBookValidator; + + @BeforeEach + void setUp() { + addressBookValidator = new AddressBookValidator(); + subject = new NodeCreateHandler(addressBookValidator); + } + + @Test + @DisplayName("pureChecks fail when accountId is null") + void accountIdCannotNull() { + txn = new NodeCreateBuilder().build(); + final var msg = assertThrows(PreCheckException.class, () -> subject.pureChecks(txn)); + assertThat(INVALID_NODE_ACCOUNT_ID).isEqualTo(msg.responseCode()); + } + + @Test + @DisplayName("pureChecks fail when accountId not set") + void accountIdNeedSet() { + txn = new NodeCreateBuilder().withAccountId(AccountID.DEFAULT).build(); + final var msg = assertThrows(PreCheckException.class, () -> subject.pureChecks(txn)); + assertThat(INVALID_NODE_ACCOUNT_ID).isEqualTo(msg.responseCode()); + } + + @Test + @DisplayName("pureChecks fail when accountId is alias") + void accountIdCannotAlias() { + txn = new NodeCreateBuilder().withAccountId(alias).build(); + final var msg = assertThrows(PreCheckException.class, () -> subject.pureChecks(txn)); + assertThat(INVALID_NODE_ACCOUNT_ID).isEqualTo(msg.responseCode()); + } + + @Test + @DisplayName("pureChecks fail when gossip_endpoint not specified") + void gossipEndpointNeedSet() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withGossipEndpoint(List.of()) + .build(); + final var msg = assertThrows(PreCheckException.class, () -> subject.pureChecks(txn)); + assertThat(INVALID_GOSSIP_ENDPOINT).isEqualTo(msg.responseCode()); + } + + @Test + @DisplayName("pureChecks fail when service_endpoint not specified") + void serviceEndpointNeedSet() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withGossipEndpoint(List.of(endpoint1)) + .withServiceEndpoint(List.of()) + .build(); + final var msg = assertThrows(PreCheckException.class, () -> subject.pureChecks(txn)); + assertThat(INVALID_SERVICE_ENDPOINT).isEqualTo(msg.responseCode()); + } + + @Test + @DisplayName("pureChecks fail when gossipCaCertificate not specified") + void gossipCaCertificateNeedSet() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withGossipEndpoint(List.of(endpoint1)) + .withServiceEndpoint(List.of(endpoint2)) + .build(); + final var msg = assertThrows(PreCheckException.class, () -> subject.pureChecks(txn)); + assertThat(INVALID_GOSSIP_CAE_CERTIFICATE).isEqualTo(msg.responseCode()); + } + + @Test + @DisplayName("pureChecks succeeds when expected attributes are specified") + void pureCheckPass() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withGossipEndpoint(List.of(endpoint1)) + .withServiceEndpoint(List.of(endpoint2)) + .withGossipCaCertificate(Bytes.wrap("cert")) + .build(); + assertDoesNotThrow(() -> subject.pureChecks(txn)); + } + + @Test + void failsWhenMaxNodesExceeds() { + txn = new NodeCreateBuilder().withAccountId(accountId).build(); + given(handleContext.body()).willReturn(txn); + refreshStoresWithCurrentNodeInWritable(); + final var config = HederaTestConfigBuilder.create() + .withValue("nodes.maxNumber", 1L) + .getOrCreateConfig(); + given(handleContext.configuration()).willReturn(config); + given(handleContext.writableStore(WritableNodeStore.class)).willReturn(writableStore); + + assertEquals(1, writableStore.sizeOfState()); + final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext)); + assertEquals(ResponseCodeEnum.MAX_NODES_CREATED, msg.getStatus()); + assertEquals(0, writableStore.modifiedNodes().size()); + } + + @Test + void accountIdMustInState() { + txn = new NodeCreateBuilder().withAccountId(accountId).build(); + given(accountStore.contains(accountId)).willReturn(false); + given(handleContext.body()).willReturn(txn); + refreshStoresWithCurrentNodeInWritable(); + final var config = HederaTestConfigBuilder.create() + .withValue("nodes.nodeMaxDescriptionUtf8Bytes", 10) + .getOrCreateConfig(); + given(handleContext.configuration()).willReturn(config); + given(handleContext.writableStore(WritableNodeStore.class)).willReturn(writableStore); + given(handleContext.readableStore(ReadableAccountStore.class)).willReturn(accountStore); + + final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext)); + assertEquals(ResponseCodeEnum.INVALID_NODE_ACCOUNT_ID, msg.getStatus()); + } + + @Test + void failsWhenDescriptionTooLarge() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withDescription("Description") + .build(); + setupHandle(); + + refreshStoresWithCurrentNodeInWritable(); + final var config = HederaTestConfigBuilder.create() + .withValue("nodes.nodeMaxDescriptionUtf8Bytes", 10) + .getOrCreateConfig(); + given(handleContext.configuration()).willReturn(config); + + final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext)); + assertEquals(ResponseCodeEnum.INVALID_NODE_DESCRIPTION, msg.getStatus()); + } + + @Test + void failsWhenDescriptionContainZeroByte() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withDescription("Des\0cription") + .build(); + setupHandle(); + final var config = HederaTestConfigBuilder.create() + .withValue("nodes.nodeMaxDescriptionUtf8Bytes", 12) + .getOrCreateConfig(); + given(handleContext.configuration()).willReturn(config); + + final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext)); + assertEquals(ResponseCodeEnum.INVALID_NODE_DESCRIPTION, msg.getStatus()); + } + + @Test + void failsWhenGossipEndpointTooLarge() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withGossipEndpoint(List.of(endpoint1, endpoint2, endpoint3)) + .build(); + setupHandle(); + + final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext)); + assertEquals(ResponseCodeEnum.GOSSIP_ENDPOINTS_EXCEEDED_LIMIT, msg.getStatus()); + } + + @Test + void failsWhenGossipEndpointNull() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withGossipEndpoint(null) + .build(); + setupHandle(); + + final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext)); + assertEquals(ResponseCodeEnum.INVALID_GOSSIP_ENDPOINT, msg.getStatus()); + } + + @Test + void failsWhenGossipEndpointEmpty() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withGossipEndpoint(List.of()) + .build(); + setupHandle(); + + final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext)); + assertEquals(ResponseCodeEnum.INVALID_GOSSIP_ENDPOINT, msg.getStatus()); + } + + @Test + void failsWhenGossipEndpointTooSmall() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withGossipEndpoint(List.of(endpoint1)) + .build(); + setupHandle(); + + final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext)); + assertEquals(ResponseCodeEnum.INVALID_GOSSIP_ENDPOINT, msg.getStatus()); + } + + @Test + void failsWhenGossipEndpointHaveIPAndFQDN() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withGossipEndpoint(List.of(endpoint1, endpoint4)) + .build(); + setupHandle(); + + final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext)); + assertEquals(ResponseCodeEnum.GOSSIP_ENDPOINT_CANNOT_HAVE_FQDN, msg.getStatus()); + } + + @Test + void failsWhenEndpointHaveEmptyIPAndFQDN() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withGossipEndpoint(List.of(endpoint1, endpoint5)) + .build(); + setupHandle(); + + final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext)); + assertEquals(ResponseCodeEnum.INVALID_ENDPOINT, msg.getStatus()); + } + + @Test + void failsWhenEndpointHaveZeroIp() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withGossipEndpoint(List.of(endpoint1, endpoint6)) + .build(); + setupHandle(); + + final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext)); + assertEquals(ResponseCodeEnum.INVALID_ENDPOINT, msg.getStatus()); + } + + @Test + void failsWhenServiceEndpointTooLarge() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withGossipEndpoint(List.of(endpoint1, endpoint2)) + .withServiceEndpoint(List.of(endpoint1, endpoint2, endpoint3)) + .build(); + setupHandle(); + final var config = HederaTestConfigBuilder.create() + .withValue("nodes.maxGossipEndpoint", 2) + .withValue("nodes.maxServiceEndpoint", 2) + .getOrCreateConfig(); + given(handleContext.configuration()).willReturn(config); + + final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext)); + assertEquals(ResponseCodeEnum.INVALID_SERVICE_ENDPOINT, msg.getStatus()); + } + + @Test + void failsWhenServiceEndpointNull() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withGossipEndpoint(List.of(endpoint1, endpoint2)) + .withServiceEndpoint(null) + .build(); + setupHandle(); + + final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext)); + assertEquals(ResponseCodeEnum.INVALID_SERVICE_ENDPOINT, msg.getStatus()); + } + + @Test + void failsWhenServiceEndpointEmpty() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withGossipEndpoint(List.of(endpoint1, endpoint2)) + .withServiceEndpoint(List.of()) + .build(); + setupHandle(); + + final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext)); + assertEquals(ResponseCodeEnum.INVALID_SERVICE_ENDPOINT, msg.getStatus()); + } + + @Test + void failsWhenEndpointHaveIPAndFQDN() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withGossipEndpoint(List.of(endpoint1, endpoint2)) + .withServiceEndpoint(List.of(endpoint1, endpoint4)) + .build(); + setupHandle(); + + final var config = HederaTestConfigBuilder.create() + .withValue("nodes.maxGossipEndpoint", 2) + .withValue("nodes.maxServiceEndpoint", 2) + .getOrCreateConfig(); + given(handleContext.configuration()).willReturn(config); + + final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext)); + assertEquals(ResponseCodeEnum.IP_FQDN_CANNOT_BE_SET_FOR_SAME_ENDPOINT, msg.getStatus()); + } + + @Test + void failsWhenEndpointFQDNTooLarge() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withGossipEndpoint(List.of(endpoint1, endpoint2)) + .withServiceEndpoint(List.of(endpoint1, endpoint3)) + .build(); + setupHandle(); + + final var config = HederaTestConfigBuilder.create() + .withValue("nodes.maxGossipEndpoint", 2) + .withValue("nodes.maxServiceEndpoint", 2) + .withValue("nodes.maxFqdnSize", 4) + .getOrCreateConfig(); + given(handleContext.configuration()).willReturn(config); + + final var msg = assertThrows(HandleException.class, () -> subject.handle(handleContext)); + assertEquals(ResponseCodeEnum.FQDN_SIZE_TOO_LARGE, msg.getStatus()); + } + + @Test + void hanldeWorkAsExpected() { + txn = new NodeCreateBuilder() + .withAccountId(accountId) + .withDescription("Description") + .withGossipEndpoint(List.of(endpoint1, endpoint2)) + .withServiceEndpoint(List.of(endpoint1, endpoint3)) + .withGossipCaCertificate(Bytes.wrap("cert")) + .withGrpcCertificateHash(Bytes.wrap("hash")) + .build(); + given(handleContext.body()).willReturn(txn); + refreshStoresWithMoreNodeInWritable(); + final var config = HederaTestConfigBuilder.create() + .withValue("nodes.nodeMaxDescriptionUtf8Bytes", 12) + .withValue("nodes.maxGossipEndpoint", 4) + .withValue("nodes.maxServiceEndpoint", 3) + .getOrCreateConfig(); + given(handleContext.configuration()).willReturn(config); + given(handleContext.writableStore(WritableNodeStore.class)).willReturn(writableStore); + given(handleContext.recordBuilder(any())).willReturn(recordBuilder); + given(accountStore.contains(accountId)).willReturn(true); + given(handleContext.readableStore(ReadableAccountStore.class)).willReturn(accountStore); + + assertDoesNotThrow(() -> subject.handle(handleContext)); + final var createdNode = writableStore.get(4L); + assertNotNull(createdNode); + verify(recordBuilder).nodeID(4L); + assertEquals(4, createdNode.nodeId()); + assertEquals("Description", createdNode.description()); + assertArrayEquals( + (List.of(endpoint1, endpoint2)).toArray(), + createdNode.gossipEndpoint().toArray()); + assertArrayEquals( + (List.of(endpoint1, endpoint3)).toArray(), + createdNode.serviceEndpoint().toArray()); + assertArrayEquals("cert".getBytes(), createdNode.gossipCaCertificate().toByteArray()); + assertArrayEquals("hash".getBytes(), createdNode.grpcCertificateHash().toByteArray()); + } + + private void setupHandle() { + given(handleContext.body()).willReturn(txn); + refreshStoresWithCurrentNodeInWritable(); + final var config = HederaTestConfigBuilder.create() + .withValue("nodes.maxGossipEndpoint", 2) + .getOrCreateConfig(); + given(handleContext.configuration()).willReturn(config); + given(handleContext.writableStore(WritableNodeStore.class)).willReturn(writableStore); + given(accountStore.contains(accountId)).willReturn(true); + given(handleContext.readableStore(ReadableAccountStore.class)).willReturn(accountStore); + } + + private class NodeCreateBuilder { + private AccountID accountId = null; + private String description = null; + private List gossipEndpoint = null; + + private List serviceEndpoint = null; + + private Bytes gossipCaCertificate = null; + + private Bytes grpcCertificateHash = null; + + private NodeCreateBuilder() {} + + public TransactionBody build() { + final var txnId = TransactionID.newBuilder().accountID(payerId).transactionValidStart(consensusTimestamp); + final var txnBody = NodeCreateTransactionBody.newBuilder(); + if (accountId != null) { + txnBody.accountId(accountId); + } + if (description != null) { + txnBody.description(description); + } + if (gossipEndpoint != null) { + txnBody.gossipEndpoint(gossipEndpoint); + } + if (serviceEndpoint != null) { + txnBody.serviceEndpoint(serviceEndpoint); + } + if (gossipCaCertificate != null) { + txnBody.gossipCaCertificate(gossipCaCertificate); + } + if (grpcCertificateHash != null) { + txnBody.grpcCertificateHash(grpcCertificateHash); + } + + return TransactionBody.newBuilder() + .transactionID(txnId) + .nodeCreate(txnBody.build()) + .build(); + } + + public NodeCreateBuilder withAccountId(final AccountID accountId) { + this.accountId = accountId; + return this; + } + + public NodeCreateBuilder withDescription(final String description) { + this.description = description; + return this; + } + + public NodeCreateBuilder withGossipEndpoint(final List gossipEndpoint) { + this.gossipEndpoint = gossipEndpoint; + return this; + } + + public NodeCreateBuilder withServiceEndpoint(final List serviceEndpoint) { + this.serviceEndpoint = serviceEndpoint; + return this; + } + + public NodeCreateBuilder withGossipCaCertificate(final Bytes gossipCaCertificate) { + this.gossipCaCertificate = gossipCaCertificate; + return this; + } + + public NodeCreateBuilder withGrpcCertificateHash(final Bytes grpcCertificateHash) { + this.grpcCertificateHash = grpcCertificateHash; + return this; + } + } +} diff --git a/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/handlers/NodeDeleteHandlerTest.java b/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/handlers/NodeDeleteHandlerTest.java new file mode 100644 index 000000000000..f4bd08e9d91d --- /dev/null +++ b/hedera-node/hedera-addressbook-service-impl/src/test/java/com/hedera/node/app/service/addressbook/impl/test/handlers/NodeDeleteHandlerTest.java @@ -0,0 +1,215 @@ +/* + * 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.service.addressbook.impl.test.handlers; + +import static com.hedera.hapi.node.base.ResponseCodeEnum.INVALID_NODE_ID; +import static com.hedera.node.app.service.addressbook.impl.AddressBookServiceImpl.NODES_KEY; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.assertj.core.api.AssertionsForClassTypes.catchThrowable; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; + +import com.hedera.hapi.node.addressbook.NodeDeleteTransactionBody; +import com.hedera.hapi.node.base.ResponseCodeEnum; +import com.hedera.hapi.node.base.TransactionID; +import com.hedera.hapi.node.state.addressbook.Node; +import com.hedera.hapi.node.state.common.EntityNumber; +import com.hedera.hapi.node.transaction.TransactionBody; +import com.hedera.node.app.service.addressbook.impl.ReadableNodeStoreImpl; +import com.hedera.node.app.service.addressbook.impl.WritableNodeStore; +import com.hedera.node.app.service.addressbook.impl.handlers.NodeDeleteHandler; +import com.hedera.node.app.service.token.ReadableAccountStore; +import com.hedera.node.app.spi.fees.FeeCalculator; +import com.hedera.node.app.spi.fees.FeeContext; +import com.hedera.node.app.spi.fees.Fees; +import com.hedera.node.app.spi.metrics.StoreMetricsService; +import com.hedera.node.app.spi.workflows.HandleContext; +import com.hedera.node.app.spi.workflows.HandleException; +import com.hedera.node.app.spi.workflows.PreCheckException; +import com.hedera.node.config.testfixtures.HederaTestConfigBuilder; +import com.swirlds.config.api.Configuration; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mock.Strictness; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class NodeDeleteHandlerTest extends AddressBookTestBase { + + @Mock + private ReadableAccountStore accountStore; + + @Mock + private ReadableNodeStoreImpl mockStore; + + @Mock(strictness = Strictness.LENIENT) + private HandleContext handleContext; + + @Mock + private NodeDeleteHandler subject; + + @Mock + private StoreMetricsService storeMetricsService; + + protected Configuration testConfig; + + @BeforeEach + void setUp() { + mockStore = mock(ReadableNodeStoreImpl.class); + subject = new NodeDeleteHandler(); + + writableNodeState = writableNodeStateWithOneKey(); + given(writableStates.get(NODES_KEY)).willReturn(writableNodeState); + testConfig = HederaTestConfigBuilder.createConfig(); + writableStore = new WritableNodeStore(writableStates, testConfig, storeMetricsService); + lenient().when(handleContext.configuration()).thenReturn(testConfig); + } + + @Test + @DisplayName("pureChecks throws exception when node id is negative or zero") + public void testPureChecksThrowsExceptionWhenFileIdIsNull() { + NodeDeleteTransactionBody transactionBody = mock(NodeDeleteTransactionBody.class); + TransactionBody transaction = mock(TransactionBody.class); + given(handleContext.body()).willReturn(transaction); + given(transaction.nodeDeleteOrThrow()).willReturn(transactionBody); + given(transactionBody.nodeId()).willReturn(-1L); + + assertThatThrownBy(() -> subject.pureChecks(handleContext.body())).isInstanceOf(PreCheckException.class); + var msg = assertThrows(PreCheckException.class, () -> subject.pureChecks(handleContext.body())); + assertThat(msg.responseCode()).isEqualTo(INVALID_NODE_ID); + } + + @Test + @DisplayName("pureChecks does not throw exception when node id is not null") + public void testPureChecksDoesNotThrowExceptionWhenNodeIdIsNotNull() { + given(handleContext.body()).willReturn(newDeleteTxn()); + + assertThatCode(() -> subject.pureChecks(handleContext.body())).doesNotThrowAnyException(); + } + + @Test + @DisplayName("check that fees are free for delete node trx") + public void testCalculateFeesInvocations() { + final var feeCtx = mock(FeeContext.class); + final var feeCalc = mock(FeeCalculator.class); + given(feeCtx.feeCalculator(notNull())).willReturn(feeCalc); + given(feeCalc.calculate()).willReturn(Fees.FREE); + + assertThat(subject.calculateFees(feeCtx)).isEqualTo(Fees.FREE); + } + + @Test + @DisplayName("Fails handle if node doesn't exist") + void fileDoesntExist() { + final var txn = newDeleteTxn().nodeDeleteOrThrow(); + + writableNodeState = emptyWritableNodeState(); + given(writableStates.get(NODES_KEY)).willReturn(writableNodeState); + writableStore = new WritableNodeStore(writableStates, testConfig, storeMetricsService); + given(handleContext.writableStore(WritableNodeStore.class)).willReturn(writableStore); + + given(handleContext.body()) + .willReturn(TransactionBody.newBuilder().nodeDelete(txn).build()); + given(handleContext.writableStore(WritableNodeStore.class)).willReturn(writableStore); + + HandleException thrown = (HandleException) catchThrowable(() -> subject.handle(handleContext)); + assertThat(thrown.getStatus()).isEqualTo(INVALID_NODE_ID); + } + + @Test + @DisplayName("Node is null") + void NodeIsNull() { + final var txn = newDeleteTxn().nodeDeleteOrThrow(); + + node = null; + + writableNodeState = writableNodeStateWithOneKey(); + given(writableStates.get(NODES_KEY)).willReturn(writableNodeState); + writableStore = new WritableNodeStore(writableStates, testConfig, storeMetricsService); + given(handleContext.writableStore(WritableNodeStore.class)).willReturn(writableStore); + + given(handleContext.body()) + .willReturn(TransactionBody.newBuilder().nodeDelete(txn).build()); + given(handleContext.writableStore(WritableNodeStore.class)).willReturn(writableStore); + HandleException thrown = (HandleException) catchThrowable(() -> subject.handle(handleContext)); + assertThat(thrown.getStatus()).isEqualTo(INVALID_NODE_ID); + } + + @Test + @DisplayName("Handle works as expected") + void handleWorksAsExpected() { + final var txn = newDeleteTxn().nodeDeleteOrThrow(); + + final var existingNode = writableStore.get(WELL_KNOWN_NODE_ID); + assertThat(existingNode).isNotNull(); + assertThat(existingNode.deleted()).isFalse(); + + given(handleContext.body()) + .willReturn(TransactionBody.newBuilder().nodeDelete(txn).build()); + given(handleContext.writableStore(WritableNodeStore.class)).willReturn(writableStore); + + subject.handle(handleContext); + + final var changedFile = writableStore.get(WELL_KNOWN_NODE_ID); + + assertThat(changedFile).isNotNull(); + assertThat(changedFile.deleted()).isTrue(); + } + + @Test + @DisplayName("Node already deleted returns error") + void noFileKeys() { + givenValidNode(true); + refreshStoresWithCurrentNodeInBothReadableAndWritable(); + + final var txn = newDeleteTxn().nodeDeleteOrThrow(); + + final var existingNode = writableStore.get(WELL_KNOWN_NODE_ID); + assertThat(existingNode).isNotNull(); + assertThat(existingNode.deleted()).isTrue(); + + given(handleContext.body()) + .willReturn(TransactionBody.newBuilder().nodeDelete(txn).build()); + given(handleContext.writableStore(WritableNodeStore.class)).willReturn(writableStore); + // expect: + assertFailsWith(() -> subject.handle(handleContext), ResponseCodeEnum.NODE_DELETED); + } + + private TransactionBody newDeleteTxn() { + final var txnId = TransactionID.newBuilder().accountID(payerId).build(); + final var deleteFileBuilder = NodeDeleteTransactionBody.newBuilder().nodeId(WELL_KNOWN_NODE_ID); + return TransactionBody.newBuilder() + .transactionID(txnId) + .nodeDelete(deleteFileBuilder.build()) + .build(); + } + + private static void assertFailsWith(final Runnable something, final ResponseCodeEnum status) { + assertThatThrownBy(something::run) + .isInstanceOf(HandleException.class) + .extracting(ex -> ((HandleException) ex).getStatus()) + .isEqualTo(status); + } +} diff --git a/hedera-node/hedera-addressbook-service/build.gradle.kts b/hedera-node/hedera-addressbook-service/build.gradle.kts new file mode 100644 index 000000000000..33944bba9fea --- /dev/null +++ b/hedera-node/hedera-addressbook-service/build.gradle.kts @@ -0,0 +1,23 @@ +/* + * 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. + */ + +plugins { + id("com.hedera.gradle.services") + id("com.hedera.gradle.services-publish") + id("com.hedera.gradle.java-test-fixtures") +} + +description = "Hedera AddressBook Service API" diff --git a/hedera-node/hedera-addressbook-service/src/main/java/com/hedera/node/app/service/addressbook/AddressBookService.java b/hedera-node/hedera-addressbook-service/src/main/java/com/hedera/node/app/service/addressbook/AddressBookService.java new file mode 100644 index 000000000000..84f8e6520205 --- /dev/null +++ b/hedera-node/hedera-addressbook-service/src/main/java/com/hedera/node/app/service/addressbook/AddressBookService.java @@ -0,0 +1,55 @@ +/* + * 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.service.addressbook; + +import com.hedera.node.app.spi.RpcService; +import com.hedera.node.app.spi.RpcServiceFactory; +import com.hedera.pbj.runtime.RpcServiceDefinition; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.ServiceLoader; +import java.util.Set; + +/** + * Implements the HAPI Address Book Service. + */ +public interface AddressBookService extends RpcService { + + String NAME = "AddressBookService"; + + @NonNull + @Override + default String getServiceName() { + return NAME; + } + + @NonNull + @Override + default Set rpcDefinitions() { + return Set.of(AddressBookServiceDefinition.INSTANCE); + } + + /** + * Returns the concrete implementation instance of the service + * + * @return the implementation instance + */ + @NonNull + static AddressBookService getInstance() { + return RpcServiceFactory.loadService(AddressBookService.class, ServiceLoader.load(AddressBookService.class)); + } +} diff --git a/hedera-node/hedera-addressbook-service/src/main/java/com/hedera/node/app/service/addressbook/AddressBookServiceDefinition.java b/hedera-node/hedera-addressbook-service/src/main/java/com/hedera/node/app/service/addressbook/AddressBookServiceDefinition.java new file mode 100644 index 000000000000..a7f2f0ecc82b --- /dev/null +++ b/hedera-node/hedera-addressbook-service/src/main/java/com/hedera/node/app/service/addressbook/AddressBookServiceDefinition.java @@ -0,0 +1,78 @@ +/* + * 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.service.addressbook; + +import com.hedera.hapi.node.base.Transaction; +import com.hedera.hapi.node.transaction.Query; +import com.hedera.hapi.node.transaction.Response; +import com.hedera.hapi.node.transaction.TransactionResponse; +import com.hedera.pbj.runtime.RpcMethodDefinition; +import com.hedera.pbj.runtime.RpcServiceDefinition; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Set; + +/** + * The Addressbook Service provides the ability for Hedera Hashgraph to facilitate changes to the nodes used across the Hedera network. + */ +@SuppressWarnings("java:S6548") +public final class AddressBookServiceDefinition implements RpcServiceDefinition { + public static final AddressBookServiceDefinition INSTANCE = new AddressBookServiceDefinition(); + + private static final Set> methods = Set.of( + + // Prepare to add a new node to the network. + // When a valid council member initiates a HAPI transaction to add a new node, + // then the network should acknowledge the transaction and update the network’s Address Book within 24 + // hours. + // The added node will not be active until the network is upgraded + // Request is [NodeCreateTransactionBody](#proto.NodeCreateTransactionBody) + // + new RpcMethodDefinition<>("createNode", Transaction.class, TransactionResponse.class), + // Prepare to update the node to the network. + // The node will not be updated until the network is upgraded. + // Request is [NodeUpdateTransactionBody](#proto.NodeUpdateTransactionBody) + // + new RpcMethodDefinition<>("updateNode", Transaction.class, TransactionResponse.class), + // Prepare to delete the node from the network. + // The deleted node will not be deleted until the network is upgraded. + // Such a deleted node can never be reused. + // Request is [NodeDeleteTransactionBody](#proto.NodeDeleteTransactionBody) + // + new RpcMethodDefinition<>("deleteNode", Transaction.class, TransactionResponse.class), + + // Retrieves the node information by node Id. + // Request is [NodeGetInfoQuery](#proto.NodeGetInfoQuery) + // Response is [NodeGetInfoResponse](#proto.NodeGetInfoResponse) + // + new RpcMethodDefinition<>("getNodeInfo", Query.class, Response.class)); + + private AddressBookServiceDefinition() { + // Forbid instantiation + } + + @Override + @NonNull + public String basePath() { + return "proto.AddressbookService"; + } + + @Override + @NonNull + public Set> methods() { + return methods; + } +} diff --git a/hedera-node/hedera-addressbook-service/src/main/java/com/hedera/node/app/service/addressbook/ReadableNodeStore.java b/hedera-node/hedera-addressbook-service/src/main/java/com/hedera/node/app/service/addressbook/ReadableNodeStore.java new file mode 100644 index 000000000000..94417a1f1707 --- /dev/null +++ b/hedera-node/hedera-addressbook-service/src/main/java/com/hedera/node/app/service/addressbook/ReadableNodeStore.java @@ -0,0 +1,54 @@ +/* + * 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.service.addressbook; + +import com.hedera.hapi.node.state.addressbook.Node; +import edu.umd.cs.findbugs.annotations.Nullable; + +/** + * Provides read-only methods for interacting with the underlying data storage mechanisms for + * working with Nodes. + * + *

This class is not exported from the module. It is an internal implementation detail. + */ +public interface ReadableNodeStore { + + /** + * Returns the node needed. If the node doesn't exist returns failureReason. If the + * node exists , the failure reason will be null. + * + * @param nodeId node id being looked up + * @return node's metadata + */ + @Nullable + Node get(final long nodeId); + + /** + * Returns the number of nodes in the state. + * @return the number of nodes in the state. + */ + long sizeOfState(); + + /** + * Warms the system by preloading a node into memory + * + *

The default implementation is empty because preloading data into memory is only used for some implementations. + * + * @param nodeId the node id + */ + default void warm(final long nodeId) {} +} diff --git a/hedera-node/hedera-addressbook-service/src/main/java/com/hedera/node/app/service/addressbook/package-info.java b/hedera-node/hedera-addressbook-service/src/main/java/com/hedera/node/app/service/addressbook/package-info.java new file mode 100644 index 000000000000..c4f909c3cf9b --- /dev/null +++ b/hedera-node/hedera-addressbook-service/src/main/java/com/hedera/node/app/service/addressbook/package-info.java @@ -0,0 +1,5 @@ +/** + * This package is the base package of the address book service API. This module must not contain any + * sources outside this base package + */ +package com.hedera.node.app.service.addressbook; diff --git a/hedera-node/hedera-addressbook-service/src/main/java/module-info.java b/hedera-node/hedera-addressbook-service/src/main/java/module-info.java new file mode 100644 index 000000000000..0d3591ace542 --- /dev/null +++ b/hedera-node/hedera-addressbook-service/src/main/java/module-info.java @@ -0,0 +1,10 @@ +module com.hedera.node.app.service.addressbook { + exports com.hedera.node.app.service.addressbook; + + uses com.hedera.node.app.service.addressbook.AddressBookService; + + requires transitive com.hedera.node.app.spi; + requires transitive com.hedera.node.hapi; + requires transitive com.hedera.pbj.runtime; + requires static com.github.spotbugs.annotations; +} diff --git a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/RpcService.java b/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/RpcService.java new file mode 100644 index 000000000000..94001cf17a7a --- /dev/null +++ b/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/RpcService.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022-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.spi; + +import com.hedera.pbj.runtime.RpcServiceDefinition; +import com.swirlds.state.spi.Service; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Set; + +/** + * This interface defines the contract for a service that can expose RPC endpoints. + */ +public interface RpcService extends Service { + + /** + * If this service exposes RPC endpoints, then this method returns the RPC service definitions. + * + * @return The RPC service definitions if this service is exposed via RPC. + */ + @NonNull + Set rpcDefinitions(); +} diff --git a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/ServiceFactory.java b/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/RpcServiceFactory.java similarity index 90% rename from hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/ServiceFactory.java rename to hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/RpcServiceFactory.java index 2d4973e65fcb..7d4a69f49436 100644 --- a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/ServiceFactory.java +++ b/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/RpcServiceFactory.java @@ -22,14 +22,14 @@ import java.util.ServiceLoader; /** - * This class provides the ability to load {@link Service} implementations at runtime. The Java SPI + * This class provides the ability to load {@link RpcService} implementations at runtime. The Java SPI * (see {@link ServiceLoader}) is used to provide such information at runtime. Since we use the Java * module system the {@link ServiceLoader} instance can not be created in the factory. It must be * created in the module that add "uses" information for the module to the {@code module-info.java}. */ -public final class ServiceFactory { +public final class RpcServiceFactory { - private ServiceFactory() {} + private RpcServiceFactory() {} /** * This method returns a service instance of the given service that is provided by the Java SPI. @@ -41,7 +41,7 @@ private ServiceFactory() {} * @throws IllegalStateException if no or multiple services are found */ @NonNull - public static S loadService( + public static S loadService( @NonNull final Class type, @NonNull final ServiceLoader serviceLoader) { Objects.requireNonNull(type, "type must not be null"); Objects.requireNonNull(serviceLoader, "serviceLoader must not be null"); diff --git a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/metrics/StoreMetricsService.java b/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/metrics/StoreMetricsService.java index 4e1b546733fb..f4f84dbe5ba9 100644 --- a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/metrics/StoreMetricsService.java +++ b/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/metrics/StoreMetricsService.java @@ -30,7 +30,8 @@ enum StoreType { FILE, SLOT_STORAGE, CONTRACT, - SCHEDULE + SCHEDULE, + NODE } StoreMetrics get(@NonNull StoreType storeType, long capacity); diff --git a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/records/RecordCache.java b/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/records/RecordCache.java index e861f3ee4b43..b0803f62dd57 100644 --- a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/records/RecordCache.java +++ b/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/records/RecordCache.java @@ -28,7 +28,7 @@ import com.hedera.hapi.node.base.TransactionID; import com.hedera.hapi.node.transaction.TransactionReceipt; import com.hedera.hapi.node.transaction.TransactionRecord; -import com.hedera.node.app.spi.Service; +import com.hedera.node.app.spi.RpcService; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.ArrayList; @@ -39,7 +39,7 @@ import java.util.Set; /** - * Supplies {@link Service}s access to records and receipts. + * Supplies {@link RpcService}s access to records and receipts. * *

A receipt is added when this node ingests a new transaction, or when this node pre-handles a transaction ingested * on another node. A receipt in this state will have a status of diff --git a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/validation/Validations.java b/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/validation/Validations.java index 41723e83107e..0d7c0809536e 100644 --- a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/validation/Validations.java +++ b/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/validation/Validations.java @@ -64,11 +64,18 @@ public static T mustExist(@Nullable final T subject, @NonNull final Response @NonNull public static AccountID validateAccountID(@Nullable final AccountID subject, ResponseCodeEnum responseCodeEnum) throws PreCheckException { - final var result = validateNullableAccountID(subject); - // Cannot be null - if (result == null) { - throw new PreCheckException( - responseCodeEnum == null ? ResponseCodeEnum.INVALID_ACCOUNT_ID : responseCodeEnum); + AccountID result = null; + try { + result = validateNullableAccountID(subject); + // Cannot be null + if (result == null) { + throw new PreCheckException( + responseCodeEnum == null ? ResponseCodeEnum.INVALID_ACCOUNT_ID : responseCodeEnum); + } + } catch (PreCheckException e) { + if (responseCodeEnum != null) { + throw new PreCheckException(responseCodeEnum); + } else throw e; } return result; } diff --git a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/workflows/HandleContext.java b/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/workflows/HandleContext.java index 139c22eb54c5..c7cee4d6df26 100644 --- a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/workflows/HandleContext.java +++ b/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/workflows/HandleContext.java @@ -30,7 +30,6 @@ import com.hedera.node.app.spi.fees.FeeAccumulator; import com.hedera.node.app.spi.fees.FeeCalculator; import com.hedera.node.app.spi.fees.Fees; -import com.hedera.node.app.spi.info.NetworkInfo; import com.hedera.node.app.spi.records.BlockRecordInfo; import com.hedera.node.app.spi.records.RecordCache; import com.hedera.node.app.spi.signatures.SignatureVerification; @@ -41,6 +40,7 @@ import com.hedera.node.app.spi.workflows.record.RecordListCheckPoint; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.config.api.Configuration; +import com.swirlds.state.spi.info.NetworkInfo; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.time.Instant; diff --git a/hedera-node/hedera-app-spi/src/main/java/module-info.java b/hedera-node/hedera-app-spi/src/main/java/module-info.java index 9bed5c5ba71c..b5b418479d2a 100644 --- a/hedera-node/hedera-app-spi/src/main/java/module-info.java +++ b/hedera-node/hedera-app-spi/src/main/java/module-info.java @@ -1,16 +1,15 @@ module com.hedera.node.app.spi { requires transitive com.hedera.node.app.hapi.utils; requires transitive com.hedera.node.hapi; - requires transitive com.hedera.pbj.runtime; requires transitive com.swirlds.config.api; requires transitive com.swirlds.platform.core; requires transitive com.swirlds.state.api; + requires transitive com.hedera.pbj.runtime; requires static com.github.spotbugs.annotations; exports com.hedera.node.app.spi; exports com.hedera.node.app.spi.fees; exports com.hedera.node.app.spi.api; - exports com.hedera.node.app.spi.info; exports com.hedera.node.app.spi.key; exports com.hedera.node.app.spi.numbers; exports com.hedera.node.app.spi.workflows; diff --git a/hedera-node/hedera-app-spi/src/test/java/com/hedera/node/app/spi/state/SchemaTest.java b/hedera-node/hedera-app-spi/src/test/java/com/hedera/node/app/spi/state/SchemaTest.java index 1918786d15ca..670378710043 100644 --- a/hedera-node/hedera-app-spi/src/test/java/com/hedera/node/app/spi/state/SchemaTest.java +++ b/hedera-node/hedera-app-spi/src/test/java/com/hedera/node/app/spi/state/SchemaTest.java @@ -23,6 +23,7 @@ import com.hedera.node.app.spi.fixtures.state.MapWritableStates; import com.hedera.node.app.spi.fixtures.state.TestSchema; import com.swirlds.platform.test.fixtures.state.StateTestBase; +import com.swirlds.state.spi.Schema; import java.util.ArrayList; import java.util.Collections; import org.junit.jupiter.api.Disabled; diff --git a/hedera-node/hedera-app-spi/src/test/java/com/hedera/node/app/spi/state/StateDefinitionTest.java b/hedera-node/hedera-app-spi/src/test/java/com/hedera/node/app/spi/state/StateDefinitionTest.java index c2d41ce2a3ce..88396b509c0e 100644 --- a/hedera-node/hedera-app-spi/src/test/java/com/hedera/node/app/spi/state/StateDefinitionTest.java +++ b/hedera-node/hedera-app-spi/src/test/java/com/hedera/node/app/spi/state/StateDefinitionTest.java @@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.*; import com.hedera.pbj.runtime.Codec; +import com.swirlds.state.spi.StateDefinition; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; diff --git a/hedera-node/hedera-app-spi/src/testFixtures/java/com/hedera/node/app/spi/fixtures/TestService.java b/hedera-node/hedera-app-spi/src/testFixtures/java/com/hedera/node/app/spi/fixtures/TestService.java index 9c1fc1c3d1ff..b493f3989c17 100644 --- a/hedera-node/hedera-app-spi/src/testFixtures/java/com/hedera/node/app/spi/fixtures/TestService.java +++ b/hedera-node/hedera-app-spi/src/testFixtures/java/com/hedera/node/app/spi/fixtures/TestService.java @@ -19,10 +19,10 @@ import static java.util.Objects.requireNonNull; import com.hedera.node.app.hapi.utils.ethereum.EthTxData; -import com.hedera.node.app.spi.Service; import com.hedera.node.app.spi.fixtures.state.TestSchema; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.SchemaRegistry; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.SchemaRegistry; +import com.swirlds.state.spi.Service; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ArrayList; import java.util.List; 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 678566d0ef91..3f812ce724a1 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 @@ -20,11 +20,11 @@ import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.base.SemanticVersion; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.info.NodeInfo; -import com.hedera.node.app.spi.info.SelfNodeInfo; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.common.platform.NodeId; +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; diff --git a/hedera-node/hedera-app-spi/src/testFixtures/java/com/hedera/node/app/spi/fixtures/state/NoOpGenesisRecordsBuilder.java b/hedera-node/hedera-app-spi/src/testFixtures/java/com/hedera/node/app/spi/fixtures/state/NoOpGenesisRecordsBuilder.java index c793f847e5af..d37d53cd24a0 100644 --- a/hedera-node/hedera-app-spi/src/testFixtures/java/com/hedera/node/app/spi/fixtures/state/NoOpGenesisRecordsBuilder.java +++ b/hedera-node/hedera-app-spi/src/testFixtures/java/com/hedera/node/app/spi/fixtures/state/NoOpGenesisRecordsBuilder.java @@ -33,7 +33,7 @@ */ import com.hedera.hapi.node.state.token.Account; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; +import com.swirlds.state.spi.workflows.record.GenesisRecordsBuilder; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.SortedSet; diff --git a/hedera-node/hedera-app-spi/src/testFixtures/java/com/hedera/node/app/spi/fixtures/state/TestSchema.java b/hedera-node/hedera-app-spi/src/testFixtures/java/com/hedera/node/app/spi/fixtures/state/TestSchema.java index 387d6e1db7f3..bfcc29bb89ec 100644 --- a/hedera-node/hedera-app-spi/src/testFixtures/java/com/hedera/node/app/spi/fixtures/state/TestSchema.java +++ b/hedera-node/hedera-app-spi/src/testFixtures/java/com/hedera/node/app/spi/fixtures/state/TestSchema.java @@ -17,9 +17,9 @@ package com.hedera.node.app.spi.fixtures.state; import com.hedera.hapi.node.base.SemanticVersion; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.StateDefinition; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.StateDefinition; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.HashSet; 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 7943e0ddfa64..2cbc29a64568 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 @@ -6,10 +6,10 @@ requires transitive com.hedera.node.app.spi; requires transitive com.hedera.node.hapi; - requires transitive com.hedera.pbj.runtime; requires transitive com.swirlds.config.api; requires transitive com.swirlds.platform.core.test.fixtures; requires transitive com.swirlds.state.api; + requires transitive com.hedera.pbj.runtime; requires transitive org.apache.logging.log4j; requires transitive org.assertj.core; requires transitive org.junit.jupiter.api; @@ -17,8 +17,6 @@ requires com.swirlds.common; requires com.swirlds.platform.core; requires org.apache.logging.log4j.core; - - // Temporarily needed until FakePreHandleContext can be removed - requires static com.hedera.node.app.service.token; + requires static com.hedera.node.app.service.token; // TMP until FakePreHandleContext can be removed 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 dcabacedc823..f45d6a2b4141 100644 --- a/hedera-node/hedera-app/build.gradle.kts +++ b/hedera-node/hedera-app/build.gradle.kts @@ -67,6 +67,7 @@ itestModuleInfo { requires("com.hedera.pbj.runtime") requires("com.swirlds.common") requires("com.swirlds.config.api") + requires("com.swirlds.state.api") requires("com.swirlds.platform.core.test.fixtures") requires("com.hedera.node.hapi") requires("com.swirlds.metrics.api") diff --git a/hedera-node/hedera-app/src/itest/java/grpc/GrpcTestBase.java b/hedera-node/hedera-app/src/itest/java/grpc/GrpcTestBase.java index a5815f8bbf77..e693848334d6 100644 --- a/hedera-node/hedera-app/src/itest/java/grpc/GrpcTestBase.java +++ b/hedera-node/hedera-app/src/itest/java/grpc/GrpcTestBase.java @@ -22,7 +22,7 @@ import com.hedera.hapi.node.transaction.TransactionResponse; import com.hedera.node.app.grpc.impl.netty.NettyGrpcServerManager; import com.hedera.node.app.services.ServicesRegistryImpl; -import com.hedera.node.app.spi.Service; +import com.hedera.node.app.spi.RpcService; import com.hedera.node.app.spi.fixtures.state.NoOpGenesisRecordsBuilder; import com.hedera.node.app.workflows.ingest.IngestWorkflow; import com.hedera.node.app.workflows.query.QueryWorkflow; @@ -43,7 +43,9 @@ import com.swirlds.config.api.source.ConfigSource; import com.swirlds.metrics.api.Metrics; import com.swirlds.platform.test.fixtures.state.TestBase; +import com.swirlds.state.spi.SchemaRegistry; import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import io.grpc.CallOptions; import io.grpc.Channel; import io.grpc.MethodDescriptor; @@ -63,7 +65,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import org.assertj.core.api.Assumptions; -import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.AfterEach; /** @@ -141,7 +142,7 @@ protected void registerIngest( /** Starts the grpcServer and sets up the clients. */ protected void startServer() { - final var testService = new Service() { + final var testService = new RpcService() { @NonNull @Override public String getServiceName() { @@ -173,6 +174,11 @@ public String basePath() { } }); } + + @Override + public void registerSchemas(@NonNull SchemaRegistry registry) { + // no-op + } }; final var servicesRegistry = 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 1e3b44abfafc..1e50b82222e8 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 @@ -59,7 +59,6 @@ import com.hedera.node.app.service.token.impl.schemas.SyntheticRecordsGenerator; import com.hedera.node.app.service.util.impl.UtilServiceImpl; import com.hedera.node.app.services.ServicesRegistry; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; import com.hedera.node.app.state.HederaLifecyclesImpl; import com.hedera.node.app.state.merkle.MerkleHederaState; import com.hedera.node.app.state.recordcache.RecordCacheService; @@ -95,6 +94,7 @@ import com.swirlds.platform.system.status.PlatformStatus; import com.swirlds.platform.system.transaction.Transaction; import com.swirlds.state.HederaState; +import com.swirlds.state.spi.workflows.record.GenesisRecordsBuilder; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.nio.charset.Charset; 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 00da7c4704cd..3e74118d2a5b 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 @@ -38,8 +38,6 @@ import com.hedera.node.app.service.mono.utils.SystemExits; import com.hedera.node.app.services.ServicesInjectionModule; import com.hedera.node.app.services.ServicesRegistry; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.info.SelfNodeInfo; import com.hedera.node.app.spi.metrics.StoreMetricsService; import com.hedera.node.app.spi.records.RecordCache; import com.hedera.node.app.state.HederaStateInjectionModule; @@ -60,6 +58,8 @@ import com.swirlds.platform.system.InitTrigger; import com.swirlds.platform.system.Platform; import com.swirlds.platform.system.SoftwareVersion; +import com.swirlds.state.spi.info.NetworkInfo; +import com.swirlds.state.spi.info.SelfNodeInfo; import dagger.BindsInstance; import dagger.Component; import java.nio.charset.Charset; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/OrderedServiceMigrator.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/OrderedServiceMigrator.java index 968edfda4550..ee16b503a618 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/OrderedServiceMigrator.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/OrderedServiceMigrator.java @@ -22,14 +22,14 @@ import com.hedera.node.app.ids.EntityIdService; import com.hedera.node.app.ids.WritableEntityIdStore; import com.hedera.node.app.services.ServicesRegistry; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.state.SchemaRegistry; import com.hedera.node.app.state.merkle.MerkleHederaState; import com.hedera.node.app.state.merkle.MerkleSchemaRegistry; import com.hedera.node.config.VersionedConfiguration; import com.swirlds.metrics.api.Metrics; import com.swirlds.state.HederaState; +import com.swirlds.state.spi.SchemaRegistry; +import com.swirlds.state.spi.Service; +import com.swirlds.state.spi.info.NetworkInfo; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.HashMap; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/fees/FeeService.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/fees/FeeService.java index efac20b0b7f4..67c753428fa7 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/fees/FeeService.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/fees/FeeService.java @@ -17,8 +17,8 @@ package com.hedera.node.app.fees; import com.hedera.node.app.fees.schemas.V0490FeeSchema; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.state.SchemaRegistry; +import com.swirlds.state.spi.SchemaRegistry; +import com.swirlds.state.spi.Service; import edu.umd.cs.findbugs.annotations.NonNull; public class FeeService implements Service { diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/fees/schemas/V0490FeeSchema.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/fees/schemas/V0490FeeSchema.java index 3d7071403587..596c927ad2ad 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/fees/schemas/V0490FeeSchema.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/fees/schemas/V0490FeeSchema.java @@ -21,11 +21,11 @@ import com.hedera.hapi.node.transaction.ExchangeRate; import com.hedera.hapi.node.transaction.ExchangeRateSet; import com.hedera.node.app.service.mono.state.submerkle.ExchangeRates; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.StateDefinition; import com.hedera.node.config.data.BootstrapConfig; import com.swirlds.platform.state.spi.WritableSingletonStateBase; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.StateDefinition; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Set; import org.apache.logging.log4j.LogManager; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/grpc/impl/netty/NettyGrpcServerManager.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/grpc/impl/netty/NettyGrpcServerManager.java index 242b848eac25..dfdae706ff8b 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/grpc/impl/netty/NettyGrpcServerManager.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/grpc/impl/netty/NettyGrpcServerManager.java @@ -22,6 +22,7 @@ import com.hedera.hapi.node.base.Transaction; import com.hedera.node.app.grpc.GrpcServerManager; import com.hedera.node.app.services.ServicesRegistry; +import com.hedera.node.app.spi.RpcService; import com.hedera.node.app.workflows.ingest.IngestWorkflow; import com.hedera.node.app.workflows.query.QueryWorkflow; import com.hedera.node.config.ConfigProvider; @@ -114,9 +115,14 @@ public NettyGrpcServerManager( requireNonNull(queryWorkflow); requireNonNull(metrics); - // Convert the various RPC service definitions into transaction or query endpoints using the GrpcServiceBuilder. + // Convert the various RPC service definitions into transaction or query endpoints using the + // GrpcServiceBuilder. services = servicesRegistry.registrations().stream() .map(ServicesRegistry.Registration::service) + // Not all services are RPC services, but here we need RPC services only. The main difference + // between RPC service and a service is that the RPC service has RPC definition. + .filter(v -> v instanceof RpcService) + .map(v -> (RpcService) v) .flatMap(s -> s.rpcDefinitions().stream()) .map(d -> { final var builder = new GrpcServiceBuilder(d.basePath(), ingestWorkflow, queryWorkflow); diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/ids/EntityIdService.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/ids/EntityIdService.java index e655cf21453f..ff0ff94dffe7 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/ids/EntityIdService.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/ids/EntityIdService.java @@ -17,8 +17,8 @@ package com.hedera.node.app.ids; import com.hedera.node.app.ids.schemas.V0490EntityIdSchema; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.state.SchemaRegistry; +import com.swirlds.state.spi.SchemaRegistry; +import com.swirlds.state.spi.Service; import edu.umd.cs.findbugs.annotations.NonNull; /** diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/ids/schemas/V0490EntityIdSchema.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/ids/schemas/V0490EntityIdSchema.java index 422d0bcab4d6..8423c9a0bd0f 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/ids/schemas/V0490EntityIdSchema.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/ids/schemas/V0490EntityIdSchema.java @@ -18,11 +18,11 @@ import com.hedera.hapi.node.base.SemanticVersion; import com.hedera.hapi.node.state.common.EntityNumber; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.StateDefinition; import com.hedera.node.config.data.HederaConfig; +import com.swirlds.state.spi.MigrationContext; import com.swirlds.state.spi.ReadableStates; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.StateDefinition; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Set; import org.apache.logging.log4j.LogManager; 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 7603ef83eaaa..2357195fb829 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,9 +18,9 @@ import com.hedera.hapi.node.base.AccountID; import com.hedera.node.app.annotations.NodeSelfId; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.info.SelfNodeInfo; import com.hedera.node.app.spi.numbers.HederaAccountNumbers; +import com.swirlds.state.spi.info.NetworkInfo; +import com.swirlds.state.spi.info.SelfNodeInfo; import dagger.Binds; import dagger.Module; import dagger.Provides; 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 index b8b9dd13e374..d90e36f7b3ae 100644 --- 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 @@ -18,14 +18,14 @@ import static java.util.Objects.requireNonNull; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.info.NodeInfo; -import com.hedera.node.app.spi.info.SelfNodeInfo; import com.hedera.node.config.ConfigProvider; import com.hedera.node.config.data.LedgerConfig; import com.hedera.pbj.runtime.io.buffer.Bytes; 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; 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 cb58f83c5da2..65cb3981621e 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 @@ -20,9 +20,9 @@ import static java.util.Objects.requireNonNull; import com.hedera.hapi.node.base.AccountID; -import com.hedera.node.app.spi.info.NodeInfo; 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; public record NodeInfoImpl( 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 index f5f6e14dff61..c21fd523ca30 100644 --- 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 @@ -21,10 +21,10 @@ import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.base.SemanticVersion; -import com.hedera.node.app.spi.info.SelfNodeInfo; import com.hedera.node.app.version.HederaSoftwareVersion; 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; public record SelfNodeInfoImpl( diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/BlockRecordService.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/BlockRecordService.java index 3a9a9b22b80f..55137bb01971 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/BlockRecordService.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/BlockRecordService.java @@ -19,8 +19,8 @@ import com.hedera.hapi.node.base.Timestamp; import com.hedera.node.app.records.impl.BlockRecordManagerImpl; import com.hedera.node.app.records.schemas.V0490BlockRecordSchema; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.state.SchemaRegistry; +import com.swirlds.state.spi.SchemaRegistry; +import com.swirlds.state.spi.Service; import edu.umd.cs.findbugs.annotations.NonNull; import javax.inject.Singleton; 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 7b4da4b8ea61..0a46197a298d 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 @@ -25,9 +25,9 @@ import com.hedera.hapi.streams.HashObject; import com.hedera.node.app.annotations.CommonExecutor; import com.hedera.node.app.records.impl.BlockRecordStreamProducer; -import com.hedera.node.app.spi.info.SelfNodeInfo; 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; 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 d5ad007a8d9e..6fd08c4790a9 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 @@ -23,9 +23,9 @@ import com.hedera.hapi.streams.HashAlgorithm; import com.hedera.hapi.streams.HashObject; import com.hedera.node.app.records.impl.BlockRecordStreamProducer; -import com.hedera.node.app.spi.info.SelfNodeInfo; 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; 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 6882463273d0..8720dcbcfb88 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 @@ -21,10 +21,10 @@ import com.hedera.node.app.records.impl.producers.BlockRecordWriter; import com.hedera.node.app.records.impl.producers.BlockRecordWriterFactory; import com.hedera.node.app.records.impl.producers.formats.v6.BlockRecordWriterV6; -import com.hedera.node.app.spi.info.SelfNodeInfo; 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 edu.umd.cs.findbugs.annotations.NonNull; import java.nio.file.FileSystem; import javax.inject.Inject; 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 499c366d01af..c1ecdbbab2d1 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 @@ -38,7 +38,6 @@ import com.hedera.hapi.streams.SidecarMetadata; import com.hedera.node.app.records.impl.producers.BlockRecordWriter; import com.hedera.node.app.records.impl.producers.SerializedSingleTransactionRecord; -import com.hedera.node.app.spi.info.NodeInfo; import com.hedera.node.config.data.BlockRecordStreamConfig; import com.hedera.pbj.runtime.ProtoWriterTools; import com.hedera.pbj.runtime.io.buffer.Bytes; @@ -46,6 +45,7 @@ import com.swirlds.common.crypto.DigestType; import com.swirlds.common.crypto.HashingOutputStream; import com.swirlds.common.stream.Signer; +import com.swirlds.state.spi.info.NodeInfo; import edu.umd.cs.findbugs.annotations.NonNull; import java.io.BufferedOutputStream; import java.io.IOException; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/schemas/V0490BlockRecordSchema.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/schemas/V0490BlockRecordSchema.java index 088afbbd8dd0..cd793fd54b42 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/schemas/V0490BlockRecordSchema.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/records/schemas/V0490BlockRecordSchema.java @@ -25,11 +25,11 @@ import com.hedera.node.app.records.impl.codec.RunningHashesTranslator; import com.hedera.node.app.service.mono.state.merkle.MerkleNetworkContext; import com.hedera.node.app.service.mono.stream.RecordsRunningHashLeaf; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.StateDefinition; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.platform.state.spi.WritableSingletonStateBase; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.StateDefinition; import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.util.Set; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/ServicesRegistry.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/ServicesRegistry.java index 287632fe4689..9199004580d5 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/ServicesRegistry.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/ServicesRegistry.java @@ -18,9 +18,9 @@ import static java.util.Objects.requireNonNull; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.state.SchemaRegistry; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; +import com.swirlds.state.spi.SchemaRegistry; +import com.swirlds.state.spi.Service; +import com.swirlds.state.spi.workflows.record.GenesisRecordsBuilder; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Comparator; import java.util.Set; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/ServicesRegistryImpl.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/ServicesRegistryImpl.java index 1f4e786d3fe3..e753172b857e 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/ServicesRegistryImpl.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/services/ServicesRegistryImpl.java @@ -18,11 +18,11 @@ import static java.util.Objects.requireNonNull; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; import com.hedera.node.app.state.merkle.MerkleSchemaRegistry; import com.hedera.node.app.state.merkle.SchemaApplications; import com.swirlds.common.constructable.ConstructableRegistry; +import com.swirlds.state.spi.Service; +import com.swirlds.state.spi.workflows.record.GenesisRecordsBuilder; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Collections; import java.util.SortedSet; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/logging/TransactionStateLogger.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/logging/TransactionStateLogger.java index ffbc3ae483f0..a467b948f672 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/logging/TransactionStateLogger.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/logging/TransactionStateLogger.java @@ -26,7 +26,6 @@ import com.hedera.hapi.node.base.TransactionID; import com.hedera.hapi.node.transaction.TransactionBody; import com.hedera.hapi.node.transaction.TransactionRecord; -import com.hedera.node.app.spi.info.NodeInfo; import com.hedera.node.app.workflows.prehandle.PreHandleResult; import com.swirlds.fcqueue.FCQueue; import com.swirlds.platform.state.merkle.disk.OnDiskKey; @@ -37,6 +36,7 @@ import com.swirlds.platform.system.Round; import com.swirlds.platform.system.events.ConsensusEvent; import com.swirlds.platform.system.transaction.ConsensusTransaction; +import com.swirlds.state.spi.info.NodeInfo; import com.swirlds.virtualmap.VirtualMap; import com.swirlds.virtualmap.internal.merkle.VirtualLeafNode; import edu.umd.cs.findbugs.annotations.NonNull; 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 c4c81a8ba900..8c79780b1a9c 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 @@ -25,15 +25,8 @@ import com.hedera.hapi.node.base.SemanticVersion; import com.hedera.hapi.util.HapiUtils; import com.hedera.node.app.ids.WritableEntityIdStore; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.info.NetworkInfo; import com.hedera.node.app.spi.state.FilteredReadableStates; import com.hedera.node.app.spi.state.FilteredWritableStates; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.SchemaRegistry; -import com.hedera.node.app.spi.state.StateDefinition; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; import com.hedera.node.app.workflows.handle.record.MigrationContextImpl; import com.swirlds.common.constructable.ClassConstructorPair; import com.swirlds.common.constructable.ConstructableRegistry; @@ -56,7 +49,14 @@ import com.swirlds.platform.state.merkle.singleton.StringLeaf; import com.swirlds.platform.state.merkle.singleton.ValueLeaf; import com.swirlds.state.HederaState; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.SchemaRegistry; +import com.swirlds.state.spi.Service; +import com.swirlds.state.spi.StateDefinition; import com.swirlds.state.spi.WritableStates; +import com.swirlds.state.spi.info.NetworkInfo; +import com.swirlds.state.spi.workflows.record.GenesisRecordsBuilder; import com.swirlds.virtualmap.VirtualMap; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/SchemaApplicationType.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/SchemaApplicationType.java index 42bec45b9a33..329ae876edc5 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/SchemaApplicationType.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/SchemaApplicationType.java @@ -16,7 +16,7 @@ package com.hedera.node.app.state.merkle; -import com.hedera.node.app.spi.state.Schema; +import com.swirlds.state.spi.Schema; /** * Enumerates the ways the {@link MerkleSchemaRegistry} may apply a {@link Schema} diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/SchemaApplications.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/SchemaApplications.java index a0b5b4f4b63d..579140011d77 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/SchemaApplications.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/SchemaApplications.java @@ -24,7 +24,7 @@ import static java.util.Objects.requireNonNull; import com.hedera.hapi.node.base.SemanticVersion; -import com.hedera.node.app.spi.state.Schema; +import com.swirlds.state.spi.Schema; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.EnumSet; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/StateMetadata.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/StateMetadata.java index 340ff3aeab13..5c375bacbeab 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/StateMetadata.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/StateMetadata.java @@ -16,9 +16,9 @@ package com.hedera.node.app.state.merkle; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.StateDefinition; import com.swirlds.platform.state.merkle.StateUtils; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.StateDefinition; import edu.umd.cs.findbugs.annotations.NonNull; /** diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/VersionUtils.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/VersionUtils.java index a1ed877d0908..db214062780a 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/VersionUtils.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/merkle/VersionUtils.java @@ -16,7 +16,7 @@ package com.hedera.node.app.state.merkle; -import static com.hedera.hapi.util.HapiUtils.SEMANTIC_VERSION_COMPARATOR; +import static com.swirlds.state.spi.HapiUtils.SEMANTIC_VERSION_COMPARATOR; import com.hedera.hapi.node.base.SemanticVersion; import edu.umd.cs.findbugs.annotations.Nullable; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/recordcache/RecordCacheService.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/recordcache/RecordCacheService.java index 9dccdfbd1b7d..31e696fa0069 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/recordcache/RecordCacheService.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/recordcache/RecordCacheService.java @@ -16,9 +16,9 @@ package com.hedera.node.app.state.recordcache; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.state.SchemaRegistry; import com.hedera.node.app.state.recordcache.schemas.V0490RecordCacheSchema; +import com.swirlds.state.spi.SchemaRegistry; +import com.swirlds.state.spi.Service; import edu.umd.cs.findbugs.annotations.NonNull; /** diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/recordcache/schemas/V0490RecordCacheSchema.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/recordcache/schemas/V0490RecordCacheSchema.java index e0deebc4c878..a27b51fd3996 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/recordcache/schemas/V0490RecordCacheSchema.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/state/recordcache/schemas/V0490RecordCacheSchema.java @@ -24,10 +24,10 @@ import com.hedera.hapi.node.transaction.TransactionRecord; import com.hedera.node.app.service.mono.pbj.PbjConverter; import com.hedera.node.app.service.mono.state.submerkle.ExpirableTxnRecord; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.StateDefinition; import com.swirlds.platform.state.spi.WritableQueueStateBase; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.StateDefinition; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.List; import java.util.Set; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/throttle/CongestionThrottleService.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/throttle/CongestionThrottleService.java index 0344c544002a..7a2d110d1207 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/throttle/CongestionThrottleService.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/throttle/CongestionThrottleService.java @@ -16,9 +16,9 @@ package com.hedera.node.app.throttle; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.state.SchemaRegistry; import com.hedera.node.app.throttle.schemas.V0490CongestionThrottleSchema; +import com.swirlds.state.spi.SchemaRegistry; +import com.swirlds.state.spi.Service; import edu.umd.cs.findbugs.annotations.NonNull; import javax.inject.Singleton; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/throttle/schemas/V0490CongestionThrottleSchema.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/throttle/schemas/V0490CongestionThrottleSchema.java index 11c7ada29d57..0cdfafe35f61 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/throttle/schemas/V0490CongestionThrottleSchema.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/throttle/schemas/V0490CongestionThrottleSchema.java @@ -23,9 +23,9 @@ import com.hedera.hapi.node.state.throttles.ThrottleUsageSnapshots; import com.hedera.node.app.hapi.utils.throttles.DeterministicThrottle; import com.hedera.node.app.service.mono.pbj.PbjConverter; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.StateDefinition; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.StateDefinition; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Arrays; import java.util.Set; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/util/MonoMigrationUtils.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/util/MonoMigrationUtils.java index e28d06e54853..f12b905913bb 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/util/MonoMigrationUtils.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/util/MonoMigrationUtils.java @@ -59,7 +59,6 @@ import com.hedera.node.app.service.networkadmin.impl.schemas.V0490FreezeSchema; import com.hedera.node.app.service.schedule.impl.schemas.V0490ScheduleSchema; import com.hedera.node.app.service.token.impl.schemas.V0490TokenSchema; -import com.hedera.node.app.spi.state.Schema; import com.hedera.node.app.state.merkle.MerkleHederaState; import com.hedera.node.app.state.recordcache.schemas.V0490RecordCacheSchema; import com.hedera.node.app.throttle.schemas.V0490CongestionThrottleSchema; @@ -68,6 +67,7 @@ import com.swirlds.metrics.api.Metrics; import com.swirlds.platform.system.InitTrigger; import com.swirlds.state.HederaState; +import com.swirlds.state.spi.Schema; import com.swirlds.virtualmap.VirtualMap; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ArrayList; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/version/HederaSoftwareVersion.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/version/HederaSoftwareVersion.java index fcde2fe2cca6..ce3998d86181 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/version/HederaSoftwareVersion.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/version/HederaSoftwareVersion.java @@ -16,7 +16,7 @@ package com.hedera.node.app.version; -import static com.hedera.hapi.util.HapiUtils.SEMANTIC_VERSION_COMPARATOR; +import static com.swirlds.state.spi.HapiUtils.SEMANTIC_VERSION_COMPARATOR; import com.hedera.hapi.node.base.SemanticVersion; import com.hedera.hapi.util.HapiUtils; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/HandleContextImpl.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/HandleContextImpl.java index 93acdc55d693..1ff545cf34eb 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/HandleContextImpl.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/HandleContextImpl.java @@ -65,7 +65,6 @@ import com.hedera.node.app.spi.fees.FeeCalculator; import com.hedera.node.app.spi.fees.FeeContext; import com.hedera.node.app.spi.fees.Fees; -import com.hedera.node.app.spi.info.NetworkInfo; import com.hedera.node.app.spi.metrics.StoreMetricsService; import com.hedera.node.app.spi.records.BlockRecordInfo; import com.hedera.node.app.spi.records.RecordCache; @@ -102,6 +101,7 @@ import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.config.api.Configuration; import com.swirlds.platform.state.PlatformState; +import com.swirlds.state.spi.info.NetworkInfo; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.time.Instant; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/HandleWorkflow.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/HandleWorkflow.java index 160ba739e2ae..cc1d3833c9ed 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/HandleWorkflow.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/HandleWorkflow.java @@ -86,8 +86,6 @@ import com.hedera.node.app.spi.authorization.SystemPrivilege; import com.hedera.node.app.spi.fees.FeeAccumulator; import com.hedera.node.app.spi.fees.Fees; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.info.NodeInfo; import com.hedera.node.app.spi.metrics.StoreMetricsService; import com.hedera.node.app.spi.signatures.SignatureVerification; import com.hedera.node.app.spi.workflows.HandleContext; @@ -128,6 +126,8 @@ import com.swirlds.platform.system.events.ConsensusEvent; import com.swirlds.platform.system.transaction.ConsensusTransaction; import com.swirlds.state.HederaState; +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.time.Instant; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/record/GenesisRecordsConsensusHook.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/record/GenesisRecordsConsensusHook.java index f707318b3621..b2191a8bb443 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/record/GenesisRecordsConsensusHook.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/record/GenesisRecordsConsensusHook.java @@ -32,7 +32,7 @@ import com.hedera.node.app.records.ReadableBlockRecordStore; import com.hedera.node.app.service.token.records.GenesisAccountRecordBuilder; import com.hedera.node.app.service.token.records.TokenContext; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; +import com.swirlds.state.spi.workflows.record.GenesisRecordsBuilder; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.Comparator; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/record/MigrationContextImpl.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/record/MigrationContextImpl.java index 2989e17cae2d..20ed7f5a4519 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/record/MigrationContextImpl.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/handle/record/MigrationContextImpl.java @@ -20,14 +20,14 @@ import com.hedera.hapi.node.base.SemanticVersion; import com.hedera.node.app.ids.WritableEntityIdStore; -import com.hedera.node.app.spi.info.NetworkInfo; import com.hedera.node.app.spi.state.FilteredWritableStates; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; import com.hedera.node.app.state.merkle.MerkleHederaState; import com.swirlds.config.api.Configuration; +import com.swirlds.state.spi.MigrationContext; import com.swirlds.state.spi.ReadableStates; import com.swirlds.state.spi.WritableStates; +import com.swirlds.state.spi.info.NetworkInfo; +import com.swirlds.state.spi.workflows.record.GenesisRecordsBuilder; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.Map; diff --git a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/query/QueryDispatcher.java b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/query/QueryDispatcher.java index 3a94fa1d68e2..4b7919f9e4e4 100644 --- a/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/query/QueryDispatcher.java +++ b/hedera-node/hedera-app/src/main/java/com/hedera/node/app/workflows/query/QueryDispatcher.java @@ -87,6 +87,7 @@ public QueryHandler getHandler(@NonNull final Query query) { case TOKEN_GET_NFT_INFO -> handlers.tokenGetNftInfoHandler(); case TOKEN_GET_NFT_INFOS -> handlers.tokenGetNftInfosHandler(); + case NODE_GET_INFO -> throw new UnsupportedOperationException("Not implemented yet"); case UNSET -> throw new UnsupportedOperationException(QUERY_NOT_SET); }; } diff --git a/hedera-node/hedera-app/src/main/java/module-info.java b/hedera-node/hedera-app/src/main/java/module-info.java index 76c04a44ec5a..9c1ef90808f0 100644 --- a/hedera-node/hedera-app/src/main/java/module-info.java +++ b/hedera-node/hedera-app/src/main/java/module-info.java @@ -16,12 +16,12 @@ requires transitive com.hedera.node.app.spi; requires transitive com.hedera.node.config; requires transitive com.hedera.node.hapi; - requires transitive com.hedera.pbj.runtime; requires transitive com.swirlds.common; requires transitive com.swirlds.config.api; requires transitive com.swirlds.metrics.api; requires transitive com.swirlds.platform.core; requires transitive com.swirlds.state.api; + requires transitive com.hedera.pbj.runtime; requires transitive dagger; requires transitive grpc.stub; requires transitive javax.inject; @@ -31,9 +31,6 @@ requires com.hedera.node.app.service.file; requires com.hedera.node.app.service.network.admin; requires com.hedera.node.app.service.util; - requires com.google.common; - requires com.google.protobuf; - requires com.hedera.evm; requires com.swirlds.base; requires com.swirlds.config.extensions; requires com.swirlds.fcqueue; @@ -41,6 +38,9 @@ requires com.swirlds.merkle; requires com.swirlds.merkledb; requires com.swirlds.virtualmap; + requires com.google.common; + requires com.google.protobuf; + requires com.hedera.evm; requires grpc.netty; requires io.grpc; requires io.netty.handler; diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/platform/event/EventMigrationTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/platform/event/EventMigrationTest.java index dcb49ed6cce7..24cdd2ec7253 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/platform/event/EventMigrationTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/platform/event/EventMigrationTest.java @@ -19,18 +19,18 @@ import com.hedera.node.app.service.mono.context.properties.SerializableSemVers; import com.swirlds.common.constructable.ConstructableRegistry; import com.swirlds.common.constructable.ConstructableRegistryException; -import com.swirlds.common.crypto.CryptographyHolder; import com.swirlds.common.crypto.Hash; +import com.swirlds.platform.event.GossipEvent; +import com.swirlds.platform.event.hashing.StatefulEventHasher; import com.swirlds.platform.recovery.internal.EventStreamSingleFileIterator; import com.swirlds.platform.system.StaticSoftwareVersion; -import com.swirlds.platform.system.events.BaseEventHashedData; +import com.swirlds.platform.system.events.EventDescriptor; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.util.HashSet; import java.util.Objects; import java.util.Set; -import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -69,13 +69,13 @@ public void migration() throws URISyntaxException, IOException { .toPath(), false)) { while (iterator.hasNext()) { - final BaseEventHashedData hashedData = - iterator.next().getGossipEvent().getHashedData(); + final GossipEvent gossipEvent = iterator.next().getGossipEvent(); + new StatefulEventHasher().hashEvent(gossipEvent); numEvents++; - CryptographyHolder.get().digestSync(hashedData); - eventHashes.add(hashedData.getHash()); - Stream.of(hashedData.getSelfParentHash(), hashedData.getOtherParentHash()) + eventHashes.add(gossipEvent.getHash()); + gossipEvent.getAllParents().stream() .filter(Objects::nonNull) + .map(EventDescriptor::getHash) .forEach(parentHashes::add); } } diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/services/ServicesRegistryImplTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/services/ServiceRegistryImplTest.java similarity index 96% rename from hedera-node/hedera-app/src/test/java/com/hedera/node/app/services/ServicesRegistryImplTest.java rename to hedera-node/hedera-app/src/test/java/com/hedera/node/app/services/ServiceRegistryImplTest.java index 6b6a239baa42..c525100faf95 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/services/ServicesRegistryImplTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/services/ServiceRegistryImplTest.java @@ -25,10 +25,10 @@ import com.hedera.hapi.node.base.Timestamp; import com.hedera.node.app.spi.fixtures.TestService; import com.hedera.node.app.spi.fixtures.state.TestSchema; -import com.hedera.node.app.spi.state.StateDefinition; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; import com.swirlds.common.constructable.ConstructableRegistry; import com.swirlds.common.constructable.ConstructableRegistryException; +import com.swirlds.state.spi.StateDefinition; +import com.swirlds.state.spi.workflows.record.GenesisRecordsBuilder; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; 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 f67628abb85a..d451da9386db 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 @@ -26,24 +26,23 @@ import com.hedera.node.app.OrderedServiceMigrator; import com.hedera.node.app.ids.EntityIdService; import com.hedera.node.app.services.ServicesRegistryImpl; -import com.hedera.node.app.spi.Service; import com.hedera.node.app.spi.fixtures.state.NoOpGenesisRecordsBuilder; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.SchemaRegistry; -import com.hedera.node.app.spi.state.StateDefinition; import com.hedera.node.config.VersionedConfigImpl; import com.hedera.node.config.testfixtures.HederaTestConfigBuilder; import com.swirlds.common.constructable.ConstructableRegistry; import com.swirlds.metrics.api.Metrics; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.SchemaRegistry; +import com.swirlds.state.spi.Service; +import com.swirlds.state.spi.StateDefinition; import com.swirlds.state.spi.WritableStates; +import com.swirlds.state.spi.info.NetworkInfo; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.assertj.core.api.Assertions; -import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -284,7 +283,7 @@ private static class DependentService implements Service { static final String NAME = "DependentService"; static final String STATE_KEY = "DS_MAPPINGS"; - @NotNull + @NonNull @Override public String getServiceName() { return NAME; diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/MerkleHederaStateTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/MerkleHederaStateTest.java index f86654a4bc14..8d57fa0ad47e 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/MerkleHederaStateTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/MerkleHederaStateTest.java @@ -21,7 +21,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import com.hedera.node.app.spi.fixtures.state.TestSchema; -import com.hedera.node.app.spi.state.StateDefinition; import com.swirlds.base.state.MutabilityException; import com.swirlds.common.context.PlatformContext; import com.swirlds.common.merkle.MerkleNode; @@ -38,9 +37,12 @@ import com.swirlds.state.spi.ReadableKVState; import com.swirlds.state.spi.ReadableQueueState; import com.swirlds.state.spi.ReadableSingletonState; +import com.swirlds.state.spi.StateDefinition; import com.swirlds.state.spi.WritableKVState; import com.swirlds.state.spi.WritableQueueState; import com.swirlds.state.spi.WritableSingletonState; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -48,8 +50,6 @@ import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -69,34 +69,34 @@ class MerkleHederaStateTest extends MerkleTestBase { private final HederaLifecycles lifecycles = new HederaLifecycles() { @Override - public void onPreHandle(@NotNull Event event, @NotNull HederaState state) { + public void onPreHandle(@NonNull Event event, @NonNull HederaState state) { onPreHandleCalled.set(true); } @Override - public void onNewRecoveredState(@NotNull MerkleHederaState recoveredState) { + public void onNewRecoveredState(@NonNull MerkleHederaState recoveredState) { // No-op } @Override public void onHandleConsensusRound( - @NotNull Round round, @NotNull PlatformState platformState, @NotNull HederaState state) { + @NonNull Round round, @NonNull PlatformState platformState, @NonNull HederaState state) { onHandleCalled.set(true); } @Override public void onStateInitialized( - @NotNull HederaState state, - @NotNull Platform platform, - @NotNull PlatformState platformState, - @NotNull InitTrigger trigger, + @NonNull HederaState state, + @NonNull Platform platform, + @NonNull PlatformState platformState, + @NonNull InitTrigger trigger, @Nullable SoftwareVersion previousVersion) {} @Override public void onUpdateWeight( - @NotNull MerkleHederaState state, - @NotNull AddressBook configAddressBook, - @NotNull PlatformContext context) { + @NonNull MerkleHederaState state, + @NonNull AddressBook configAddressBook, + @NonNull PlatformContext context) { onUpdateWeightCalled.set(true); } }; 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 a78a96737a0b..6324d0ec5320 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 @@ -25,21 +25,21 @@ import com.hedera.node.app.ids.WritableEntityIdStore; import com.hedera.node.app.spi.fixtures.state.NoOpGenesisRecordsBuilder; import com.hedera.node.app.spi.fixtures.state.TestSchema; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.StateDefinition; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; import com.hedera.node.config.data.HederaConfig; import com.swirlds.common.constructable.ConstructableRegistry; import com.swirlds.common.constructable.ConstructableRegistryException; import com.swirlds.config.api.Configuration; import com.swirlds.merkledb.MerkleDb; import com.swirlds.metrics.api.Metrics; +import com.swirlds.state.spi.MigrationContext; import com.swirlds.state.spi.ReadableKVState; import com.swirlds.state.spi.ReadableSingletonState; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.StateDefinition; import com.swirlds.state.spi.WritableKVState; import com.swirlds.state.spi.WritableSingletonState; +import com.swirlds.state.spi.info.NetworkInfo; +import com.swirlds.state.spi.workflows.record.GenesisRecordsBuilder; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.HashMap; import java.util.LinkedList; diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/MerkleTestBase.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/MerkleTestBase.java index 4be63ce53d80..9e959d4bc7d1 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/MerkleTestBase.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/MerkleTestBase.java @@ -17,7 +17,6 @@ package com.hedera.node.app.state.merkle; import com.hedera.node.app.spi.fixtures.state.TestSchema; -import com.hedera.node.app.spi.state.StateDefinition; import com.hedera.pbj.runtime.Codec; import com.swirlds.common.merkle.MerkleNode; import com.swirlds.common.utility.Labeled; @@ -28,6 +27,7 @@ import com.swirlds.platform.state.merkle.memory.InMemoryKey; import com.swirlds.platform.state.merkle.memory.InMemoryValue; import com.swirlds.platform.test.fixtures.state.StateTestBase; +import com.swirlds.state.spi.StateDefinition; import com.swirlds.virtualmap.VirtualMap; import org.junit.jupiter.api.AfterEach; diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SchemaApplicationsTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SchemaApplicationsTest.java index e29b572ef630..0885c4e89bba 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SchemaApplicationsTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SchemaApplicationsTest.java @@ -24,8 +24,8 @@ import com.hedera.hapi.node.base.SemanticVersion; import com.hedera.hapi.node.state.common.EntityNumber; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.StateDefinition; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.StateDefinition; import java.util.Set; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java index b75e490dc8cd..907acb7daf29 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/SerializationTest.java @@ -21,11 +21,6 @@ import com.hedera.node.app.ids.WritableEntityIdStore; import com.hedera.node.app.spi.fixtures.state.TestSchema; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.StateDefinition; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; import com.hedera.node.config.data.HederaConfig; import com.swirlds.common.constructable.ClassConstructorPair; import com.swirlds.common.constructable.ConstructableRegistryException; @@ -37,7 +32,17 @@ import com.swirlds.metrics.api.Metrics; import com.swirlds.platform.state.merkle.disk.OnDiskReadableKVState; import com.swirlds.platform.state.merkle.disk.OnDiskWritableKVState; -import com.swirlds.state.spi.*; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.ReadableKVState; +import com.swirlds.state.spi.ReadableQueueState; +import com.swirlds.state.spi.ReadableSingletonState; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.StateDefinition; +import com.swirlds.state.spi.WritableKVState; +import com.swirlds.state.spi.WritableQueueState; +import com.swirlds.state.spi.WritableSingletonState; +import com.swirlds.state.spi.info.NetworkInfo; +import com.swirlds.state.spi.workflows.record.GenesisRecordsBuilder; import com.swirlds.virtualmap.VirtualMap; import com.swirlds.virtualmap.config.VirtualMapConfig; import com.swirlds.virtualmap.config.VirtualMapConfig_; diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/StateMetadataTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/StateMetadataTest.java index aaae57bf53a7..d33734d503bb 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/StateMetadataTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/StateMetadataTest.java @@ -20,9 +20,9 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import com.hedera.node.app.spi.fixtures.state.TestSchema; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.StateDefinition; import com.swirlds.platform.state.merkle.StateUtils; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.StateDefinition; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/disk/OnDiskTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/disk/OnDiskTest.java index 00761e5ffff4..ff4c027845ba 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/disk/OnDiskTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/merkle/disk/OnDiskTest.java @@ -22,9 +22,6 @@ import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.state.token.Account; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.StateDefinition; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; import com.hedera.node.app.state.merkle.MerkleSchemaRegistry; import com.hedera.node.app.state.merkle.MerkleTestBase; import com.hedera.node.app.state.merkle.SchemaApplications; @@ -41,6 +38,9 @@ import com.swirlds.platform.state.merkle.disk.OnDiskValue; import com.swirlds.platform.state.merkle.disk.OnDiskValueSerializer; import com.swirlds.platform.state.merkle.disk.OnDiskWritableKVState; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.StateDefinition; +import com.swirlds.state.spi.workflows.record.GenesisRecordsBuilder; import com.swirlds.virtualmap.VirtualMap; import com.swirlds.virtualmap.internal.merkle.VirtualRootNode; import edu.umd.cs.findbugs.annotations.NonNull; diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/recordcache/RecordCacheImplTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/recordcache/RecordCacheImplTest.java index 6b9d33e9e45d..dd0da40407db 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/recordcache/RecordCacheImplTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/recordcache/RecordCacheImplTest.java @@ -40,7 +40,6 @@ import com.hedera.node.app.fixtures.AppTestBase; import com.hedera.node.app.fixtures.state.FakeHederaState; import com.hedera.node.app.fixtures.state.FakeSchemaRegistry; -import com.hedera.node.app.spi.info.NetworkInfo; import com.hedera.node.app.spi.records.RecordCache; import com.hedera.node.app.state.DeduplicationCache; import com.hedera.node.app.state.SingleTransactionRecord; @@ -53,6 +52,7 @@ import com.hedera.node.config.data.LedgerConfig; import com.swirlds.platform.test.fixtures.state.ListWritableQueueState; import com.swirlds.state.spi.WritableQueueState; +import com.swirlds.state.spi.info.NetworkInfo; import edu.umd.cs.findbugs.annotations.NonNull; import java.time.Instant; import java.time.temporal.ChronoUnit; diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/recordcache/RecordCacheServiceTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/recordcache/RecordCacheServiceTest.java index ba530d338a28..92d9f0940391 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/recordcache/RecordCacheServiceTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/state/recordcache/RecordCacheServiceTest.java @@ -19,9 +19,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verify; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.SchemaRegistry; import com.hedera.node.app.state.recordcache.schemas.V0490RecordCacheSchema; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.SchemaRegistry; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/throttle/ThrottleAccumulatorTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/throttle/ThrottleAccumulatorTest.java index d054bb5251c5..6d6271feaacc 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/throttle/ThrottleAccumulatorTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/throttle/ThrottleAccumulatorTest.java @@ -85,13 +85,13 @@ import com.swirlds.state.HederaState; import com.swirlds.state.spi.ReadableKVState; import com.swirlds.state.spi.ReadableStates; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.io.InputStream; import java.time.Instant; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -2027,7 +2027,7 @@ void updateMetrics() { verify(throttleMetrics).updateAllMetrics(); } - @NotNull + @NonNull private static Bytes keyToBytes(Key key) throws IOException, ParseException { final var dataBuffer = getThreadLocalDataBuffer(); Key.PROTOBUF.write(key, dataBuffer); diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/version/HederaSoftwareVersionTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/version/HederaSoftwareVersionTest.java index c2da56f401b6..8f612bd038bf 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/version/HederaSoftwareVersionTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/version/HederaSoftwareVersionTest.java @@ -17,13 +17,13 @@ package com.hedera.node.app.version; import static com.hedera.node.app.version.HederaSoftwareVersion.RELEASE_027_VERSION; +import static com.swirlds.state.spi.HapiUtils.SEMANTIC_VERSION_COMPARATOR; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import com.hedera.hapi.node.base.SemanticVersion; -import com.hedera.hapi.util.HapiUtils; import com.hedera.node.config.converter.SemanticVersionConverter; import com.swirlds.common.constructable.ClassConstructorPair; import com.swirlds.common.constructable.ConstructableRegistry; @@ -77,11 +77,11 @@ void compareTo(@NonNull final String a, @NonNull final String b, final String ex final SemanticVersion pbjA = versionA.getPbjSemanticVersion(); final SemanticVersion pbjB = versionB.getPbjSemanticVersion(); switch (expected) { - case "<" -> assertThat(HapiUtils.SEMANTIC_VERSION_COMPARATOR.compare(pbjA, pbjB)) + case "<" -> assertThat(SEMANTIC_VERSION_COMPARATOR.compare(pbjA, pbjB)) .isLessThan(0); - case "=" -> assertThat(HapiUtils.SEMANTIC_VERSION_COMPARATOR.compare(pbjA, pbjB)) + case "=" -> assertThat(SEMANTIC_VERSION_COMPARATOR.compare(pbjA, pbjB)) .isEqualTo(0); - case ">" -> assertThat(HapiUtils.SEMANTIC_VERSION_COMPARATOR.compare(pbjA, pbjB)) + case ">" -> assertThat(SEMANTIC_VERSION_COMPARATOR.compare(pbjA, pbjB)) .isGreaterThan(0); default -> throw new IllegalArgumentException("Unknown expected value: " + expected); } diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/HandleContextImplTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/HandleContextImplTest.java index b4069ab685b6..f2d7104c08f2 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/HandleContextImplTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/HandleContextImplTest.java @@ -72,8 +72,6 @@ import com.hedera.node.app.spi.fees.Fees; import com.hedera.node.app.spi.fixtures.Scenarios; import com.hedera.node.app.spi.fixtures.state.MapWritableStates; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.info.SelfNodeInfo; import com.hedera.node.app.spi.metrics.StoreMetricsService; import com.hedera.node.app.spi.records.BlockRecordInfo; import com.hedera.node.app.spi.signatures.SignatureVerification; @@ -104,6 +102,8 @@ import com.swirlds.state.spi.ReadableStates; import com.swirlds.state.spi.WritableSingletonState; import com.swirlds.state.spi.WritableStates; +import com.swirlds.state.spi.info.NetworkInfo; +import com.swirlds.state.spi.info.SelfNodeInfo; import java.lang.reflect.InvocationTargetException; import java.time.Instant; import java.util.Arrays; diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/HandleWorkflowTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/HandleWorkflowTest.java index 25b27951afd7..22410fb87eb1 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/HandleWorkflowTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/HandleWorkflowTest.java @@ -59,7 +59,6 @@ import com.hedera.node.app.spi.authorization.Authorizer; import com.hedera.node.app.spi.authorization.SystemPrivilege; import com.hedera.node.app.spi.fees.Fees; -import com.hedera.node.app.spi.info.NetworkInfo; import com.hedera.node.app.spi.metrics.StoreMetricsService; import com.hedera.node.app.spi.workflows.HandleContext; import com.hedera.node.app.spi.workflows.HandleException; @@ -93,6 +92,7 @@ import com.swirlds.platform.system.events.ConsensusEvent; import com.swirlds.platform.system.transaction.ConsensusTransaction; import com.swirlds.platform.system.transaction.SwirldTransaction; +import com.swirlds.state.spi.info.NetworkInfo; import edu.umd.cs.findbugs.annotations.NonNull; import java.time.Instant; import java.util.List; diff --git a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/BlockRecordServiceTest.java b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/BlockRecordServiceTest.java index 62140bb0beea..546a006da6ed 100644 --- a/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/BlockRecordServiceTest.java +++ b/hedera-node/hedera-app/src/test/java/com/hedera/node/app/workflows/handle/record/BlockRecordServiceTest.java @@ -30,11 +30,11 @@ import com.hedera.hapi.node.state.blockrecords.RunningHashes; import com.hedera.node.app.records.BlockRecordService; import com.hedera.node.app.records.schemas.V0490BlockRecordSchema; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.SchemaRegistry; -import com.hedera.node.app.spi.state.StateDefinition; import com.hedera.pbj.runtime.io.buffer.Bytes; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.SchemaRegistry; +import com.swirlds.state.spi.StateDefinition; import com.swirlds.state.spi.WritableSingletonState; import com.swirlds.state.spi.WritableStates; import java.util.Set; 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 b2e70f8640b9..ca448b59b0cc 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 @@ -29,13 +29,9 @@ import com.hedera.node.app.info.NetworkInfoImpl; import com.hedera.node.app.info.SelfNodeInfoImpl; import com.hedera.node.app.service.token.TokenService; -import com.hedera.node.app.spi.Service; import com.hedera.node.app.spi.fixtures.Scenarios; import com.hedera.node.app.spi.fixtures.TransactionFactory; import com.hedera.node.app.spi.fixtures.state.MapWritableStates; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.info.NodeInfo; -import com.hedera.node.app.spi.info.SelfNodeInfo; import com.hedera.node.app.state.WorkingStateAccessor; import com.hedera.node.app.version.HederaSoftwareVersion; import com.hedera.node.config.ConfigProvider; @@ -59,7 +55,11 @@ import com.swirlds.platform.test.fixtures.state.TestBase; import com.swirlds.state.HederaState; import com.swirlds.state.spi.ReadableStates; +import com.swirlds.state.spi.Service; 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.util.LinkedHashSet; 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 2170b9408773..2ca8f6a5609d 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 @@ -21,19 +21,19 @@ import com.hedera.hapi.node.base.SemanticVersion; import com.hedera.node.app.spi.fixtures.state.MapWritableStates; import com.hedera.node.app.spi.fixtures.state.NoOpGenesisRecordsBuilder; -import com.hedera.node.app.spi.info.NetworkInfo; import com.hedera.node.app.spi.state.EmptyReadableStates; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.SchemaRegistry; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; import com.swirlds.config.api.Configuration; import com.swirlds.config.api.ConfigurationBuilder; import com.swirlds.platform.state.spi.WritableSingletonStateBase; import com.swirlds.platform.test.fixtures.state.ListWritableQueueState; import com.swirlds.platform.test.fixtures.state.MapWritableKVState; +import com.swirlds.state.spi.MigrationContext; import com.swirlds.state.spi.ReadableStates; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.SchemaRegistry; import com.swirlds.state.spi.WritableStates; +import com.swirlds.state.spi.info.NetworkInfo; +import com.swirlds.state.spi.workflows.record.GenesisRecordsBuilder; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.HashMap; import java.util.LinkedList; diff --git a/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeServicesRegistry.java b/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeServicesRegistry.java index 35f85c60ba4f..d10042a322a8 100644 --- a/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeServicesRegistry.java +++ b/hedera-node/hedera-app/src/testFixtures/java/com/hedera/node/app/fixtures/state/FakeServicesRegistry.java @@ -17,9 +17,9 @@ package com.hedera.node.app.fixtures.state; import com.hedera.node.app.services.ServicesRegistry; -import com.hedera.node.app.spi.Service; import com.hedera.node.app.spi.fixtures.state.NoOpGenesisRecordsBuilder; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; +import com.swirlds.state.spi.Service; +import com.swirlds.state.spi.workflows.record.GenesisRecordsBuilder; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Collections; import java.util.SortedSet; diff --git a/hedera-node/hedera-app/src/testFixtures/java/module-info.java b/hedera-node/hedera-app/src/testFixtures/java/module-info.java index b68272888ed9..974829d77eb7 100644 --- a/hedera-node/hedera-app/src/testFixtures/java/module-info.java +++ b/hedera-node/hedera-app/src/testFixtures/java/module-info.java @@ -2,7 +2,6 @@ exports com.hedera.node.app.fixtures.state; requires transitive com.hedera.node.app.spi.test.fixtures; - requires transitive com.hedera.node.app.spi; requires transitive com.hedera.node.app; requires transitive com.swirlds.common; requires transitive com.swirlds.platform.core.test.fixtures; @@ -10,14 +9,15 @@ requires transitive com.swirlds.state.api; requires com.hedera.node.app.service.mono; requires com.hedera.node.app.service.token; + requires com.hedera.node.app.spi; requires com.hedera.node.config.test.fixtures; requires com.hedera.node.config; requires com.hedera.node.hapi; - requires com.hedera.pbj.runtime; requires com.swirlds.base; requires com.swirlds.config.api; requires com.swirlds.config.extensions.test.fixtures; requires com.swirlds.metrics.api; + requires com.hedera.pbj.runtime; requires org.apache.logging.log4j; requires org.assertj.core; requires static com.github.spotbugs.annotations; diff --git a/hedera-node/hedera-app/src/xtest/java/common/BaseScaffoldingModule.java b/hedera-node/hedera-app/src/xtest/java/common/BaseScaffoldingModule.java index da8686530bc3..0368597aece3 100644 --- a/hedera-node/hedera-app/src/xtest/java/common/BaseScaffoldingModule.java +++ b/hedera-node/hedera-app/src/xtest/java/common/BaseScaffoldingModule.java @@ -64,8 +64,6 @@ import com.hedera.node.app.spi.authorization.Authorizer; import com.hedera.node.app.spi.fixtures.info.FakeNetworkInfo; import com.hedera.node.app.spi.fixtures.numbers.FakeHederaNumbers; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.info.SelfNodeInfo; import com.hedera.node.app.spi.metrics.StoreMetricsService; import com.hedera.node.app.spi.records.RecordCache; import com.hedera.node.app.spi.workflows.*; @@ -101,6 +99,8 @@ import com.swirlds.metrics.api.Metrics; import com.swirlds.platform.state.PlatformState; import com.swirlds.state.HederaState; +import com.swirlds.state.spi.info.NetworkInfo; +import com.swirlds.state.spi.info.SelfNodeInfo; import contract.ContractScaffoldingComponent; import dagger.Binds; import dagger.Module; @@ -114,7 +114,6 @@ import java.util.function.BiFunction; import java.util.function.Function; import javax.inject.Singleton; -import org.jetbrains.annotations.NotNull; /** * A helper module for Dagger2 to instantiate an {@link ContractScaffoldingComponent}; provides @@ -360,9 +359,9 @@ static SynchronizedThrottleAccumulator createSynchronizedThrottleAccumulator( return new SynchronizedThrottleAccumulator(frontendThrottle); } - @NotNull + @NonNull private static ThrottleMultiplier getThrottleMultiplier( - @NotNull ConfigProvider configProvider, ThrottleAccumulator backendThrottle) { + @NonNull ConfigProvider configProvider, ThrottleAccumulator backendThrottle) { return new ThrottleMultiplier( "logical TPS", "TPS", @@ -378,9 +377,9 @@ private static ThrottleMultiplier getThrottleMultiplier( () -> backendThrottle.activeThrottlesFor(CRYPTO_TRANSFER)); } - @NotNull + @NonNull private static CongestionMultipliers getCongestionMultipliers( - @NotNull ConfigProvider configProvider, + @NonNull ConfigProvider configProvider, ThrottleMultiplier genericFeeMultiplier, ThrottleAccumulator backendThrottle) { final var txnRateMultiplier = new UtilizationScaledThrottleMultiplier(genericFeeMultiplier, configProvider); diff --git a/hedera-node/hedera-app/src/xtest/java/contract/AssociationsXTest.java b/hedera-node/hedera-app/src/xtest/java/contract/AssociationsXTest.java index 6b9f0fa89693..25d15cdb129e 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/AssociationsXTest.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/AssociationsXTest.java @@ -50,11 +50,11 @@ import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.ReturnTypes; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.associations.AssociationsTranslator; import com.swirlds.state.spi.ReadableKVState; +import edu.umd.cs.findbugs.annotations.NonNull; import java.math.BigInteger; import java.util.HashMap; import java.util.Map; import org.apache.tuweni.bytes.Bytes; -import org.jetbrains.annotations.NotNull; /** * Exercises both classic and HRC associations via the following steps relative to an {@code OWNER} account: @@ -122,7 +122,7 @@ protected void doScenarioOperations() { } @Override - protected void assertExpectedTokenRelations(@NotNull ReadableKVState tokenRels) { + protected void assertExpectedTokenRelations(@NonNull ReadableKVState tokenRels) { // The owner should be associated with A_TOKEN and E_TOKEN assertPresentTokenAssociation(tokenRels, OWNER_ID, A_TOKEN_ID); assertMissingTokenAssociation(tokenRels, OWNER_ID, B_TOKEN_ID); @@ -132,23 +132,23 @@ protected void assertExpectedTokenRelations(@NotNull ReadableKVState tokenRels, - @NotNull final AccountID accountId, - @NotNull final TokenID tokenId) { + @NonNull final ReadableKVState tokenRels, + @NonNull final AccountID accountId, + @NonNull final TokenID tokenId) { assertTokenAssociation(tokenRels, accountId, tokenId, false); } private void assertPresentTokenAssociation( - @NotNull final ReadableKVState tokenRels, - @NotNull final AccountID accountId, - @NotNull final TokenID tokenId) { + @NonNull final ReadableKVState tokenRels, + @NonNull final AccountID accountId, + @NonNull final TokenID tokenId) { assertTokenAssociation(tokenRels, accountId, tokenId, true); } private void assertTokenAssociation( - @NotNull final ReadableKVState tokenRels, - @NotNull final AccountID accountId, - @NotNull final TokenID tokenId, + @NonNull final ReadableKVState tokenRels, + @NonNull final AccountID accountId, + @NonNull final TokenID tokenId, final boolean shouldBePresent) { final var tokenRel = tokenRels.get( EntityIDPair.newBuilder().tokenId(tokenId).accountId(accountId).build()); diff --git a/hedera-node/hedera-app/src/xtest/java/contract/BurnsXTest.java b/hedera-node/hedera-app/src/xtest/java/contract/BurnsXTest.java index a69b5efa611e..d72d2ffef672 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/BurnsXTest.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/BurnsXTest.java @@ -66,12 +66,12 @@ import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.burn.BurnTranslator; import com.hedera.node.app.spi.fixtures.Scenarios; import com.swirlds.state.spi.ReadableKVState; +import edu.umd.cs.findbugs.annotations.NonNull; import java.math.BigInteger; import java.util.HashMap; import java.util.Map; import java.util.Objects; import org.apache.tuweni.bytes.Bytes; -import org.jetbrains.annotations.NotNull; /** * Exercises burnToken on a fungible and non-fungible token via the following steps relative to an {@code OWNER} account: @@ -314,7 +314,7 @@ protected void doScenarioOperations() { @Override protected void assertExpectedTokenRelations( - @NotNull final ReadableKVState tokenRelationships) { + @NonNull final ReadableKVState tokenRelationships) { final var tokenRelation = tokenRelationships.get(EntityIDPair.newBuilder() .tokenId(ERC20_TOKEN_ID) .accountId(OWNER_ID) diff --git a/hedera-node/hedera-app/src/xtest/java/contract/Erc721XTest.java b/hedera-node/hedera-app/src/xtest/java/contract/Erc721XTest.java index 9a278a8f91e8..e3255b45176e 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/Erc721XTest.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/Erc721XTest.java @@ -58,7 +58,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; -import org.jetbrains.annotations.NotNull; /** *

GIVEN @@ -160,8 +159,8 @@ private ContractCallTransactionBody callWithParams(@NonNull final ByteBuffer enc @Override protected void assertExpectedStorage( - @NotNull final ReadableKVState storage, - @NotNull final ReadableKVState accounts) { + @NonNull final ReadableKVState storage, + @NonNull final ReadableKVState accounts) { assertEquals(EXPECTED_STORAGE.size(), storage.size()); EXPECTED_STORAGE.forEach((key, value) -> { final var slot = storage.get(new SlotKey(ERC721_FULL_CONTRACT_ID, key)); @@ -173,19 +172,19 @@ protected void assertExpectedStorage( } @Override - protected void assertExpectedAliases(@NotNull final ReadableKVState aliases) { + protected void assertExpectedAliases(@NonNull final ReadableKVState aliases) { // No aliases change in this test } @Override - protected void assertExpectedAccounts(@NotNull final ReadableKVState accounts) { + protected void assertExpectedAccounts(@NonNull final ReadableKVState accounts) { final var contract = accounts.get(ERC721_FULL_ID); assertNotNull(contract); assertTrue(contract.smartContract()); } @Override - protected void assertExpectedBytecodes(@NotNull final ReadableKVState bytecodes) { + protected void assertExpectedBytecodes(@NonNull final ReadableKVState bytecodes) { final var actualBytecode = bytecodes.get(new EntityNumber(ERC721_FULL_ID.accountNumOrThrow())); assertNotNull(actualBytecode); assertEquals(resourceAsBytes("bytecode/ERC721Full.bin"), actualBytecode.code()); diff --git a/hedera-node/hedera-app/src/xtest/java/contract/FreezeUnfreezeXTest.java b/hedera-node/hedera-app/src/xtest/java/contract/FreezeUnfreezeXTest.java index 8ef0f9946f88..77cf44921d65 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/FreezeUnfreezeXTest.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/FreezeUnfreezeXTest.java @@ -54,12 +54,12 @@ import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.freeze.FreezeUnfreezeTranslator; import com.hedera.node.app.spi.fixtures.Scenarios; import com.swirlds.state.spi.ReadableKVState; +import edu.umd.cs.findbugs.annotations.NonNull; import java.math.BigInteger; import java.util.HashMap; import java.util.Map; import java.util.Objects; import org.apache.tuweni.bytes.Bytes; -import org.jetbrains.annotations.NotNull; /** * Exercises freeze and unfreeze a token via the following steps relative to an {@code OWNER} account: @@ -259,7 +259,7 @@ protected Map initialTokenRelationships() { } @Override - protected void assertExpectedTokenRelations(@NotNull final ReadableKVState tokenRels) { + protected void assertExpectedTokenRelations(@NonNull final ReadableKVState tokenRels) { assertTokenBalance(tokenRels, OWNER_ID, 900L); assertTokenBalance(tokenRels, RECEIVER_ID, 100L); } diff --git a/hedera-node/hedera-app/src/xtest/java/contract/HtsErc20TransfersXTest.java b/hedera-node/hedera-app/src/xtest/java/contract/HtsErc20TransfersXTest.java index ed045b69ac2e..08f34411b6a4 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/HtsErc20TransfersXTest.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/HtsErc20TransfersXTest.java @@ -45,12 +45,12 @@ import com.hedera.hapi.node.state.token.Token; import com.hedera.hapi.node.state.token.TokenRelation; import com.swirlds.state.spi.ReadableKVState; +import edu.umd.cs.findbugs.annotations.NonNull; import java.math.BigInteger; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import org.jetbrains.annotations.NotNull; /** * Exercises the ERC-20 {@code transferFrom()} and {@code transfer()} call by setting up an owner that has a @@ -96,7 +96,7 @@ protected void doScenarioOperations() { } @Override - protected void assertExpectedAccounts(@NotNull ReadableKVState accounts) { + protected void assertExpectedAccounts(@NonNull ReadableKVState accounts) { final var owner = Objects.requireNonNull(accounts.get(OWNER_ID)); final var allowance = Objects.requireNonNull(owner.tokenAllowances()).get(0); assertEquals(ERC20_TOKEN_ID, allowance.tokenId()); @@ -105,7 +105,7 @@ protected void assertExpectedAccounts(@NotNull ReadableKVState tokenRels) { + protected void assertExpectedTokenRelations(@NonNull ReadableKVState tokenRels) { // The owner's balance should have been reduced by 300 assertTokenBalance(tokenRels, OWNER_ID, 700L); // The receiver's balance should have been increased by 300 diff --git a/hedera-node/hedera-app/src/xtest/java/contract/HtsErc721TransferFromXTest.java b/hedera-node/hedera-app/src/xtest/java/contract/HtsErc721TransferFromXTest.java index 38608d3ff164..32c2e28a9b9a 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/HtsErc721TransferFromXTest.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/HtsErc721TransferFromXTest.java @@ -62,7 +62,6 @@ import java.util.Map; import java.util.Objects; import org.apache.tuweni.bytes.Bytes; -import org.jetbrains.annotations.NotNull; /** * Exercises the ERC-721 {@code transferFrom()} call by setting up an owner that has two NFTs, one of which is @@ -182,7 +181,7 @@ protected Map initialTokenRelationships() { } @Override - protected void assertExpectedTokenRelations(@NotNull ReadableKVState tokenRels) { + protected void assertExpectedTokenRelations(@NonNull ReadableKVState tokenRels) { final var ownerRelation = Objects.requireNonNull(tokenRels.get(EntityIDPair.newBuilder() .tokenId(ERC721_TOKEN_ID) .accountId(OWNER_ID) diff --git a/hedera-node/hedera-app/src/xtest/java/contract/MintsXTest.java b/hedera-node/hedera-app/src/xtest/java/contract/MintsXTest.java index f792c99062e6..5392048479e6 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/MintsXTest.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/MintsXTest.java @@ -62,11 +62,11 @@ import com.hedera.hapi.node.state.token.TokenRelation; import com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.mint.MintTranslator; import com.swirlds.state.spi.ReadableKVState; +import edu.umd.cs.findbugs.annotations.NonNull; import java.math.BigInteger; import java.util.HashMap; import java.util.Map; import org.apache.tuweni.bytes.Bytes; -import org.jetbrains.annotations.NotNull; /** * Exercises mint on a fungible and non-fungible token via the following steps relative to an {@code OWNER} account: @@ -420,7 +420,7 @@ protected Map initialAccounts() { @Override protected void assertExpectedTokenRelations( - @NotNull final ReadableKVState tokenRelationships) { + @NonNull final ReadableKVState tokenRelationships) { final var tokenRelation = tokenRelationships.get(EntityIDPair.newBuilder() .tokenId(ERC20_TOKEN_ID) .accountId(OWNER_ID) diff --git a/hedera-node/hedera-app/src/xtest/java/contract/MiscClassicTransfersXTest.java b/hedera-node/hedera-app/src/xtest/java/contract/MiscClassicTransfersXTest.java index 2ef049fd9a25..78f5d6493c43 100644 --- a/hedera-node/hedera-app/src/xtest/java/contract/MiscClassicTransfersXTest.java +++ b/hedera-node/hedera-app/src/xtest/java/contract/MiscClassicTransfersXTest.java @@ -67,7 +67,6 @@ import java.util.HashMap; import java.util.Map; import org.apache.tuweni.bytes.Bytes; -import org.jetbrains.annotations.NotNull; /** * Exercises some miscellaneous classic transfers, emphasizing slightly more exotic behaviors. These include, @@ -133,7 +132,7 @@ protected void assertExpectedTokenRelations(@NonNull final ReadableKVState accounts) { + protected void assertExpectedAccounts(@NonNull ReadableKVState accounts) { final var lazyCreation = requireNonNull(accounts.get(LAZY_CREATED_ID)); assertEquals(1, lazyCreation.maxAutoAssociations()); assertEquals(1, lazyCreation.usedAutoAssociations()); diff --git a/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/ApiPermissionConfig.java b/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/ApiPermissionConfig.java index ece69aa0abfb..ea0cfbffd0fd 100644 --- a/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/ApiPermissionConfig.java +++ b/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/ApiPermissionConfig.java @@ -49,6 +49,10 @@ import static com.hedera.hapi.node.base.HederaFunctionality.GET_ACCOUNT_DETAILS; import static com.hedera.hapi.node.base.HederaFunctionality.GET_VERSION_INFO; import static com.hedera.hapi.node.base.HederaFunctionality.NETWORK_GET_EXECUTION_TIME; +import static com.hedera.hapi.node.base.HederaFunctionality.NODE_CREATE; +import static com.hedera.hapi.node.base.HederaFunctionality.NODE_DELETE; +import static com.hedera.hapi.node.base.HederaFunctionality.NODE_GET_INFO; +import static com.hedera.hapi.node.base.HederaFunctionality.NODE_UPDATE; import static com.hedera.hapi.node.base.HederaFunctionality.SCHEDULE_CREATE; import static com.hedera.hapi.node.base.HederaFunctionality.SCHEDULE_DELETE; import static com.hedera.hapi.node.base.HederaFunctionality.SCHEDULE_GET_INFO; @@ -172,6 +176,11 @@ * @param freeze the permission for {@link HederaFunctionality#FREEZE} functionality * @param getAccountDetails the permission for {@link HederaFunctionality#GET_ACCOUNT_DETAILS} functionality * @param tokenUpdateNfts the permission for {@link HederaFunctionality#TOKEN_UPDATE_NFTS} functionality + * + * @param createNode the permission for {@link HederaFunctionality#NODE_CREATE} functionality + * @param updateNode the permission for {@link HederaFunctionality#NODE_UPDATE} functionality + * @param deleteNode the permission for {@link HederaFunctionality#NODE_DELETE} functionality + * @param getNodeInfo the permission for {@link HederaFunctionality#NODE_GET_INFO} functionality */ @ConfigData public record ApiPermissionConfig( @@ -237,7 +246,11 @@ public record ApiPermissionConfig( @ConfigProperty(defaultValue = "2-60") PermissionedAccountsRange systemUndelete, @ConfigProperty(defaultValue = "2-58") PermissionedAccountsRange freeze, @ConfigProperty(defaultValue = "2-50") PermissionedAccountsRange getAccountDetails, - @ConfigProperty(defaultValue = "0-*") PermissionedAccountsRange tokenUpdateNfts) { + @ConfigProperty(defaultValue = "0-*") PermissionedAccountsRange tokenUpdateNfts, + @ConfigProperty(defaultValue = "0-*") PermissionedAccountsRange createNode, + @ConfigProperty(defaultValue = "0-*") PermissionedAccountsRange updateNode, + @ConfigProperty(defaultValue = "0-*") PermissionedAccountsRange deleteNode, + @ConfigProperty(defaultValue = "0-*") PermissionedAccountsRange getNodeInfo) { private static final EnumMap> permissionKeys = new EnumMap<>(HederaFunctionality.class); @@ -308,6 +321,10 @@ public record ApiPermissionConfig( permissionKeys.put(TOKEN_GET_ACCOUNT_NFT_INFOS, c -> c.tokenGetAccountNftInfos); permissionKeys.put(TOKEN_FEE_SCHEDULE_UPDATE, c -> c.tokenFeeScheduleUpdate); permissionKeys.put(UTIL_PRNG, c -> c.utilPrng); + permissionKeys.put(NODE_CREATE, c -> c.createNode); + permissionKeys.put(NODE_UPDATE, c -> c.updateNode); + permissionKeys.put(NODE_DELETE, c -> c.deleteNode); + permissionKeys.put(NODE_GET_INFO, c -> c.getNodeInfo); } /** diff --git a/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/NodesConfig.java b/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/NodesConfig.java new file mode 100644 index 000000000000..255dbc9021b9 --- /dev/null +++ b/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/NodesConfig.java @@ -0,0 +1,30 @@ +/* + * 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.config.data; + +import com.hedera.node.config.NetworkProperty; +import com.swirlds.config.api.ConfigData; +import com.swirlds.config.api.ConfigProperty; + +@ConfigData("nodes") +public record NodesConfig( + @ConfigProperty(defaultValue = "100") @NetworkProperty long maxNumber, + @ConfigProperty(defaultValue = "100") @NetworkProperty int nodeMaxDescriptionUtf8Bytes, + @ConfigProperty(defaultValue = "10") @NetworkProperty int maxGossipEndpoint, + @ConfigProperty(defaultValue = "10") @NetworkProperty int maxServiceEndpoint, + @ConfigProperty(defaultValue = "true") @NetworkProperty boolean gossipFqdnRestricted, + @ConfigProperty(defaultValue = "253") @NetworkProperty int maxFqdnSize) {} diff --git a/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/TokensConfig.java b/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/TokensConfig.java index 53419df82ec9..142341cc366b 100644 --- a/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/TokensConfig.java +++ b/hedera-node/hedera-config/src/main/java/com/hedera/node/config/data/TokensConfig.java @@ -50,7 +50,7 @@ public record TokensConfig( @ConfigProperty(value = "autoCreations.isEnabled", defaultValue = "true") @NetworkProperty boolean autoCreationsIsEnabled, @ConfigProperty(value = "maxMetadataBytes", defaultValue = "100") @NetworkProperty int tokensMaxMetadataBytes, - @ConfigProperty(value = "balancesInQueries.enabled", defaultValue = "false") @NetworkProperty + @ConfigProperty(value = "balancesInQueries.enabled", defaultValue = "true") @NetworkProperty boolean balancesInQueriesEnabled, @ConfigProperty(value = "nfts.maxBatchSizeUpdate", defaultValue = "10") @NetworkProperty int nftsMaxBatchSizeUpdate) {} diff --git a/hedera-node/hedera-config/src/main/java/module-info.java b/hedera-node/hedera-config/src/main/java/module-info.java index de74929bf71b..cdfc533f751b 100644 --- a/hedera-node/hedera-config/src/main/java/module-info.java +++ b/hedera-node/hedera-config/src/main/java/module-info.java @@ -9,9 +9,9 @@ requires transitive com.hedera.node.app.hapi.utils; requires transitive com.hedera.node.app.service.mono; requires transitive com.hedera.node.hapi; - requires transitive com.hedera.pbj.runtime; requires transitive com.swirlds.config.api; requires transitive com.swirlds.config.extensions; + requires transitive com.hedera.pbj.runtime; requires com.swirlds.common; requires org.apache.logging.log4j; requires static com.github.spotbugs.annotations; diff --git a/hedera-node/hedera-config/src/testFixtures/java/com/hedera/node/config/testfixtures/HederaTestConfigBuilder.java b/hedera-node/hedera-config/src/testFixtures/java/com/hedera/node/config/testfixtures/HederaTestConfigBuilder.java index 71bddf3655ee..02d01e460371 100644 --- a/hedera-node/hedera-config/src/testFixtures/java/com/hedera/node/config/testfixtures/HederaTestConfigBuilder.java +++ b/hedera-node/hedera-config/src/testFixtures/java/com/hedera/node/config/testfixtures/HederaTestConfigBuilder.java @@ -65,6 +65,7 @@ import com.hedera.node.config.data.LedgerConfig; import com.hedera.node.config.data.NettyConfig; import com.hedera.node.config.data.NetworkAdminConfig; +import com.hedera.node.config.data.NodesConfig; import com.hedera.node.config.data.RatesConfig; import com.hedera.node.config.data.SchedulingConfig; import com.hedera.node.config.data.SigsConfig; @@ -189,6 +190,7 @@ public static TestConfigBuilder create() { .withConfigDataType(UpgradeConfig.class) .withConfigDataType(UtilPrngConfig.class) .withConfigDataType(VersionConfig.class) + .withConfigDataType(NodesConfig.class) .withConverter(CongestionMultipliers.class, new CongestionMultipliersConverter()) .withConverter(EntityScaleFactors.class, new EntityScaleFactorsConverter()) .withConverter(KnownBlockValues.class, new KnownBlockValuesConverter()) diff --git a/hedera-node/hedera-consensus-service-impl/src/main/java/com/hedera/node/app/service/consensus/impl/ConsensusServiceImpl.java b/hedera-node/hedera-consensus-service-impl/src/main/java/com/hedera/node/app/service/consensus/impl/ConsensusServiceImpl.java index 7e39184891bf..790314c4877e 100644 --- a/hedera-node/hedera-consensus-service-impl/src/main/java/com/hedera/node/app/service/consensus/impl/ConsensusServiceImpl.java +++ b/hedera-node/hedera-consensus-service-impl/src/main/java/com/hedera/node/app/service/consensus/impl/ConsensusServiceImpl.java @@ -18,12 +18,12 @@ import com.hedera.node.app.service.consensus.ConsensusService; import com.hedera.node.app.service.consensus.impl.schemas.V0490ConsensusSchema; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.state.SchemaRegistry; +import com.hedera.node.app.spi.RpcService; +import com.swirlds.state.spi.SchemaRegistry; import edu.umd.cs.findbugs.annotations.NonNull; /** - * Standard implementation of the {@link ConsensusService} {@link Service}. + * Standard implementation of the {@link ConsensusService} {@link RpcService}. */ public final class ConsensusServiceImpl implements ConsensusService { public static final int RUNNING_HASH_BYTE_ARRAY_SIZE = 48; diff --git a/hedera-node/hedera-consensus-service-impl/src/main/java/com/hedera/node/app/service/consensus/impl/schemas/V0490ConsensusSchema.java b/hedera-node/hedera-consensus-service-impl/src/main/java/com/hedera/node/app/service/consensus/impl/schemas/V0490ConsensusSchema.java index a664a6389e38..505a84e7c152 100644 --- a/hedera-node/hedera-consensus-service-impl/src/main/java/com/hedera/node/app/service/consensus/impl/schemas/V0490ConsensusSchema.java +++ b/hedera-node/hedera-consensus-service-impl/src/main/java/com/hedera/node/app/service/consensus/impl/schemas/V0490ConsensusSchema.java @@ -25,11 +25,11 @@ import com.hedera.node.app.service.mono.state.adapters.MerkleMapLike; import com.hedera.node.app.service.mono.state.merkle.MerkleTopic; import com.hedera.node.app.service.mono.utils.EntityNum; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.StateDefinition; import com.swirlds.merkle.map.MerkleMap; import com.swirlds.platform.state.spi.WritableKVStateBase; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.StateDefinition; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.Set; diff --git a/hedera-node/hedera-consensus-service-impl/src/main/java/module-info.java b/hedera-node/hedera-consensus-service-impl/src/main/java/module-info.java index 11f4ae752496..3e4a9f28f843 100644 --- a/hedera-node/hedera-consensus-service-impl/src/main/java/module-info.java +++ b/hedera-node/hedera-consensus-service-impl/src/main/java/module-info.java @@ -5,10 +5,10 @@ requires transitive com.hedera.node.app.service.mono; requires transitive com.hedera.node.app.spi; requires transitive com.hedera.node.hapi; - requires transitive com.hedera.pbj.runtime; requires transitive com.swirlds.config.api; requires transitive com.swirlds.merkle; requires transitive com.swirlds.state.api; + requires transitive com.hedera.pbj.runtime; requires transitive dagger; requires transitive javax.inject; requires com.hedera.node.app.hapi.utils; diff --git a/hedera-node/hedera-consensus-service-impl/src/test/java/com/hedera/node/app/service/consensus/impl/test/ConsensusServiceImplTest.java b/hedera-node/hedera-consensus-service-impl/src/test/java/com/hedera/node/app/service/consensus/impl/test/ConsensusServiceImplTest.java index f9f843a8b4e4..13012f01e808 100644 --- a/hedera-node/hedera-consensus-service-impl/src/test/java/com/hedera/node/app/service/consensus/impl/test/ConsensusServiceImplTest.java +++ b/hedera-node/hedera-consensus-service-impl/src/test/java/com/hedera/node/app/service/consensus/impl/test/ConsensusServiceImplTest.java @@ -22,7 +22,7 @@ import static org.mockito.Mockito.verify; import com.hedera.node.app.service.consensus.impl.ConsensusServiceImpl; -import com.hedera.node.app.spi.state.SchemaRegistry; +import com.swirlds.state.spi.SchemaRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/hedera-node/hedera-consensus-service-impl/src/test/java/com/hedera/node/app/service/consensus/impl/test/schemas/V0490ConsensusSchemaTest.java b/hedera-node/hedera-consensus-service-impl/src/test/java/com/hedera/node/app/service/consensus/impl/test/schemas/V0490ConsensusSchemaTest.java index 08902cff47ac..cb8a36959567 100644 --- a/hedera-node/hedera-consensus-service-impl/src/test/java/com/hedera/node/app/service/consensus/impl/test/schemas/V0490ConsensusSchemaTest.java +++ b/hedera-node/hedera-consensus-service-impl/src/test/java/com/hedera/node/app/service/consensus/impl/test/schemas/V0490ConsensusSchemaTest.java @@ -29,9 +29,9 @@ import com.hedera.node.app.spi.fixtures.util.LogCaptureExtension; import com.hedera.node.app.spi.fixtures.util.LoggingSubject; import com.hedera.node.app.spi.fixtures.util.LoggingTarget; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.StateDefinition; import com.swirlds.merkle.map.MerkleMap; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.StateDefinition; import com.swirlds.state.spi.WritableKVState; import com.swirlds.state.spi.WritableStates; import org.junit.jupiter.api.BeforeEach; diff --git a/hedera-node/hedera-consensus-service/build.gradle.kts b/hedera-node/hedera-consensus-service/build.gradle.kts index db7ed73d0ffd..06b95eaa6ce5 100644 --- a/hedera-node/hedera-consensus-service/build.gradle.kts +++ b/hedera-node/hedera-consensus-service/build.gradle.kts @@ -24,3 +24,8 @@ description = "Hedera Consensus Service API" // Remove the following line to enable all 'javac' lint checks that we have turned on by default // and then fix the reported issues. tasks.withType().configureEach { options.compilerArgs.add("-Xlint:-exports") } + +testModuleInfo { + requires("org.assertj.core") + requires("org.junit.jupiter.api") +} diff --git a/hedera-node/hedera-consensus-service/src/main/java/com/hedera/node/app/service/consensus/ConsensusService.java b/hedera-node/hedera-consensus-service/src/main/java/com/hedera/node/app/service/consensus/ConsensusService.java index c59d9d5461a0..855e45ed5aa9 100644 --- a/hedera-node/hedera-consensus-service/src/main/java/com/hedera/node/app/service/consensus/ConsensusService.java +++ b/hedera-node/hedera-consensus-service/src/main/java/com/hedera/node/app/service/consensus/ConsensusService.java @@ -16,8 +16,8 @@ package com.hedera.node.app.service.consensus; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.ServiceFactory; +import com.hedera.node.app.spi.RpcService; +import com.hedera.node.app.spi.RpcServiceFactory; import com.hedera.pbj.runtime.RpcServiceDefinition; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ServiceLoader; @@ -28,7 +28,7 @@ * href="https://github.com/hashgraph/hedera-protobufs/blob/main/services/consensus_service.proto">Consensus * Service. */ -public interface ConsensusService extends Service { +public interface ConsensusService extends RpcService { String NAME = "ConsensusService"; @NonNull @@ -50,6 +50,6 @@ default Set rpcDefinitions() { */ @NonNull static ConsensusService getInstance() { - return ServiceFactory.loadService(ConsensusService.class, ServiceLoader.load(ConsensusService.class)); + return RpcServiceFactory.loadService(ConsensusService.class, ServiceLoader.load(ConsensusService.class)); } } diff --git a/hedera-node/hedera-consensus-service/src/main/java/module-info.java b/hedera-node/hedera-consensus-service/src/main/java/module-info.java index 3715bb9b646a..33b830494415 100644 --- a/hedera-node/hedera-consensus-service/src/main/java/module-info.java +++ b/hedera-node/hedera-consensus-service/src/main/java/module-info.java @@ -3,9 +3,9 @@ uses com.hedera.node.app.service.consensus.ConsensusService; - requires com.hedera.node.app.hapi.utils; requires transitive com.hedera.node.app.spi; requires transitive com.hedera.node.hapi; requires transitive com.hedera.pbj.runtime; + requires com.hedera.node.app.hapi.utils; requires static com.github.spotbugs.annotations; } diff --git a/hedera-node/hedera-consensus-service/src/test/java/com/hedera/node/app/service/consensus/ConsensusServiceDefinitionTest.java b/hedera-node/hedera-consensus-service/src/test/java/com/hedera/node/app/service/consensus/ConsensusServiceDefinitionTest.java new file mode 100644 index 000000000000..77fd56a05dcb --- /dev/null +++ b/hedera-node/hedera-consensus-service/src/test/java/com/hedera/node/app/service/consensus/ConsensusServiceDefinitionTest.java @@ -0,0 +1,45 @@ +/* + * 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.service.consensus; + +import com.hedera.hapi.node.base.Transaction; +import com.hedera.hapi.node.transaction.Query; +import com.hedera.hapi.node.transaction.Response; +import com.hedera.hapi.node.transaction.TransactionResponse; +import com.hedera.pbj.runtime.RpcMethodDefinition; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +class ConsensusServiceDefinitionTest { + + @Test + void checkBasePath() { + Assertions.assertThat(ConsensusServiceDefinition.INSTANCE.basePath()).isEqualTo("proto.ConsensusService"); + } + + @Test + void methodsDefined() { + final var methods = ConsensusServiceDefinition.INSTANCE.methods(); + Assertions.assertThat(methods) + .containsExactlyInAnyOrder( + new RpcMethodDefinition<>("createTopic", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("updateTopic", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("deleteTopic", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("getTopicInfo", Query.class, Response.class), + new RpcMethodDefinition<>("submitMessage", Transaction.class, TransactionResponse.class)); + } +} diff --git a/hedera-node/hedera-evm/src/main/java/module-info.java b/hedera-node/hedera-evm/src/main/java/module-info.java index 841df004abef..8c8406049828 100644 --- a/hedera-node/hedera-evm/src/main/java/module-info.java +++ b/hedera-node/hedera-evm/src/main/java/module-info.java @@ -1,8 +1,8 @@ /** Provides the core interfaces for the Hedera EVM implementation. */ module com.hedera.evm { + requires transitive com.hedera.node.hapi; requires transitive com.github.benmanes.caffeine; requires transitive com.google.protobuf; - requires transitive com.hedera.node.hapi; requires transitive com.hedera.pbj.runtime; requires transitive dagger; requires transitive headlong; @@ -13,9 +13,9 @@ requires transitive org.hyperledger.besu.nativelib.secp256k1; requires transitive tuweni.bytes; requires transitive tuweni.units; + requires com.swirlds.common; requires com.google.common; requires com.sun.jna; - requires com.swirlds.common; requires org.bouncycastle.provider; requires static com.github.spotbugs.annotations; diff --git a/hedera-node/hedera-file-service-impl/src/main/java/com/hedera/node/app/service/file/impl/FileServiceImpl.java b/hedera-node/hedera-file-service-impl/src/main/java/com/hedera/node/app/service/file/impl/FileServiceImpl.java index b852b6335e52..b2b0e5f22de5 100644 --- a/hedera-node/hedera-file-service-impl/src/main/java/com/hedera/node/app/service/file/impl/FileServiceImpl.java +++ b/hedera-node/hedera-file-service-impl/src/main/java/com/hedera/node/app/service/file/impl/FileServiceImpl.java @@ -18,13 +18,13 @@ import com.hedera.node.app.service.file.FileService; import com.hedera.node.app.service.file.impl.schemas.V0490FileSchema; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.state.SchemaRegistry; +import com.hedera.node.app.spi.RpcService; import com.hedera.node.config.ConfigProvider; +import com.swirlds.state.spi.SchemaRegistry; import edu.umd.cs.findbugs.annotations.NonNull; import javax.inject.Inject; -/** Standard implementation of the {@link FileService} {@link Service}. */ +/** Standard implementation of the {@link FileService} {@link RpcService}. */ public final class FileServiceImpl implements FileService { private final ConfigProvider configProvider; diff --git a/hedera-node/hedera-file-service-impl/src/main/java/com/hedera/node/app/service/file/impl/schemas/V0490FileSchema.java b/hedera-node/hedera-file-service-impl/src/main/java/com/hedera/node/app/service/file/impl/schemas/V0490FileSchema.java index bdc742a11132..c7ced8428a4f 100644 --- a/hedera-node/hedera-file-service-impl/src/main/java/com/hedera/node/app/service/file/impl/schemas/V0490FileSchema.java +++ b/hedera-node/hedera-file-service-impl/src/main/java/com/hedera/node/app/service/file/impl/schemas/V0490FileSchema.java @@ -50,10 +50,6 @@ import com.hedera.node.app.service.mono.state.adapters.VirtualMapLike; import com.hedera.node.app.service.mono.state.virtual.VirtualBlobKey; import com.hedera.node.app.service.mono.state.virtual.VirtualBlobValue; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.StateDefinition; import com.hedera.node.config.ConfigProvider; import com.hedera.node.config.data.BootstrapConfig; import com.hedera.node.config.data.FilesConfig; @@ -63,7 +59,11 @@ import com.swirlds.common.threading.manager.AdHocThreadManager; import com.swirlds.config.api.Configuration; import com.swirlds.platform.state.spi.WritableKVStateBase; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.StateDefinition; import com.swirlds.state.spi.WritableKVState; +import com.swirlds.state.spi.info.NetworkInfo; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.io.IOException; diff --git a/hedera-node/hedera-file-service-impl/src/main/java/module-info.java b/hedera-node/hedera-file-service-impl/src/main/java/module-info.java index 023f76406c3f..baf88d3e7195 100644 --- a/hedera-node/hedera-file-service-impl/src/main/java/module-info.java +++ b/hedera-node/hedera-file-service-impl/src/main/java/module-info.java @@ -1,10 +1,4 @@ module com.hedera.node.app.service.file.impl { - requires com.fasterxml.jackson.databind; - requires com.swirlds.base; - requires com.swirlds.common; - requires com.swirlds.platform.core; - requires org.apache.commons.lang3; - requires org.apache.logging.log4j; requires transitive com.hedera.node.app.hapi.fees; requires transitive com.hedera.node.app.hapi.utils; requires transitive com.hedera.node.app.service.file; @@ -12,11 +6,17 @@ requires transitive com.hedera.node.app.spi; requires transitive com.hedera.node.config; requires transitive com.hedera.node.hapi; - requires transitive com.hedera.pbj.runtime; requires transitive com.swirlds.config.api; requires transitive com.swirlds.state.api; + requires transitive com.hedera.pbj.runtime; requires transitive dagger; requires transitive javax.inject; + requires com.swirlds.base; + requires com.swirlds.common; + requires com.swirlds.platform.core; + requires com.fasterxml.jackson.databind; + requires org.apache.commons.lang3; + requires org.apache.logging.log4j; requires static com.github.spotbugs.annotations; requires static java.compiler; // javax.annotation.processing.Generated diff --git a/hedera-node/hedera-file-service-impl/src/test/java/com/hedera/node/app/service/file/impl/test/FileServiceImplTest.java b/hedera-node/hedera-file-service-impl/src/test/java/com/hedera/node/app/service/file/impl/test/FileServiceImplTest.java index 45b14684a100..95c7c6b5dab1 100644 --- a/hedera-node/hedera-file-service-impl/src/test/java/com/hedera/node/app/service/file/impl/test/FileServiceImplTest.java +++ b/hedera-node/hedera-file-service-impl/src/test/java/com/hedera/node/app/service/file/impl/test/FileServiceImplTest.java @@ -23,10 +23,10 @@ import com.hedera.node.app.service.file.FileService; import com.hedera.node.app.service.file.impl.FileServiceImpl; import com.hedera.node.app.service.file.impl.schemas.V0490FileSchema; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.SchemaRegistry; -import com.hedera.node.app.spi.state.StateDefinition; import com.hedera.node.config.ConfigProvider; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.SchemaRegistry; +import com.swirlds.state.spi.StateDefinition; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/hedera-node/hedera-file-service-impl/src/test/java/com/hedera/node/app/service/file/impl/test/schemas/FileSchemaTest.java b/hedera-node/hedera-file-service-impl/src/test/java/com/hedera/node/app/service/file/impl/test/schemas/FileSchemaTest.java index 13e0a101987d..00c8b3ddec42 100644 --- a/hedera-node/hedera-file-service-impl/src/test/java/com/hedera/node/app/service/file/impl/test/schemas/FileSchemaTest.java +++ b/hedera-node/hedera-file-service-impl/src/test/java/com/hedera/node/app/service/file/impl/test/schemas/FileSchemaTest.java @@ -28,7 +28,6 @@ import com.hedera.node.app.service.file.impl.schemas.V0490FileSchema; import com.hedera.node.app.spi.fixtures.info.FakeNetworkInfo; import com.hedera.node.app.spi.fixtures.state.MapWritableStates; -import com.hedera.node.app.spi.info.NetworkInfo; import com.hedera.node.app.spi.state.EmptyReadableStates; import com.hedera.node.app.workflows.handle.record.GenesisRecordsConsensusHook; import com.hedera.node.app.workflows.handle.record.MigrationContextImpl; @@ -37,6 +36,7 @@ import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.platform.test.fixtures.state.MapWritableKVState; import com.swirlds.state.spi.ReadableStates; +import com.swirlds.state.spi.info.NetworkInfo; import java.util.HashMap; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; diff --git a/hedera-node/hedera-file-service/build.gradle.kts b/hedera-node/hedera-file-service/build.gradle.kts index a4455d038270..5fcd969c3ce2 100644 --- a/hedera-node/hedera-file-service/build.gradle.kts +++ b/hedera-node/hedera-file-service/build.gradle.kts @@ -24,3 +24,8 @@ description = "Hedera File Service API" // Remove the following line to enable all 'javac' lint checks that we have turned on by default // and then fix the reported issues. tasks.withType().configureEach { options.compilerArgs.add("-Xlint:-exports") } + +testModuleInfo { + requires("org.assertj.core") + requires("org.junit.jupiter.api") +} diff --git a/hedera-node/hedera-file-service/src/main/java/com/hedera/node/app/service/file/FileService.java b/hedera-node/hedera-file-service/src/main/java/com/hedera/node/app/service/file/FileService.java index 470ad74ce667..529db187d6b7 100644 --- a/hedera-node/hedera-file-service/src/main/java/com/hedera/node/app/service/file/FileService.java +++ b/hedera-node/hedera-file-service/src/main/java/com/hedera/node/app/service/file/FileService.java @@ -16,8 +16,8 @@ package com.hedera.node.app.service.file; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.ServiceFactory; +import com.hedera.node.app.spi.RpcService; +import com.hedera.node.app.spi.RpcServiceFactory; import com.hedera.pbj.runtime.RpcServiceDefinition; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ServiceLoader; @@ -28,7 +28,7 @@ * href="https://github.com/hashgraph/hedera-protobufs/blob/main/services/file_service.proto">File * Service. */ -public interface FileService extends Service { +public interface FileService extends RpcService { /** * The name of the service @@ -54,6 +54,6 @@ default Set rpcDefinitions() { */ @NonNull static FileService getInstance() { - return ServiceFactory.loadService(FileService.class, ServiceLoader.load(FileService.class)); + return RpcServiceFactory.loadService(FileService.class, ServiceLoader.load(FileService.class)); } } diff --git a/hedera-node/hedera-file-service/src/main/java/com/hedera/node/app/service/file/ReadableUpgradeFileStore.java b/hedera-node/hedera-file-service/src/main/java/com/hedera/node/app/service/file/ReadableUpgradeFileStore.java index 289c48a832b4..8d18ef6617d9 100644 --- a/hedera-node/hedera-file-service/src/main/java/com/hedera/node/app/service/file/ReadableUpgradeFileStore.java +++ b/hedera-node/hedera-file-service/src/main/java/com/hedera/node/app/service/file/ReadableUpgradeFileStore.java @@ -18,9 +18,9 @@ import com.hedera.hapi.node.base.FileID; import com.hedera.hapi.node.state.file.File; -import com.hedera.node.app.spi.state.Schema; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.state.spi.ReadableQueueState; +import com.swirlds.state.spi.Schema; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.io.IOException; diff --git a/hedera-node/hedera-file-service/src/main/java/module-info.java b/hedera-node/hedera-file-service/src/main/java/module-info.java index d0ed43778e15..d99e5d272e76 100644 --- a/hedera-node/hedera-file-service/src/main/java/module-info.java +++ b/hedera-node/hedera-file-service/src/main/java/module-info.java @@ -3,10 +3,10 @@ uses com.hedera.node.app.service.file.FileService; - requires com.hedera.node.app.hapi.utils; - requires com.swirlds.state.api; requires transitive com.hedera.node.app.spi; requires transitive com.hedera.node.hapi; requires transitive com.hedera.pbj.runtime; + requires com.hedera.node.app.hapi.utils; + requires com.swirlds.state.api; requires static com.github.spotbugs.annotations; } diff --git a/hedera-node/hedera-file-service/src/test/java/com/hedera/node/app/service/file/FileServiceDefinitionTest.java b/hedera-node/hedera-file-service/src/test/java/com/hedera/node/app/service/file/FileServiceDefinitionTest.java new file mode 100644 index 000000000000..dc7d14a0931b --- /dev/null +++ b/hedera-node/hedera-file-service/src/test/java/com/hedera/node/app/service/file/FileServiceDefinitionTest.java @@ -0,0 +1,48 @@ +/* + * 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.service.file; + +import com.hedera.hapi.node.base.Transaction; +import com.hedera.hapi.node.transaction.Query; +import com.hedera.hapi.node.transaction.Response; +import com.hedera.hapi.node.transaction.TransactionResponse; +import com.hedera.pbj.runtime.RpcMethodDefinition; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +class FileServiceDefinitionTest { + + @Test + void checkBasePath() { + Assertions.assertThat(FileServiceDefinition.INSTANCE.basePath()).isEqualTo("proto.FileService"); + } + + @Test + void methodsDefined() { + final var methods = FileServiceDefinition.INSTANCE.methods(); + Assertions.assertThat(methods) + .containsExactlyInAnyOrder( + new RpcMethodDefinition<>("createFile", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("updateFile", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("deleteFile", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("appendContent", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("getFileContent", Query.class, Response.class), + new RpcMethodDefinition<>("getFileInfo", Query.class, Response.class), + new RpcMethodDefinition<>("systemDelete", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("systemUndelete", Transaction.class, TransactionResponse.class)); + } +} diff --git a/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/pbj/PbjConverter.java b/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/pbj/PbjConverter.java index aa372658580a..3f36828d17ad 100644 --- a/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/pbj/PbjConverter.java +++ b/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/pbj/PbjConverter.java @@ -319,6 +319,10 @@ public final class PbjConverter { case NetworkGetExecutionTime -> HederaFunctionality.NETWORK_GET_EXECUTION_TIME; case NONE -> HederaFunctionality.NONE; case NodeStakeUpdate -> HederaFunctionality.NODE_STAKE_UPDATE; + case NodeCreate -> HederaFunctionality.NODE_CREATE; + case NodeUpdate -> HederaFunctionality.NODE_UPDATE; + case NodeDelete -> HederaFunctionality.NODE_DELETE; + case NodeGetInfo -> HederaFunctionality.NODE_GET_INFO; case ScheduleCreate -> HederaFunctionality.SCHEDULE_CREATE; case ScheduleDelete -> HederaFunctionality.SCHEDULE_DELETE; case ScheduleGetInfo -> HederaFunctionality.SCHEDULE_GET_INFO; @@ -411,6 +415,10 @@ public final class PbjConverter { .NetworkGetExecutionTime; case NONE -> com.hederahashgraph.api.proto.java.HederaFunctionality.NONE; case NODE_STAKE_UPDATE -> com.hederahashgraph.api.proto.java.HederaFunctionality.NodeStakeUpdate; + case NODE_CREATE -> com.hederahashgraph.api.proto.java.HederaFunctionality.NodeCreate; + case NODE_UPDATE -> com.hederahashgraph.api.proto.java.HederaFunctionality.NodeUpdate; + case NODE_DELETE -> com.hederahashgraph.api.proto.java.HederaFunctionality.NodeDelete; + case NODE_GET_INFO -> com.hederahashgraph.api.proto.java.HederaFunctionality.NodeGetInfo; case SCHEDULE_CREATE -> com.hederahashgraph.api.proto.java.HederaFunctionality.ScheduleCreate; case SCHEDULE_DELETE -> com.hederahashgraph.api.proto.java.HederaFunctionality.ScheduleDelete; case SCHEDULE_GET_INFO -> com.hederahashgraph.api.proto.java.HederaFunctionality.ScheduleGetInfo; @@ -773,7 +781,21 @@ public final class PbjConverter { case TOKEN_HAS_NO_METADATA_KEY -> ResponseCodeEnum.TOKEN_HAS_NO_METADATA_KEY; case MISSING_SERIAL_NUMBERS -> ResponseCodeEnum.MISSING_SERIAL_NUMBERS; case TOKEN_HAS_NO_ADMIN_KEY -> ResponseCodeEnum.TOKEN_HAS_NO_ADMIN_KEY; + case NODE_DELETED -> ResponseCodeEnum.NODE_DELETED; + case INVALID_NODE_ID -> ResponseCodeEnum.INVALID_NODE_ID; + case INVALID_GOSSIP_ENDPOINT -> ResponseCodeEnum.INVALID_GOSSIP_ENDPOINT; + case INVALID_NODE_ACCOUNT_ID -> ResponseCodeEnum.INVALID_NODE_ACCOUNT_ID; + case INVALID_NODE_DESCRIPTION -> ResponseCodeEnum.INVALID_NODE_DESCRIPTION; + case INVALID_SERVICE_ENDPOINT -> ResponseCodeEnum.INVALID_SERVICE_ENDPOINT; + case INVALID_GOSSIP_CAE_CERTIFICATE -> ResponseCodeEnum.INVALID_GOSSIP_CAE_CERTIFICATE; + case INVALID_GRPC_CERTIFICATE -> ResponseCodeEnum.INVALID_GRPC_CERTIFICATE; case INVALID_MAX_AUTO_ASSOCIATIONS -> ResponseCodeEnum.INVALID_MAX_AUTO_ASSOCIATIONS; + case MAX_NODES_CREATED -> ResponseCodeEnum.MAX_NODES_CREATED; + case IP_FQDN_CANNOT_BE_SET_FOR_SAME_ENDPOINT -> ResponseCodeEnum.IP_FQDN_CANNOT_BE_SET_FOR_SAME_ENDPOINT; + case GOSSIP_ENDPOINT_CANNOT_HAVE_FQDN -> ResponseCodeEnum.GOSSIP_ENDPOINT_CANNOT_HAVE_FQDN; + case FQDN_SIZE_TOO_LARGE -> ResponseCodeEnum.FQDN_SIZE_TOO_LARGE; + case INVALID_ENDPOINT -> ResponseCodeEnum.INVALID_ENDPOINT; + case GOSSIP_ENDPOINTS_EXCEEDED_LIMIT -> ResponseCodeEnum.GOSSIP_ENDPOINTS_EXCEEDED_LIMIT; case UNRECOGNIZED -> throw new RuntimeException("UNRECOGNIZED Response code!"); }; } @@ -1310,8 +1332,30 @@ public static com.hederahashgraph.api.proto.java.ResponseCodeEnum fromPbj(@NonNu case MISSING_TOKEN_METADATA -> com.hederahashgraph.api.proto.java.ResponseCodeEnum.MISSING_TOKEN_METADATA; case MISSING_SERIAL_NUMBERS -> com.hederahashgraph.api.proto.java.ResponseCodeEnum.MISSING_SERIAL_NUMBERS; case TOKEN_HAS_NO_ADMIN_KEY -> com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_HAS_NO_ADMIN_KEY; + case NODE_DELETED -> com.hederahashgraph.api.proto.java.ResponseCodeEnum.NODE_DELETED; + case INVALID_NODE_ID -> com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_NODE_ID; + case INVALID_GOSSIP_ENDPOINT -> com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_GOSSIP_ENDPOINT; + case INVALID_NODE_ACCOUNT_ID -> com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_NODE_ACCOUNT_ID; + case INVALID_NODE_DESCRIPTION -> com.hederahashgraph.api.proto.java.ResponseCodeEnum + .INVALID_NODE_DESCRIPTION; + case INVALID_SERVICE_ENDPOINT -> com.hederahashgraph.api.proto.java.ResponseCodeEnum + .INVALID_SERVICE_ENDPOINT; + case INVALID_GOSSIP_CAE_CERTIFICATE -> com.hederahashgraph.api.proto.java.ResponseCodeEnum + .INVALID_GOSSIP_CAE_CERTIFICATE; + case INVALID_GRPC_CERTIFICATE -> com.hederahashgraph.api.proto.java.ResponseCodeEnum + .INVALID_GRPC_CERTIFICATE; case INVALID_MAX_AUTO_ASSOCIATIONS -> com.hederahashgraph.api.proto.java.ResponseCodeEnum .INVALID_MAX_AUTO_ASSOCIATIONS; + case MAX_NODES_CREATED -> com.hederahashgraph.api.proto.java.ResponseCodeEnum.MAX_NODES_CREATED; + case IP_FQDN_CANNOT_BE_SET_FOR_SAME_ENDPOINT -> com.hederahashgraph.api.proto.java.ResponseCodeEnum + .IP_FQDN_CANNOT_BE_SET_FOR_SAME_ENDPOINT; + case GOSSIP_ENDPOINT_CANNOT_HAVE_FQDN -> com.hederahashgraph.api.proto.java.ResponseCodeEnum + .GOSSIP_ENDPOINT_CANNOT_HAVE_FQDN; + case FQDN_SIZE_TOO_LARGE -> com.hederahashgraph.api.proto.java.ResponseCodeEnum.FQDN_SIZE_TOO_LARGE; + case INVALID_ENDPOINT -> com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_ENDPOINT; + case GOSSIP_ENDPOINTS_EXCEEDED_LIMIT -> com.hederahashgraph.api.proto.java.ResponseCodeEnum + .GOSSIP_ENDPOINTS_EXCEEDED_LIMIT; + // case UNRECOGNIZED -> throw new RuntimeException("UNRECOGNIZED Response code!"); }; } diff --git a/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/sigs/HollowScreening.java b/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/sigs/HollowScreening.java index f51bfe3005f2..abd09d7a0df3 100644 --- a/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/sigs/HollowScreening.java +++ b/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/sigs/HollowScreening.java @@ -145,13 +145,13 @@ static HollowScreenResult performFor( /** * Given a list of {@link TransactionSignature}, go through the signatures, and construct a - * look-up table for all present evm address <-> ECDSA key pairs in the sigs. + * look-up table for all present evm address <-> ECDSA key pairs in the sigs. * *

Serves as an efficient way of obtaining a {@link JECDSASecp256k1Key} corresponding to {@link JWildcardECDSAKey} * in subsequent logic. * * @param txnSigs a list of {@link TransactionSignature} - * @return a {@code Map} of all present evm addresses <-> {@link JECDSASecp256k1Key} pairs in the {@code txnSigs} + * @return a {@code Map} of all present evm addresses <-> {@link JECDSASecp256k1Key} pairs in the {@code txnSigs} */ private static Map createEvmAddressToEcdsaKeyIndexFrom( final List txnSigs) { diff --git a/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/state/initialization/BlocklistAccountCreator.java b/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/state/initialization/BlocklistAccountCreator.java index 6e8b78487454..39897fdd96c6 100644 --- a/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/state/initialization/BlocklistAccountCreator.java +++ b/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/state/initialization/BlocklistAccountCreator.java @@ -190,9 +190,9 @@ private List readPrivateKeyBlocklistFromResource(String blocklistResourc * Parses a line from the blocklist resource and returns blocked account info record. * * The line should have the following format: - * , - * where is a hex-encoded private key - * and is a memo for the blocked account + * <private key>,<memo> + * where <private key> is a hex-encoded private key + * and <memo> is a memo for the blocked account * and both values are comma-separated. * * The resulting blocked account info record contains the EVM address derived from the private key, and the memo. diff --git a/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/store/contracts/AbstractLedgerWorldUpdater.java b/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/store/contracts/AbstractLedgerWorldUpdater.java index 7fefecfcc1cc..c73bf08953ee 100644 --- a/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/store/contracts/AbstractLedgerWorldUpdater.java +++ b/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/store/contracts/AbstractLedgerWorldUpdater.java @@ -135,7 +135,7 @@ && customizerForPendingCreation().appliesTo(aliases().resolveForEvm(address))) { * - hedera entity num will not be allocated for that account * - customizer will not be attached for that account * - * @param Address addressOrAlias + * @param addressOrAlias address or alias of the account * @return MutableAccount */ private MutableAccount createGhostAccount(final Address addressOrAlias) { diff --git a/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/stream/RecordStreamFileWriter.java b/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/stream/RecordStreamFileWriter.java index 714d6a14e59e..995d06d74d6b 100644 --- a/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/stream/RecordStreamFileWriter.java +++ b/hedera-node/hedera-mono-service/src/main/java/com/hedera/node/app/service/mono/stream/RecordStreamFileWriter.java @@ -92,36 +92,36 @@ public class RecordStreamFileWriter implements LinkedObjectStream().configureEach { options.compilerArgs.add("-Xlint:-exports") } + +testModuleInfo { + requires("org.assertj.core") + requires("org.junit.jupiter.api") +} diff --git a/hedera-node/hedera-network-admin-service/src/main/java/com/hedera/node/app/service/networkadmin/FreezeService.java b/hedera-node/hedera-network-admin-service/src/main/java/com/hedera/node/app/service/networkadmin/FreezeService.java index d90c9ae82562..702c047d3857 100644 --- a/hedera-node/hedera-network-admin-service/src/main/java/com/hedera/node/app/service/networkadmin/FreezeService.java +++ b/hedera-node/hedera-network-admin-service/src/main/java/com/hedera/node/app/service/networkadmin/FreezeService.java @@ -16,8 +16,8 @@ package com.hedera.node.app.service.networkadmin; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.ServiceFactory; +import com.hedera.node.app.spi.RpcService; +import com.hedera.node.app.spi.RpcServiceFactory; import com.hedera.pbj.runtime.RpcServiceDefinition; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ServiceLoader; @@ -28,7 +28,7 @@ * href="https://github.com/hashgraph/hedera-protobufs/blob/main/services/freeze_service.proto">Freeze * Service. */ -public interface FreezeService extends Service { +public interface FreezeService extends RpcService { String NAME = "FreezeService"; @@ -51,6 +51,6 @@ default Set rpcDefinitions() { */ @NonNull static FreezeService getInstance() { - return ServiceFactory.loadService(FreezeService.class, ServiceLoader.load(FreezeService.class)); + return RpcServiceFactory.loadService(FreezeService.class, ServiceLoader.load(FreezeService.class)); } } diff --git a/hedera-node/hedera-network-admin-service/src/main/java/com/hedera/node/app/service/networkadmin/NetworkService.java b/hedera-node/hedera-network-admin-service/src/main/java/com/hedera/node/app/service/networkadmin/NetworkService.java index e42ef1168864..1864d6b6d53a 100644 --- a/hedera-node/hedera-network-admin-service/src/main/java/com/hedera/node/app/service/networkadmin/NetworkService.java +++ b/hedera-node/hedera-network-admin-service/src/main/java/com/hedera/node/app/service/networkadmin/NetworkService.java @@ -16,8 +16,8 @@ package com.hedera.node.app.service.networkadmin; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.ServiceFactory; +import com.hedera.node.app.spi.RpcService; +import com.hedera.node.app.spi.RpcServiceFactory; import com.hedera.pbj.runtime.RpcServiceDefinition; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ServiceLoader; @@ -28,7 +28,7 @@ * href="https://github.com/hashgraph/hedera-protobufs/blob/main/services/network_service.proto">Network * Service. */ -public interface NetworkService extends Service { +public interface NetworkService extends RpcService { String NAME = "NetworkService"; @NonNull @@ -50,6 +50,6 @@ default Set rpcDefinitions() { */ @NonNull static NetworkService getInstance() { - return ServiceFactory.loadService(NetworkService.class, ServiceLoader.load(NetworkService.class)); + return RpcServiceFactory.loadService(NetworkService.class, ServiceLoader.load(NetworkService.class)); } } diff --git a/hedera-node/hedera-network-admin-service/src/main/java/module-info.java b/hedera-node/hedera-network-admin-service/src/main/java/module-info.java index af696f42fc13..90b45e0f596f 100644 --- a/hedera-node/hedera-network-admin-service/src/main/java/module-info.java +++ b/hedera-node/hedera-network-admin-service/src/main/java/module-info.java @@ -4,9 +4,9 @@ uses com.hedera.node.app.service.networkadmin.FreezeService; uses com.hedera.node.app.service.networkadmin.NetworkService; - requires com.hedera.node.app.hapi.utils; requires transitive com.hedera.node.app.spi; requires transitive com.hedera.node.hapi; requires transitive com.hedera.pbj.runtime; + requires com.hedera.node.app.hapi.utils; requires static com.github.spotbugs.annotations; } diff --git a/hedera-node/hedera-network-admin-service/src/test/java/com/hedera/node/app/service/networkadmin/FreezeServiceDefinitionTest.java b/hedera-node/hedera-network-admin-service/src/test/java/com/hedera/node/app/service/networkadmin/FreezeServiceDefinitionTest.java new file mode 100644 index 000000000000..96f169c2b5d0 --- /dev/null +++ b/hedera-node/hedera-network-admin-service/src/test/java/com/hedera/node/app/service/networkadmin/FreezeServiceDefinitionTest.java @@ -0,0 +1,39 @@ +/* + * 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.service.networkadmin; + +import com.hedera.hapi.node.base.Transaction; +import com.hedera.hapi.node.transaction.TransactionResponse; +import com.hedera.pbj.runtime.RpcMethodDefinition; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +class FreezeServiceDefinitionTest { + + @Test + void checkBasePath() { + Assertions.assertThat(FreezeServiceDefinition.INSTANCE.basePath()).isEqualTo("proto.FreezeService"); + } + + @Test + void methodsDefined() { + final var methods = FreezeServiceDefinition.INSTANCE.methods(); + Assertions.assertThat(methods) + .containsExactlyInAnyOrder( + new RpcMethodDefinition<>("freeze", Transaction.class, TransactionResponse.class)); + } +} diff --git a/hedera-node/hedera-network-admin-service/src/test/java/com/hedera/node/app/service/networkadmin/NetworkServiceDefinitionTest.java b/hedera-node/hedera-network-admin-service/src/test/java/com/hedera/node/app/service/networkadmin/NetworkServiceDefinitionTest.java new file mode 100644 index 000000000000..8a9bc035f1a9 --- /dev/null +++ b/hedera-node/hedera-network-admin-service/src/test/java/com/hedera/node/app/service/networkadmin/NetworkServiceDefinitionTest.java @@ -0,0 +1,44 @@ +/* + * 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.service.networkadmin; + +import com.hedera.hapi.node.base.Transaction; +import com.hedera.hapi.node.transaction.Query; +import com.hedera.hapi.node.transaction.Response; +import com.hedera.hapi.node.transaction.TransactionResponse; +import com.hedera.pbj.runtime.RpcMethodDefinition; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +class NetworkServiceDefinitionTest { + + @Test + void checkBasePath() { + Assertions.assertThat(NetworkServiceDefinition.INSTANCE.basePath()).isEqualTo("proto.NetworkService"); + } + + @Test + void methodsDefined() { + final var methods = NetworkServiceDefinition.INSTANCE.methods(); + Assertions.assertThat(methods) + .containsExactlyInAnyOrder( + new RpcMethodDefinition<>("getVersionInfo", Query.class, Response.class), + new RpcMethodDefinition<>("getExecutionTime", Query.class, Response.class), + new RpcMethodDefinition<>("uncheckedSubmit", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("getAccountDetails", Query.class, Response.class)); + } +} diff --git a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/ScheduleServiceImpl.java b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/ScheduleServiceImpl.java index 21f2c28f4b3b..adc37239249e 100644 --- a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/ScheduleServiceImpl.java +++ b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/ScheduleServiceImpl.java @@ -18,12 +18,12 @@ import com.hedera.node.app.service.schedule.ScheduleService; import com.hedera.node.app.service.schedule.impl.schemas.V0490ScheduleSchema; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.state.SchemaRegistry; +import com.hedera.node.app.spi.RpcService; +import com.swirlds.state.spi.SchemaRegistry; import edu.umd.cs.findbugs.annotations.NonNull; /** - * Standard implementation of the {@link ScheduleService} {@link Service}. + * Standard implementation of the {@link ScheduleService} {@link RpcService}. */ public final class ScheduleServiceImpl implements ScheduleService { @Override diff --git a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/ScheduleServiceInjectionModule.java b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/ScheduleServiceInjectionModule.java index e54427866918..38207f4cdd14 100644 --- a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/ScheduleServiceInjectionModule.java +++ b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/ScheduleServiceInjectionModule.java @@ -23,16 +23,45 @@ import com.hedera.node.app.service.schedule.impl.handlers.ScheduleSignHandler; import dagger.Module; +/** + * Schedule service injection interface. Used to inject the schedule service handlers into the + * implementation class using Dagger dependency injection + */ @Module public interface ScheduleServiceInjectionModule { + /** + * Schedule create handler + * + * @return the schedule create handler + */ ScheduleCreateHandler scheduleCreateHandler(); + /** + * Schedule delete handler + * + * @return the schedule delete handler + */ ScheduleDeleteHandler scheduleDeleteHandler(); + /** + * Schedule get info handler + * + * @return the schedule get info handler + */ ScheduleGetInfoHandler scheduleGetInfoHandler(); + /** + * Schedule sign handler + * + * @return the schedule sign handler + */ ScheduleSignHandler scheduleSignHandler(); + /** + * Schedule handlers + * + * @return the schedule handlers + */ ScheduleHandlers scheduleHandlers(); } diff --git a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/ScheduleStoreUtility.java b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/ScheduleStoreUtility.java index bf228d93aa59..40ce0ea56d9e 100644 --- a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/ScheduleStoreUtility.java +++ b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/ScheduleStoreUtility.java @@ -31,11 +31,20 @@ import java.util.Collections; import java.util.Objects; +/** + * Provides utility methods for the schedule store. + * Used to calculate the hash of a schedule which is then used to store the schedule in the schedule store. + * */ public final class ScheduleStoreUtility { private ScheduleStoreUtility() {} - // @todo('7773') This requires rebuilding the equality virtual map on migration, - // because it's different from ScheduleVirtualValue (and must be, due to PBJ shift) + /** + * Calculate bytes hash of a schedule based on the schedule's memo, admin key, scheduled transaction, expiration + * time, and wait for expiry flag. + * + * @param scheduleToHash the schedule to hash + * @return the bytes + */ @SuppressWarnings("UnstableApiUsage") public static Bytes calculateBytesHash(@NonNull final Schedule scheduleToHash) { Objects.requireNonNull(scheduleToHash); @@ -77,6 +86,22 @@ private static boolean isScheduleInList(final ScheduleID scheduleId, final Sched .anyMatch(s -> s.scheduleIdOrThrow().equals(scheduleId)); } + /** + * Adds a {@link Schedule} to a {@link ScheduleList}, replacing it if it already exists. + * + *

This method checks if the provided {@code Schedule} is already present in the {@code ScheduleList}. + * If it is, the existing {@code Schedule} is replaced with the new one. If it isn't, the {@code Schedule} + * is added to the list. This allows for updating entries within a {@code ScheduleList} without needing to + * manually manage duplicates or replacements. + * + * @param schedule The {@link Schedule} to add or replace in the {@code ScheduleList}. Must not be {@code null}, + * unless the {@code ScheduleList} is also {@code null}. + * @param scheduleList The {@link ScheduleList} to which the {@code Schedule} will be added or replaced. May be + * {@code null}, in which case a new {@link ScheduleList} containing only the provided + * {@code Schedule} is returned. + * @return A new {@link ScheduleList} containing the {@code Schedule} either added or replacing an existing one. + * Never returns {@code null}. + */ static ScheduleList addOrReplace(final Schedule schedule, @Nullable final ScheduleList scheduleList) { if (scheduleList == null) { return new ScheduleList(Collections.singletonList(schedule)); diff --git a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/codec/ScheduleServiceStateTranslator.java b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/codec/ScheduleServiceStateTranslator.java index 0251fd0abe48..51db8522cf48 100644 --- a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/codec/ScheduleServiceStateTranslator.java +++ b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/codec/ScheduleServiceStateTranslator.java @@ -42,11 +42,24 @@ import java.util.Objects; import java.util.Optional; +/** + * Utility class used for conversion of schedule virtual values to schedules. + * @deprecated Since there should not be anymore ScheduleVirtualValue objects in state, + * this class should no longer be required and will be removed in a future release + */ +@Deprecated(forRemoval = true) public final class ScheduleServiceStateTranslator { private static final int ED25519_KEY_LENGTH = 32; private ScheduleServiceStateTranslator() {} + /** + * Convert schedule virtual value to schedule. + * + * @param virtualValue the virtual value + * @return the schedule + * @throws ParseException if there is an error parsing the TransactionBody message + */ public static Schedule convertScheduleVirtualValueToSchedule( @NonNull final com.hedera.node.app.service.mono.state.virtual.schedule.ScheduleVirtualValue virtualValue) throws ParseException { @@ -142,6 +155,14 @@ private static Timestamp getResolutionTime(@NonNull final ScheduleVirtualValue v else return null; } + /** + * Migrates the state of the schedule service from the scheduleId to the schedule virtual value + * using the readableStore and the ScheduleId. + * + * @param scheduleID the schedule id + * @param readableScheduleStore the readable schedule store + * @return the schedule virtual value + */ @NonNull public static com.hedera.node.app.service.mono.state.virtual.schedule.ScheduleVirtualValue pbjToState( @NonNull final ScheduleID scheduleID, @NonNull final ReadableScheduleStore readableScheduleStore) { @@ -154,6 +175,12 @@ public static com.hedera.node.app.service.mono.state.virtual.schedule.ScheduleVi return pbjToState(optionalSchedule); } + /** + * Converts a {@link Schedule} object to a {@link ScheduleVirtualValue} + * * + * @param schedule the schedule + * @return the schedule virtual value + */ @NonNull public static com.hedera.node.app.service.mono.state.virtual.schedule.ScheduleVirtualValue pbjToState( @NonNull final Schedule schedule) { diff --git a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/AbstractScheduleHandler.java b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/AbstractScheduleHandler.java index 68db4570dd80..da33c554d358 100644 --- a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/AbstractScheduleHandler.java +++ b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/AbstractScheduleHandler.java @@ -60,12 +60,22 @@ abstract class AbstractScheduleHandler { /** * A simple record to return both "deemed valid" signatories and remaining primitive keys that must sign. + * * @param updatedSignatories a Set of "deemed valid" signatories, possibly updated with new entries * @param remainingRequiredKeys A Set of Key entries that have not yet signed the scheduled transaction, but * must sign that transaction before it can be executed. */ protected static record ScheduleKeysResult(Set updatedSignatories, Set remainingRequiredKeys) {} + /** + * Gets the set of all the keys required to sign a transaction. + * + * @param scheduleInState the schedule in state + * @param context the Prehandle context + * @return the set of keys required to sign the transaction + * @throws PreCheckException if the transaction cannot be handled successfully due to a validation failure of the + * dispatcher related to signer requirements or other pre-validation criteria. + */ @NonNull protected Set allKeysForTransaction( @NonNull final Schedule scheduleInState, @NonNull final PreHandleContext context) throws PreCheckException { @@ -78,6 +88,14 @@ protected Set allKeysForTransaction( return getKeySetFromTransactionKeys(keyStructure); } + /** + * Get the schedule keys result to sign the transaction + * + * @param scheduleInState the schedule in state + * @param context the Prehandle context + * @return the schedule keys result containing the updated signatories and the remaining required keys + * @throws HandleException if any validation check fails when getting the keys for the transaction + */ @NonNull protected ScheduleKeysResult allKeysForTransaction( @NonNull final Schedule scheduleInState, @NonNull final HandleContext context) throws HandleException { @@ -144,6 +162,13 @@ protected void verifyHasNewSignatures( throw new HandleException(ResponseCodeEnum.NO_NEW_VALID_SIGNATURES); } + /** + * Gets key for account. + * + * @param context the handle context + * @param accountToQuery the account to query + * @return the key for account + */ @Nullable protected Key getKeyForAccount(@NonNull final HandleContext context, @NonNull final AccountID accountToQuery) { final ReadableAccountStore accountStore = context.readableStore(ReadableAccountStore.class); @@ -276,6 +301,18 @@ protected void checkValidTransactionId(@Nullable final TransactionID currentId) if (validStart == null) throw new PreCheckException(ResponseCodeEnum.INVALID_TRANSACTION_START); } + /** + * Try to execute a schedule. Will attempt to execute a schedule if the remaining signatories are empty + * and the schedule is not waiting for expiration. + * + * @param context the context + * @param scheduleToExecute the schedule to execute + * @param remainingSignatories the remaining signatories + * @param validSignatories the valid signatories + * @param validationResult the validation result + * @param isLongTermEnabled the is long term enabled + * @return boolean indicating if the schedule was executed + */ protected boolean tryToExecuteSchedule( @NonNull final HandleContext context, @NonNull final Schedule scheduleToExecute, @@ -314,6 +351,12 @@ protected boolean tryToExecuteSchedule( } } + /** + * Checks if the validation is OK, SUCCESS, or SCHEDULE_PENDING_EXPIRATION. + * + * @param validationResult the validation result + * @return boolean indicating status of the validation + */ protected boolean validationOk(final ResponseCodeEnum validationResult) { return validationResult == ResponseCodeEnum.OK || validationResult == ResponseCodeEnum.SUCCESS diff --git a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/HandlerUtility.java b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/HandlerUtility.java index 56efce6a2581..30727e41564e 100644 --- a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/HandlerUtility.java +++ b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/HandlerUtility.java @@ -43,6 +43,12 @@ public final class HandlerUtility { private HandlerUtility() {} + /** + * Return child as ordinary transaction body. + * + * @param scheduleInState the schedule in state to convert to transaction body + * @return the transaction body + */ @NonNull public static TransactionBody childAsOrdinary(@NonNull final Schedule scheduleInState) { final TransactionID scheduledTransactionId = transactionIdForScheduled(scheduleInState); @@ -103,12 +109,20 @@ public static TransactionBody childAsOrdinary(@NonNull final Schedule scheduleIn case TOKEN_FEE_SCHEDULE_UPDATE -> ordinary.tokenFeeScheduleUpdate( scheduledTransaction.tokenFeeScheduleUpdateOrThrow()); case UTIL_PRNG -> ordinary.utilPrng(scheduledTransaction.utilPrngOrThrow()); + case NODE_CREATE -> ordinary.nodeCreate(scheduledTransaction.nodeCreateOrThrow()); + case NODE_UPDATE -> ordinary.nodeUpdate(scheduledTransaction.nodeUpdateOrThrow()); + case NODE_DELETE -> ordinary.nodeDelete(scheduledTransaction.nodeDeleteOrThrow()); case UNSET -> throw new HandleException(ResponseCodeEnum.INVALID_TRANSACTION); } } return ordinary.build(); } + /** + * Given a Transaction of one type, return the corresponding HederaFunctionality. + * @param transactionType the transaction type + * @return the hedera functionality + */ static HederaFunctionality functionalityForType(final DataOneOfType transactionType) { return switch (transactionType) { case CONSENSUS_CREATE_TOPIC -> HederaFunctionality.CONSENSUS_CREATE_TOPIC; @@ -150,6 +164,9 @@ static HederaFunctionality functionalityForType(final DataOneOfType transactionT case TOKEN_FEE_SCHEDULE_UPDATE -> HederaFunctionality.TOKEN_FEE_SCHEDULE_UPDATE; case UTIL_PRNG -> HederaFunctionality.UTIL_PRNG; case TOKEN_UPDATE_NFTS -> HederaFunctionality.TOKEN_UPDATE_NFTS; + case NODE_CREATE -> HederaFunctionality.NODE_CREATE; + case NODE_UPDATE -> HederaFunctionality.NODE_UPDATE; + case NODE_DELETE -> HederaFunctionality.NODE_DELETE; case UNSET -> HederaFunctionality.NONE; }; } @@ -170,11 +187,27 @@ static Schedule markExecuted(@NonNull final Schedule schedule, @NonNull final In .build(); } + /** + * Replace the signatories of a schedule with a new set of signatories. + * The schedule is not modified in place. + * + * @param schedule the schedule + * @param newSignatories the new signatories + * @return the schedule + */ @NonNull static Schedule replaceSignatories(@NonNull final Schedule schedule, @NonNull final Set newSignatories) { return schedule.copyBuilder().signatories(List.copyOf(newSignatories)).build(); } + /** + * Replace signatories and mark executed schedule. + * + * @param schedule the schedule + * @param newSignatories the new signatories + * @param consensusTime the consensus time + * @return the schedule + */ @NonNull static Schedule replaceSignatoriesAndMarkExecuted( @NonNull final Schedule schedule, @@ -230,6 +263,16 @@ static Schedule createProvisionalSchedule( return builder.build(); } + /** + * Complete the processing of a provisional schedule, which was created during a ScheduleCreate transaction. + * The schedule is completed by adding a schedule ID and signatories. + * + * @param provisionalSchedule the provisional schedule + * @param newEntityNumber the new entity number + * @param finalSignatories the final signatories for the schedule + * @return the schedule + * @throws HandleException if the transaction is not handled successfully. + */ @NonNull static Schedule completeProvisionalSchedule( @NonNull final Schedule provisionalSchedule, @@ -247,6 +290,15 @@ static Schedule completeProvisionalSchedule( return build.build(); } + /** + * Gets next schedule id for a given parent transaction id and new schedule number. + * The schedule ID is created using the shard and realm numbers from the parent transaction ID, + * and the new schedule number. + * + * @param parentTransactionId the parent transaction id + * @param newScheduleNumber the new schedule number + * @return the next schedule id + */ @NonNull static ScheduleID getNextScheduleID( @NonNull final TransactionID parentTransactionId, final long newScheduleNumber) { @@ -257,6 +309,12 @@ static ScheduleID getNextScheduleID( return builder.scheduleNum(newScheduleNumber).build(); } + /** + * Transaction id for scheduled transaction id. + * + * @param valueInState the value in state + * @return the transaction id + */ @NonNull static TransactionID transactionIdForScheduled(@NonNull Schedule valueInState) { // original create transaction and its transaction ID will never be null, but Sonar... @@ -287,6 +345,13 @@ private static long calculateExpiration( } } + /** + * Filters the signatories to only those that are required. + * The required signatories are those that are present in the incoming signatories set. + * + * @param signatories the signatories + * @param required the required + */ static void filterSignatoriesToRequired(Set signatories, Set required) { final Set incomingSignatories = Set.copyOf(signatories); signatories.clear(); diff --git a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/ScheduleGetInfoHandler.java b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/ScheduleGetInfoHandler.java index da7adf2db5c5..d6256c322f0a 100644 --- a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/ScheduleGetInfoHandler.java +++ b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/ScheduleGetInfoHandler.java @@ -55,6 +55,12 @@ public class ScheduleGetInfoHandler extends PaidQueryHandler { private final ScheduleOpsUsage legacyUsage; + /** + * Constructor is used by the Dagger dependency injection framework to provide the necessary dependencies to the handler. + * The handler is responsible for handling the {@link HederaFunctionality#SCHEDULE_GET_INFO} query. + * + * @param legacyUsage the legacy usage + */ @Inject public ScheduleGetInfoHandler(ScheduleOpsUsage legacyUsage) { this.legacyUsage = legacyUsage; diff --git a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/ScheduleHandlers.java b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/ScheduleHandlers.java index 7eddc2c3e74f..f5682d39d1f3 100644 --- a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/ScheduleHandlers.java +++ b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/handlers/ScheduleHandlers.java @@ -22,6 +22,9 @@ import javax.inject.Inject; import javax.inject.Singleton; +/** + * Singleton that provides access to the various handlers for the Schedule Service. + */ @Singleton public class ScheduleHandlers { @@ -33,6 +36,14 @@ public class ScheduleHandlers { private final ScheduleSignHandler scheduleSignHandler; + /** + * Instantiates a new Schedule handler. + * + * @param scheduleCreateHandler the schedule create handler + * @param scheduleDeleteHandler the schedule delete handler + * @param scheduleGetInfoHandler the schedule get info handler + * @param scheduleSignHandler the schedule sign handler + */ @Inject public ScheduleHandlers( @NonNull final ScheduleCreateHandler scheduleCreateHandler, @@ -45,18 +56,38 @@ public ScheduleHandlers( this.scheduleSignHandler = requireNonNull(scheduleSignHandler, "scheduleSignHandler must not be null"); } + /** + * Schedule create handler. + * + * @return the schedule create handler + */ public ScheduleCreateHandler scheduleCreateHandler() { return scheduleCreateHandler; } + /** + * Schedule delete handler. + * + * @return the schedule delete handler + */ public ScheduleDeleteHandler scheduleDeleteHandler() { return scheduleDeleteHandler; } + /** + * Schedule get info handler. + * + * @return the schedule get info handler + */ public ScheduleGetInfoHandler scheduleGetInfoHandler() { return scheduleGetInfoHandler; } + /** + * Schedule sign handler. + * + * @return the schedule sign handler + */ public ScheduleSignHandler scheduleSignHandler() { return scheduleSignHandler; } diff --git a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/schemas/V0490ScheduleSchema.java b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/schemas/V0490ScheduleSchema.java index 3b3a16ba582e..2fc2b22ade04 100644 --- a/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/schemas/V0490ScheduleSchema.java +++ b/hedera-node/hedera-schedule-service-impl/src/main/java/com/hedera/node/app/service/schedule/impl/schemas/V0490ScheduleSchema.java @@ -25,11 +25,11 @@ import com.hedera.node.app.service.mono.state.merkle.MerkleScheduledTransactions; import com.hedera.node.app.service.schedule.impl.ScheduleStoreUtility; import com.hedera.node.app.service.schedule.impl.codec.ScheduleServiceStateTranslator; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.StateDefinition; import com.hedera.pbj.runtime.ParseException; import com.swirlds.platform.state.spi.WritableKVStateBase; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.StateDefinition; import com.swirlds.state.spi.WritableKVState; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -64,6 +64,9 @@ public final class V0490ScheduleSchema extends Schema { */ private static MerkleScheduledTransactions fs; + /** + * Instantiates a new V0490 (version 0.49.0) schedule schema. + */ public V0490ScheduleSchema() { super(VERSION); } @@ -181,6 +184,11 @@ private static StateDefinition schedulesByEquality() { SCHEDULES_BY_EQUALITY_KEY, ProtoBytes.PROTOBUF, ScheduleList.PROTOBUF, MAX_SCHEDULES_BY_EQUALITY); } + /** + * Used to migrate the state to the new schema. It is not thread safe and is set to null after migration. + * + * @param fs the state to migrate from + */ public static void setFs(@Nullable final MerkleScheduledTransactions fs) { V0490ScheduleSchema.fs = fs; } diff --git a/hedera-node/hedera-schedule-service-impl/src/main/java/module-info.java b/hedera-node/hedera-schedule-service-impl/src/main/java/module-info.java index 4c65ba1c49fd..65e264cdc6f0 100644 --- a/hedera-node/hedera-schedule-service-impl/src/main/java/module-info.java +++ b/hedera-node/hedera-schedule-service-impl/src/main/java/module-info.java @@ -1,25 +1,23 @@ module com.hedera.node.app.service.schedule.impl { requires transitive com.hedera.node.app.hapi.fees; - // Only ScheduleServiceStateTranslator requires this item, when that is removed, this should also be removed. - requires transitive com.hedera.node.app.service.mono; + requires transitive com.hedera.node.app.service.mono; // Only for ScheduleServiceStateTranslator requires transitive com.hedera.node.app.service.schedule; requires transitive com.hedera.node.app.spi; requires transitive com.hedera.node.hapi; - requires transitive com.hedera.pbj.runtime; requires transitive com.swirlds.config.api; requires transitive com.swirlds.state.api; + requires transitive com.hedera.pbj.runtime; requires transitive dagger; requires transitive javax.inject; requires com.hedera.node.app.hapi.utils; - // Required for ReadableAccountStore to read payer account details on create, sign, or query - requires com.hedera.node.app.service.token; + requires com.hedera.node.app.service.token; // ReadableAccountStore: payer account details on create, sign, query requires com.hedera.node.config; - requires com.google.common; requires com.swirlds.platform.core; - requires static com.github.spotbugs.annotations; - requires static java.compiler; // javax.annotation.processing.Generated + requires com.google.common; requires org.apache.logging.log4j; requires org.eclipse.collections.api; + requires static com.github.spotbugs.annotations; + requires static java.compiler; // javax.annotation.processing.Generated exports com.hedera.node.app.service.schedule.impl; exports com.hedera.node.app.service.schedule.impl.handlers; diff --git a/hedera-node/hedera-schedule-service-impl/src/test/java/com/hedera/node/app/service/schedule/impl/ScheduleServiceImplTest.java b/hedera-node/hedera-schedule-service-impl/src/test/java/com/hedera/node/app/service/schedule/impl/ScheduleServiceImplTest.java index 5f898b472ddd..bdbd52d33dc5 100644 --- a/hedera-node/hedera-schedule-service-impl/src/test/java/com/hedera/node/app/service/schedule/impl/ScheduleServiceImplTest.java +++ b/hedera-node/hedera-schedule-service-impl/src/test/java/com/hedera/node/app/service/schedule/impl/ScheduleServiceImplTest.java @@ -18,9 +18,9 @@ import com.hedera.node.app.service.schedule.ScheduleService; import com.hedera.node.app.service.schedule.impl.schemas.V0490ScheduleSchema; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.SchemaRegistry; -import com.hedera.node.app.spi.state.StateDefinition; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.SchemaRegistry; +import com.swirlds.state.spi.StateDefinition; import java.util.List; import java.util.Set; import org.assertj.core.api.BDDAssertions; diff --git a/hedera-node/hedera-schedule-service-impl/src/test/java/com/hedera/node/app/service/schedule/impl/schemas/V0490ScheduleSchemaTest.java b/hedera-node/hedera-schedule-service-impl/src/test/java/com/hedera/node/app/service/schedule/impl/schemas/V0490ScheduleSchemaTest.java index 30c3dceb33fb..382da34031b5 100644 --- a/hedera-node/hedera-schedule-service-impl/src/test/java/com/hedera/node/app/service/schedule/impl/schemas/V0490ScheduleSchemaTest.java +++ b/hedera-node/hedera-schedule-service-impl/src/test/java/com/hedera/node/app/service/schedule/impl/schemas/V0490ScheduleSchemaTest.java @@ -39,10 +39,10 @@ import com.hedera.node.app.service.mono.state.virtual.schedule.ScheduleVirtualValue; import com.hedera.node.app.service.mono.state.virtual.temporal.SecondSinceEpocVirtualKey; import com.hedera.node.app.spi.fixtures.state.MapWritableStates; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.StateDefinition; import com.swirlds.merkle.map.MerkleMap; import com.swirlds.platform.test.fixtures.state.MapWritableKVState; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.StateDefinition; import com.swirlds.state.spi.WritableStates; import java.time.Instant; import java.util.Comparator; diff --git a/hedera-node/hedera-schedule-service/build.gradle.kts b/hedera-node/hedera-schedule-service/build.gradle.kts index 30c230f84589..9efcc5ecd622 100644 --- a/hedera-node/hedera-schedule-service/build.gradle.kts +++ b/hedera-node/hedera-schedule-service/build.gradle.kts @@ -19,13 +19,14 @@ plugins { id("com.hedera.gradle.services-publish") } -description = "Hedera Scheduled Service API" +description = "Hedera Schedule Service API" // Remove the following line to enable all 'javac' lint checks that we have turned on by default // and then fix the reported issues. tasks.withType().configureEach { options.compilerArgs.add("-Xlint:-exports") } testModuleInfo { + requires("com.swirlds.state.api") requires("org.assertj.core") requires("org.junit.jupiter.api") } diff --git a/hedera-node/hedera-schedule-service/src/main/java/com/hedera/node/app/service/schedule/ScheduleRecordBuilder.java b/hedera-node/hedera-schedule-service/src/main/java/com/hedera/node/app/service/schedule/ScheduleRecordBuilder.java index 3fcf0be45316..78a0e2aff8c6 100644 --- a/hedera-node/hedera-schedule-service/src/main/java/com/hedera/node/app/service/schedule/ScheduleRecordBuilder.java +++ b/hedera-node/hedera-schedule-service/src/main/java/com/hedera/node/app/service/schedule/ScheduleRecordBuilder.java @@ -30,12 +30,30 @@ * transaction other than a ScheduleCreate, ScheduleSign, or ScheduleDelete. */ public interface ScheduleRecordBuilder { + /** + * Schedule ref schedule record builder. + * + * @param scheduleRef the schedule ref + * @return the schedule record builder + */ @NonNull ScheduleRecordBuilder scheduleRef(ScheduleID scheduleRef); + /** + * Schedule id schedule record builder. + * + * @param scheduleID the schedule id + * @return the schedule record builder + */ @NonNull ScheduleRecordBuilder scheduleID(ScheduleID scheduleID); + /** + * Scheduled transaction id schedule record builder. + * + * @param scheduledTransactionID the scheduled transaction id + * @return the schedule record builder + */ @NonNull ScheduleRecordBuilder scheduledTransactionID(TransactionID scheduledTransactionID); } diff --git a/hedera-node/hedera-schedule-service/src/main/java/com/hedera/node/app/service/schedule/ScheduleService.java b/hedera-node/hedera-schedule-service/src/main/java/com/hedera/node/app/service/schedule/ScheduleService.java index f446096ed939..00894c9c5103 100644 --- a/hedera-node/hedera-schedule-service/src/main/java/com/hedera/node/app/service/schedule/ScheduleService.java +++ b/hedera-node/hedera-schedule-service/src/main/java/com/hedera/node/app/service/schedule/ScheduleService.java @@ -16,8 +16,8 @@ package com.hedera.node.app.service.schedule; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.ServiceFactory; +import com.hedera.node.app.spi.RpcService; +import com.hedera.node.app.spi.RpcServiceFactory; import com.hedera.pbj.runtime.RpcServiceDefinition; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ServiceLoader; @@ -28,7 +28,7 @@ * href="https://github.com/hashgraph/hedera-protobufs/blob/main/services/schedule_service.proto">Schedule * Service. */ -public interface ScheduleService extends Service { +public interface ScheduleService extends RpcService { String NAME = "ScheduleService"; @@ -38,6 +38,11 @@ default String getServiceName() { return NAME; } + /** + * Returns the RPC definitions for the service + * + * @return the RPC definitions + */ @NonNull @Override default Set rpcDefinitions() { @@ -51,6 +56,6 @@ default Set rpcDefinitions() { */ @NonNull static ScheduleService getInstance() { - return ServiceFactory.loadService(ScheduleService.class, ServiceLoader.load(ScheduleService.class)); + return RpcServiceFactory.loadService(ScheduleService.class, ServiceLoader.load(ScheduleService.class)); } } diff --git a/hedera-node/hedera-schedule-service/src/main/java/com/hedera/node/app/service/schedule/ScheduleServiceDefinition.java b/hedera-node/hedera-schedule-service/src/main/java/com/hedera/node/app/service/schedule/ScheduleServiceDefinition.java index a5279ad1fe5e..f8efe9fe33eb 100644 --- a/hedera-node/hedera-schedule-service/src/main/java/com/hedera/node/app/service/schedule/ScheduleServiceDefinition.java +++ b/hedera-node/hedera-schedule-service/src/main/java/com/hedera/node/app/service/schedule/ScheduleServiceDefinition.java @@ -33,8 +33,14 @@ */ @SuppressWarnings("java:S6548") public final class ScheduleServiceDefinition implements RpcServiceDefinition { + /** + * The global singleton ScheduleServiceDefinition INSTANCE. + */ public static final ScheduleServiceDefinition INSTANCE = new ScheduleServiceDefinition(); + /** + * The set of methods supported by the Schedule Service. + */ private static final Set> methods = Set.of( new RpcMethodDefinition<>("createSchedule", Transaction.class, TransactionResponse.class), new RpcMethodDefinition<>("signSchedule", Transaction.class, TransactionResponse.class), @@ -47,12 +53,22 @@ private ScheduleServiceDefinition() { requireNonNull(CommonUtils.class); } + /** + * Returns the base path for the Schedule Service + * + * @return the base path + */ @Override @NonNull public String basePath() { return "proto.ScheduleService"; } + /** + * Returns the set of methods supported by the Schedule Service + * + * @return the methods + */ @Override @NonNull public Set> methods() { diff --git a/hedera-node/hedera-schedule-service/src/main/java/com/hedera/node/app/service/schedule/WritableScheduleStore.java b/hedera-node/hedera-schedule-service/src/main/java/com/hedera/node/app/service/schedule/WritableScheduleStore.java index e5d5430f4be6..2a3c5c3349c6 100644 --- a/hedera-node/hedera-schedule-service/src/main/java/com/hedera/node/app/service/schedule/WritableScheduleStore.java +++ b/hedera-node/hedera-schedule-service/src/main/java/com/hedera/node/app/service/schedule/WritableScheduleStore.java @@ -22,27 +22,45 @@ import edu.umd.cs.findbugs.annotations.Nullable; import java.time.Instant; +/** + * The interface Writable schedule store. + */ public interface WritableScheduleStore extends ReadableScheduleStore { /** * Delete a given schedule from this state. - * Given the ID of a schedule and a consensus time, delete that ID from this state as of the - * consensus time {@link Instant} provided. + * Given the ID of a schedule and a consensus time, delete that ID from + * this state as of the consensus time {@link Instant} provided. + * * @param scheduleToDelete The ID of a schedule to be deleted. * @param consensusTime The current consensus time - * @throws IllegalStateException if the {@link ScheduleID} to be deleted is not present in this state, - * or the ID value has a mismatched realm or shard for this node. + * @return the schedule + * @throws IllegalStateException if the {@link ScheduleID} to be deleted is not present in this state, or the ID + * value has a mismatched realm or shard for this node. */ Schedule delete(@Nullable final ScheduleID scheduleToDelete, @NonNull final Instant consensusTime); + /** + * Given the ID of a schedule, return a mutable reference to the schedule in this state. + * + * @param idToFind The ID to find + * @return the Schedule to modify + */ Schedule getForModify(final ScheduleID idToFind); + /** + * Add a schedule to this state. + * If the schedule already exists it will be replaced. + * + * @param scheduleToAdd The schedule to add + */ void put(Schedule scheduleToAdd); /** * Purges expired schedules from the store. + * * @param firstSecondToExpire The consensus second of the first schedule to expire. - * @param lastSecondToExpire The consensus second of the last schedule to expire. + * @param lastSecondToExpire The consensus second of the last schedule to expire. */ void purgeExpiredSchedulesBetween(long firstSecondToExpire, long lastSecondToExpire); } diff --git a/hedera-node/hedera-schedule-service/src/main/java/module-info.java b/hedera-node/hedera-schedule-service/src/main/java/module-info.java index fa515e4da458..1bc47d168006 100644 --- a/hedera-node/hedera-schedule-service/src/main/java/module-info.java +++ b/hedera-node/hedera-schedule-service/src/main/java/module-info.java @@ -1,8 +1,8 @@ module com.hedera.node.app.service.schedule { - requires com.hedera.node.app.hapi.utils; requires transitive com.hedera.node.app.spi; requires transitive com.hedera.node.hapi; requires transitive com.hedera.pbj.runtime; + requires com.hedera.node.app.hapi.utils; requires static com.github.spotbugs.annotations; exports com.hedera.node.app.service.schedule; diff --git a/hedera-node/hedera-schedule-service/src/test/java/com/hedera/node/app/service/schedule/ScheduleServiceTest.java b/hedera-node/hedera-schedule-service/src/test/java/com/hedera/node/app/service/schedule/ScheduleServiceTest.java index 4f6153f0fdad..ef52cf543f08 100644 --- a/hedera-node/hedera-schedule-service/src/test/java/com/hedera/node/app/service/schedule/ScheduleServiceTest.java +++ b/hedera-node/hedera-schedule-service/src/test/java/com/hedera/node/app/service/schedule/ScheduleServiceTest.java @@ -21,7 +21,9 @@ class ScheduleServiceTest { - private final ScheduleService subject = new ScheduleService() {}; + private final ScheduleService subject = (registry) -> { + // no-op + }; @Test void verifyServiceName() { diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceImpl.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceImpl.java index 0919ccd62a75..1f145df081a5 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceImpl.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/ContractServiceImpl.java @@ -20,7 +20,7 @@ import com.hedera.node.app.service.contract.impl.handlers.ContractHandlers; import com.hedera.node.app.service.contract.impl.schemas.V0490ContractSchema; import com.hedera.node.app.service.contract.impl.schemas.V0500ContractSchema; -import com.hedera.node.app.spi.state.SchemaRegistry; +import com.swirlds.state.spi.SchemaRegistry; import edu.umd.cs.findbugs.annotations.NonNull; /** diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/TransactionModule.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/TransactionModule.java index 18fd9eb05622..3ca3f44bfc94 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/TransactionModule.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/TransactionModule.java @@ -52,7 +52,6 @@ import com.hedera.node.app.service.contract.impl.state.ProxyWorldUpdater; import com.hedera.node.app.service.contract.impl.state.ScopedEvmFrameStateFactory; import com.hedera.node.app.service.file.ReadableFileStore; -import com.hedera.node.app.spi.info.NetworkInfo; import com.hedera.node.app.spi.validation.AttributeValidator; import com.hedera.node.app.spi.validation.ExpiryValidator; import com.hedera.node.app.spi.workflows.ComputeDispatchFeesAsTopLevel; @@ -61,6 +60,7 @@ import com.hedera.node.config.data.ContractsConfig; import com.hedera.node.config.data.HederaConfig; import com.hedera.pbj.runtime.io.buffer.Bytes; +import com.swirlds.state.spi.info.NetworkInfo; import dagger.Binds; import dagger.Module; import dagger.Provides; diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/PrngSystemContract.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/PrngSystemContract.java index 3c4f0af9f0ca..4e590bbe0cca 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/PrngSystemContract.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/PrngSystemContract.java @@ -19,7 +19,6 @@ import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.systemContractGasCalculatorOf; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.asEvmContractId; import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.tuweniToPbjBytes; -import static com.hedera.node.app.service.contract.impl.utils.SystemContractUtils.HTS_PRECOMPILE_MIRROR_ID; import static com.hedera.node.app.service.contract.impl.utils.SystemContractUtils.successResultOfZeroValueTraceable; import static com.hedera.node.app.service.evm.utils.ValidationUtils.validateTrue; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.FAIL_INVALID; @@ -148,9 +147,7 @@ void createFailedRecord( .gasUsed(gasRequirement) .functionParameters(tuweniToPbjBytes(frame.getInputData())) .errorMessage(null) - // (FUTURE) Replace with PRNG contract address, c.f. issue - // https://github.com/hashgraph/hedera-services/issues/10552 - .contractID(HTS_PRECOMPILE_MIRROR_ID) + .contractID(contractID) .senderId(senderId) .gas(frame.getRemainingGas()) .build(); diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java index ed634babaecf..88434b9d9e0a 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/TokenTupleUtils.java @@ -68,22 +68,6 @@ public static Tuple expiryTupleFor(@NonNull final Token token) { Math.max(0, token.autoRenewSeconds())); } - /** - * Returns a tuple of the {@code KeyValue} struct - *
Link - * @param key the key to get the tuple for - * @return Tuple encoding of the KeyValue - */ - @NonNull - public static Tuple keyTupleFor(@NonNull final Key key) { - return Tuple.of( - false, - headlongAddressOf(key.contractIDOrElse(ZERO_CONTRACT_ID)), - key.ed25519OrElse(Bytes.EMPTY).toByteArray(), - key.ecdsaSecp256k1OrElse(Bytes.EMPTY).toByteArray(), - headlongAddressOf(key.delegatableContractIdOrElse(ZERO_CONTRACT_ID))); - } - /** * Returns a tuple containing the response code, fixedFees, fractionalFees and the royaltyFees for the token * diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/tokenkey/TokenKeyCall.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/tokenkey/TokenKeyCall.java index 4c044362526b..188ecff9ed21 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/tokenkey/TokenKeyCall.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/exec/systemcontracts/hts/tokenkey/TokenKeyCall.java @@ -21,8 +21,8 @@ import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.FullResult.revertResult; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.FullResult.successResult; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.common.Call.PricedResult.gasOnly; -import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.TokenTupleUtils.keyTupleFor; import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.tokenkey.TokenKeyTranslator.TOKEN_KEY; +import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.keyTupleFor; import static java.util.Objects.requireNonNull; import com.hedera.hapi.node.base.Key; diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/infra/HevmTransactionFactory.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/infra/HevmTransactionFactory.java index b46208619be2..3f12ee9afabe 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/infra/HevmTransactionFactory.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/infra/HevmTransactionFactory.java @@ -66,7 +66,6 @@ import com.hedera.node.app.service.file.ReadableFileStore; import com.hedera.node.app.service.token.ReadableAccountStore; import com.hedera.node.app.service.token.api.TokenServiceApi; -import com.hedera.node.app.spi.info.NetworkInfo; import com.hedera.node.app.spi.validation.AttributeValidator; import com.hedera.node.app.spi.validation.ExpiryMeta; import com.hedera.node.app.spi.validation.ExpiryValidator; @@ -76,6 +75,7 @@ import com.hedera.node.config.data.LedgerConfig; import com.hedera.node.config.data.StakingConfig; import com.hedera.pbj.runtime.io.buffer.Bytes; +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; diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/schemas/V0490ContractSchema.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/schemas/V0490ContractSchema.java index b60706c5c396..3b3f134f1d99 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/schemas/V0490ContractSchema.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/schemas/V0490ContractSchema.java @@ -28,12 +28,12 @@ import com.hedera.node.app.service.mono.state.virtual.IterableContractValue; import com.hedera.node.app.service.mono.state.virtual.VirtualBlobKey; import com.hedera.node.app.service.mono.state.virtual.VirtualBlobValue; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.StateDefinition; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.common.threading.manager.AdHocThreadManager; import com.swirlds.platform.state.spi.WritableKVStateBase; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.StateDefinition; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.ArrayList; diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/schemas/V0500ContractSchema.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/schemas/V0500ContractSchema.java index 3eb34e9cc1a9..cfa724c0ea31 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/schemas/V0500ContractSchema.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/schemas/V0500ContractSchema.java @@ -25,10 +25,10 @@ import com.hedera.hapi.node.base.SemanticVersion; import com.hedera.hapi.node.state.contract.SlotKey; import com.hedera.hapi.node.state.contract.SlotValue; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; import com.hedera.pbj.runtime.io.buffer.Bytes; +import com.swirlds.state.spi.MigrationContext; import com.swirlds.state.spi.ReadableKVState; +import com.swirlds.state.spi.Schema; import com.swirlds.state.spi.WritableKVState; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/utils/ConversionUtils.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/utils/ConversionUtils.java index ae363115d98e..774ef418cda7 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/utils/ConversionUtils.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/com/hedera/node/app/service/contract/impl/utils/ConversionUtils.java @@ -19,15 +19,18 @@ import static com.hedera.hapi.node.base.ResponseCodeEnum.SUCCESS; import static com.hedera.node.app.service.contract.impl.exec.scope.HederaNativeOperations.MISSING_ENTITY_NUMBER; import static com.hedera.node.app.service.contract.impl.exec.scope.HederaNativeOperations.NON_CANONICAL_REFERENCE_NUMBER; +import static com.hedera.node.app.service.contract.impl.exec.systemcontracts.hts.ReturnTypes.ZERO_CONTRACT_ID; import static com.hedera.node.app.service.contract.impl.exec.utils.FrameUtils.proxyUpdaterFor; import static com.hedera.node.app.service.contract.impl.utils.SynthTxnUtils.hasNonDegenerateAutoRenewAccountId; import static com.hedera.node.app.service.token.AliasUtils.extractEvmAddress; import static com.swirlds.common.utility.CommonUtils.unhex; import static java.util.Objects.requireNonNull; +import com.esaulpaugh.headlong.abi.Tuple; import com.hedera.hapi.node.base.AccountID; import com.hedera.hapi.node.base.ContractID; import com.hedera.hapi.node.base.Duration; +import com.hedera.hapi.node.base.Key; import com.hedera.hapi.node.base.ResponseCodeEnum; import com.hedera.hapi.node.base.TokenID; import com.hedera.hapi.node.contract.ContractCreateTransactionBody; @@ -684,7 +687,13 @@ private static long maybeMissingNumberOf( } } - private static byte[] explicitAddressOf(@NonNull final Account account) { + /** + * Given an account, returns its explicit 20-byte address. + * + * @param account the account + * @return the explicit 20-byte address + */ + public static byte[] explicitAddressOf(@NonNull final Account account) { requireNonNull(account); final var evmAddress = extractEvmAddress(account.alias()); return evmAddress != null @@ -774,4 +783,21 @@ public static long fromTinycentsToTinybars(final ExchangeRate exchangeRate, fina .declineReward(sponsor.declineReward()) .build(); } + + /** + * Returns a tuple of the {@code KeyValue} struct + *
Link + * @param key the key to get the tuple for + * @return Tuple encoding of the KeyValue + */ + @NonNull + public static Tuple keyTupleFor(@NonNull final Key key) { + return Tuple.of( + false, + headlongAddressOf(key.contractIDOrElse(ZERO_CONTRACT_ID)), + key.ed25519OrElse(com.hedera.pbj.runtime.io.buffer.Bytes.EMPTY).toByteArray(), + key.ecdsaSecp256k1OrElse(com.hedera.pbj.runtime.io.buffer.Bytes.EMPTY) + .toByteArray(), + headlongAddressOf(key.delegatableContractIdOrElse(ZERO_CONTRACT_ID))); + } } diff --git a/hedera-node/hedera-smart-contract-service-impl/src/main/java/module-info.java b/hedera-node/hedera-smart-contract-service-impl/src/main/java/module-info.java index ba83fffc4440..7af4571f48f1 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/main/java/module-info.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/main/java/module-info.java @@ -8,9 +8,9 @@ requires transitive com.hedera.node.app.spi; requires transitive com.hedera.node.config; requires transitive com.hedera.node.hapi; - requires transitive com.hedera.pbj.runtime; requires transitive com.swirlds.config.api; requires transitive com.swirlds.state.api; + requires transitive com.hedera.pbj.runtime; requires transitive dagger; requires transitive headlong; requires transitive javax.inject; @@ -19,13 +19,13 @@ requires transitive org.hyperledger.besu.evm; requires transitive tuweni.bytes; requires transitive tuweni.units; + requires com.swirlds.base; + requires com.swirlds.common; + requires com.swirlds.platform.core; requires com.github.benmanes.caffeine; requires com.google.common; requires com.google.protobuf; requires com.hedera.evm; - requires com.swirlds.base; - requires com.swirlds.common; - requires com.swirlds.platform.core; requires org.bouncycastle.provider; requires static com.github.spotbugs.annotations; requires static java.compiler; // javax.annotation.processing.Generated diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/ContractServiceImplTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/ContractServiceImplTest.java index d3ad72f7796b..fec4ebd98737 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/ContractServiceImplTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/ContractServiceImplTest.java @@ -25,8 +25,8 @@ import com.hedera.node.app.service.contract.impl.schemas.V0490ContractSchema; import com.hedera.node.app.service.contract.impl.schemas.V0500ContractSchema; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.SchemaRegistry; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.SchemaRegistry; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/TransactionModuleTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/TransactionModuleTest.java index 65dccfe83fb0..8443cb033266 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/TransactionModuleTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/exec/TransactionModuleTest.java @@ -63,11 +63,11 @@ import com.hedera.node.app.service.contract.impl.test.TestHelpers; import com.hedera.node.app.service.file.ReadableFileStore; import com.hedera.node.app.spi.fees.Fees; -import com.hedera.node.app.spi.info.NetworkInfo; import com.hedera.node.app.spi.validation.AttributeValidator; import com.hedera.node.app.spi.validation.ExpiryValidator; import com.hedera.node.app.spi.workflows.ComputeDispatchFeesAsTopLevel; import com.hedera.node.app.spi.workflows.HandleContext; +import com.swirlds.state.spi.info.NetworkInfo; import java.time.Instant; import java.util.Map; import org.junit.jupiter.api.Test; diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/infra/HevmTransactionFactoryTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/infra/HevmTransactionFactoryTest.java index 59c19fdb42b9..b4d2ad83463f 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/infra/HevmTransactionFactoryTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/infra/HevmTransactionFactoryTest.java @@ -100,13 +100,13 @@ import com.hedera.node.app.service.file.ReadableFileStore; import com.hedera.node.app.service.token.ReadableAccountStore; import com.hedera.node.app.service.token.api.TokenServiceApi; -import com.hedera.node.app.spi.info.NetworkInfo; import com.hedera.node.app.spi.validation.AttributeValidator; import com.hedera.node.app.spi.validation.ExpiryMeta; import com.hedera.node.app.spi.validation.ExpiryValidator; import com.hedera.node.app.spi.workflows.HandleException; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.common.utility.CommonUtils; +import com.swirlds.state.spi.info.NetworkInfo; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.function.Consumer; import org.hyperledger.besu.evm.gascalculator.GasCalculator; diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/records/ContractOperationRecordBuilderTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/records/ContractOperationRecordBuilderTest.java index b83d1869943f..c3a756fe83a9 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/records/ContractOperationRecordBuilderTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/records/ContractOperationRecordBuilderTest.java @@ -37,7 +37,6 @@ import java.util.Collections; import java.util.List; import java.util.Set; -import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; class ContractOperationRecordBuilderTest { @@ -45,7 +44,7 @@ class ContractOperationRecordBuilderTest { void withGasFeeWorksAsExpected() { final var subject = new ContractOperationRecordBuilder() { @Override - public void trackExplicitRewardSituation(@NotNull AccountID accountId) {} + public void trackExplicitRewardSituation(@NonNull AccountID accountId) {} @Override public Set explicitRewardSituationIds() { @@ -61,7 +60,7 @@ public long transactionFee() { return totalFee; } - @NotNull + @NonNull @Override public TransactionBody transactionBody() { return TransactionBody.DEFAULT; diff --git a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/schemas/V0500ContractSchemaTest.java b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/schemas/V0500ContractSchemaTest.java index ef8544751b19..f9d90e68e1b3 100644 --- a/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/schemas/V0500ContractSchemaTest.java +++ b/hedera-node/hedera-smart-contract-service-impl/src/test/java/com/hedera/node/app/service/contract/impl/test/schemas/V0500ContractSchemaTest.java @@ -27,11 +27,11 @@ import com.hedera.hapi.node.state.contract.SlotValue; import com.hedera.node.app.service.contract.impl.schemas.V0500ContractSchema; import com.hedera.node.app.spi.fixtures.state.MapWritableStates; -import com.hedera.node.app.spi.state.MigrationContext; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.platform.test.fixtures.state.MapReadableKVState; import com.swirlds.platform.test.fixtures.state.MapReadableStates; import com.swirlds.platform.test.fixtures.state.MapWritableKVState; +import com.swirlds.state.spi.MigrationContext; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Arrays; import java.util.Comparator; diff --git a/hedera-node/hedera-smart-contract-service/src/main/java/com/hedera/node/app/service/contract/ContractService.java b/hedera-node/hedera-smart-contract-service/src/main/java/com/hedera/node/app/service/contract/ContractService.java index 4db6463e0aae..edfe8e1a6df3 100644 --- a/hedera-node/hedera-smart-contract-service/src/main/java/com/hedera/node/app/service/contract/ContractService.java +++ b/hedera-node/hedera-smart-contract-service/src/main/java/com/hedera/node/app/service/contract/ContractService.java @@ -16,7 +16,7 @@ package com.hedera.node.app.service.contract; -import com.hedera.node.app.spi.Service; +import com.hedera.node.app.spi.RpcService; import com.hedera.pbj.runtime.RpcServiceDefinition; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Set; @@ -26,7 +26,7 @@ * href="https://github.com/hashgraph/hedera-protobufs/blob/main/services/smart_contract_service.proto">Smart * Contract Service. */ -public interface ContractService extends Service { +public interface ContractService extends RpcService { String NAME = "ContractService"; /** diff --git a/hedera-node/hedera-smart-contract-service/src/main/java/module-info.java b/hedera-node/hedera-smart-contract-service/src/main/java/module-info.java index 4d01febf37c9..02ef6cd5bd08 100644 --- a/hedera-node/hedera-smart-contract-service/src/main/java/module-info.java +++ b/hedera-node/hedera-smart-contract-service/src/main/java/module-info.java @@ -3,8 +3,8 @@ uses com.hedera.node.app.service.contract.ContractService; - requires com.hedera.node.hapi; requires transitive com.hedera.node.app.spi; requires transitive com.hedera.pbj.runtime; + requires com.hedera.node.hapi; requires static com.github.spotbugs.annotations; } diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/BlocklistParser.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/BlocklistParser.java index 12b7e300e58e..99a9c15fe7cb 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/BlocklistParser.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/BlocklistParser.java @@ -93,9 +93,9 @@ private static List readPrivateKeyBlocklist(@NonNull final String fileNa * Parses a line from the blocklist resource and returns blocked account info record. * * The line should have the following format: - * , - * where is a hex-encoded private key - * and is a memo for the blocked account + * <private key>,<memo> + * where <private key> is a hex-encoded private key + * and <memo> is a memo for the blocked account * and both values are comma-separated. * * The resulting blocked account info record contains the EVM address derived from the private key, and the memo. diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/TokenServiceImpl.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/TokenServiceImpl.java index 70e2e48b3309..7f5d8349f0a1 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/TokenServiceImpl.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/TokenServiceImpl.java @@ -23,7 +23,7 @@ import com.hedera.node.app.service.token.impl.schemas.SyntheticRecordsGenerator; import com.hedera.node.app.service.token.impl.schemas.V0490TokenSchema; import com.hedera.node.app.service.token.impl.schemas.V0500TokenSchema; -import com.hedera.node.app.spi.state.SchemaRegistry; +import com.swirlds.state.spi.SchemaRegistry; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Collections; import java.util.SortedSet; diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/api/TokenServiceApiImpl.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/api/TokenServiceApiImpl.java index 395852874787..d4fa29aef946 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/api/TokenServiceApiImpl.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/api/TokenServiceApiImpl.java @@ -41,7 +41,6 @@ import com.hedera.node.app.service.token.impl.WritableAccountStore; import com.hedera.node.app.service.token.impl.validators.StakingValidator; import com.hedera.node.app.spi.fees.Fees; -import com.hedera.node.app.spi.info.NetworkInfo; import com.hedera.node.app.spi.metrics.StoreMetricsService; import com.hedera.node.app.spi.validation.EntityType; import com.hedera.node.app.spi.validation.ExpiryValidator; @@ -53,6 +52,7 @@ import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.config.api.Configuration; import com.swirlds.state.spi.WritableStates; +import com.swirlds.state.spi.info.NetworkInfo; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.function.Predicate; diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoUpdateHandler.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoUpdateHandler.java index bca6da819584..08bd5529414a 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoUpdateHandler.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/handlers/CryptoUpdateHandler.java @@ -58,7 +58,6 @@ import com.hedera.node.app.spi.fees.FeeCalculator; import com.hedera.node.app.spi.fees.FeeContext; import com.hedera.node.app.spi.fees.Fees; -import com.hedera.node.app.spi.info.NetworkInfo; import com.hedera.node.app.spi.validation.EntityType; import com.hedera.node.app.spi.validation.ExpiryMeta; import com.hedera.node.app.spi.workflows.HandleContext; @@ -72,6 +71,7 @@ import com.hedera.node.config.data.StakingConfig; import com.hedera.node.config.data.TokensConfig; import com.swirlds.config.api.Configuration; +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.StandardCharsets; diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/StakingInfoManagementSchema.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/StakingInfoManagementSchema.java new file mode 100644 index 000000000000..9fe19396245e --- /dev/null +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/StakingInfoManagementSchema.java @@ -0,0 +1,121 @@ +/* + * 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.service.token.impl.schemas; + +import static java.util.Collections.nCopies; +import static java.util.Objects.requireNonNull; + +import com.hedera.hapi.node.base.SemanticVersion; +import com.hedera.hapi.node.state.token.StakingNodeInfo; +import com.hedera.node.app.service.token.impl.ReadableStakingInfoStoreImpl; +import com.hedera.node.app.service.token.impl.WritableStakingInfoStore; +import com.hedera.node.config.data.LedgerConfig; +import com.hedera.node.config.data.StakingConfig; +import com.swirlds.config.api.Configuration; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.info.NodeInfo; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Defines the schema for managing staking info. + * IMPORTANT: Every TokenSchema version should extend this interface + */ +public class StakingInfoManagementSchema extends Schema { + Logger log = LogManager.getLogger(StakingInfoManagementSchema.class); + + /** + * Create a new instance + * + * @param version The version of this schema + */ + protected StakingInfoManagementSchema(@NonNull final SemanticVersion version) { + super(version); + } + + /** + * Updates in-state staking info to match the address book. + *

    + *
  1. For any node with staking info in state that is no longer in the address book, + * marks it deleted and sets its weight to zero.
  2. + *
  3. For any node in the address book that is not in state, + * initializes its staking info.
  4. + *
  5. Ensures all max stake values reflect the current address book size.
  6. + *
+ * + * @param ctx {@link MigrationContext} for this schema restart operation + */ + @Override + public void restart(@NonNull MigrationContext ctx) { + final var networkInfo = ctx.networkInfo(); + final var newStakingStore = new WritableStakingInfoStore(ctx.newStates()); + // We need to validate and mark any node that are removed during upgrade as deleted. + // Since restart is called in the schema after an upgrade, and we don't want to depend on + // schema version change, validate all the nodeIds from the addressBook in state and mark + // them as deleted if they are not yet deleted in staking info. + if (!ctx.previousStates().isEmpty()) { + final var oldStakingStore = new ReadableStakingInfoStoreImpl(ctx.previousStates()); + oldStakingStore.getAll().stream().sorted().forEach(nodeId -> { + final var stakingInfo = requireNonNull(oldStakingStore.get(nodeId)); + if (!networkInfo.containsNode(nodeId) && !stakingInfo.deleted()) { + newStakingStore.put( + nodeId, + stakingInfo.copyBuilder().weight(0).deleted(true).build()); + log.info("Marked node{} as deleted since it has been removed from the address book", nodeId); + } + }); + } + // Validate if any new nodes are added in addressBook and not in staking info. + // If so, add them to staking info/ with weight 0. Also update maxStake and + // minStake for the new nodes. + completeUpdateFromNewAddressBook(newStakingStore, networkInfo.addressBook(), ctx.configuration()); + } + + private void completeUpdateFromNewAddressBook( + @NonNull final WritableStakingInfoStore store, + @NonNull final List nodeInfos, + @NonNull final Configuration config) { + final var numberOfNodesInAddressBook = nodeInfos.size(); + final long maxStakePerNode = + config.getConfigData(LedgerConfig.class).totalTinyBarFloat() / numberOfNodesInAddressBook; + final var numRewardHistoryStoredPeriods = + config.getConfigData(StakingConfig.class).rewardHistoryNumStoredPeriods(); + for (final var nodeId : nodeInfos) { + final var stakingInfo = store.get(nodeId.nodeId()); + if (stakingInfo != null) { + if (stakingInfo.maxStake() != maxStakePerNode) { + store.put( + nodeId.nodeId(), + stakingInfo.copyBuilder().maxStake(maxStakePerNode).build()); + } + } else { + final var newNodeStakingInfo = StakingNodeInfo.newBuilder() + .nodeNumber(nodeId.nodeId()) + .maxStake(maxStakePerNode) + .minStake(0L) + .rewardSumHistory( + nCopies(numRewardHistoryStoredPeriods + 1, 0L).toArray(Long[]::new)) + .weight(0) + .build(); + store.put(nodeId.nodeId(), newNodeStakingInfo); + } + } + } +} diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/SyntheticRecordsGenerator.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/SyntheticRecordsGenerator.java index e558bbf4ecc0..cc7dc407df14 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/SyntheticRecordsGenerator.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/SyntheticRecordsGenerator.java @@ -25,12 +25,12 @@ import com.hedera.hapi.node.base.Key; import com.hedera.hapi.node.state.token.Account; import com.hedera.node.app.service.token.impl.BlocklistParser; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; import com.hedera.node.config.data.AccountsConfig; import com.hedera.node.config.data.BootstrapConfig; import com.hedera.node.config.data.HederaConfig; import com.hedera.node.config.data.LedgerConfig; import com.swirlds.config.api.Configuration; +import com.swirlds.state.spi.workflows.record.GenesisRecordsBuilder; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.List; import java.util.SortedSet; 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 a3573929f22d..61ee5f0c96e5 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 @@ -56,13 +56,8 @@ import com.hedera.node.app.service.mono.state.virtual.entities.OnDiskTokenRel; import com.hedera.node.app.service.mono.utils.EntityNum; import com.hedera.node.app.service.token.AliasUtils; -import com.hedera.node.app.service.token.impl.ReadableStakingInfoStoreImpl; import com.hedera.node.app.service.token.impl.WritableStakingInfoStore; import com.hedera.node.app.service.token.impl.codec.NetworkingStakingTranslator; -import com.hedera.node.app.spi.info.NodeInfo; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.StateDefinition; import com.hedera.node.config.data.AccountsConfig; import com.hedera.node.config.data.HederaConfig; import com.hedera.node.config.data.LedgerConfig; @@ -73,7 +68,10 @@ import com.swirlds.merkle.map.MerkleMap; import com.swirlds.platform.state.spi.WritableKVStateBase; import com.swirlds.platform.state.spi.WritableSingletonStateBase; +import com.swirlds.state.spi.MigrationContext; +import com.swirlds.state.spi.StateDefinition; import com.swirlds.state.spi.WritableKVState; +import com.swirlds.state.spi.info.NodeInfo; import com.swirlds.virtualmap.VirtualMap; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -94,7 +92,7 @@ /** * Initial mod-service schema for the token service. */ -public class V0490TokenSchema extends Schema { +public class V0490TokenSchema extends StakingInfoManagementSchema { private static final Logger log = LogManager.getLogger(V0490TokenSchema.class); // These need to be big so databases are created at right scale. If they are too small then the on disk hash map @@ -177,44 +175,6 @@ public Set statesToCreate() { StateDefinition.singleton(STAKING_NETWORK_REWARDS_KEY, NetworkStakingRewards.PROTOBUF)); } - /** - * Updates in-state staking info to match the address book. - *
    - *
  1. For any node with staking info in state that is no longer in the address book, - * marks it deleted and sets its weight to zero.
  2. - *
  3. For any node in the address book that is not in state, - * initializes its staking info.
  4. - *
  5. Ensures all max stake values reflect the current address book size.
  6. - *
- * - * @param ctx {@link MigrationContext} for this schema restart operation - */ - @Override - public void restart(@NonNull MigrationContext ctx) { - final var networkInfo = ctx.networkInfo(); - final var newStakingStore = new WritableStakingInfoStore(ctx.newStates()); - // We need to validate and mark any node that are removed during upgrade as deleted. - // Since restart is called in the schema after an upgrade, and we don't want to depend on - // schema version change, validate all the nodeIds from the addressBook in state and mark - // them as deleted if they are not yet deleted in staking info. - if (!ctx.previousStates().isEmpty()) { - final var oldStakingStore = new ReadableStakingInfoStoreImpl(ctx.previousStates()); - oldStakingStore.getAll().stream().sorted().forEach(nodeId -> { - final var stakingInfo = requireNonNull(oldStakingStore.get(nodeId)); - if (!networkInfo.containsNode(nodeId) && !stakingInfo.deleted()) { - newStakingStore.put( - nodeId, - stakingInfo.copyBuilder().weight(0).deleted(true).build()); - log.info("Marked node{} as deleted since it has been removed from the address book", nodeId); - } - }); - } - // Validate if any new nodes are added in addressBook and not in staking info. - // If so, add them to staking info/ with weight 0. Also update maxStake and - // minStake for the new nodes. - completeUpdateFromNewAddressBook(newStakingStore, networkInfo.addressBook(), ctx.configuration()); - } - @Override public void migrate(@NonNull final MigrationContext ctx) { final var isGenesis = ctx.previousVersion() == null; diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/V0500TokenSchema.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/V0500TokenSchema.java index 63ac5b5f1067..e5887d3af4d1 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/V0500TokenSchema.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/schemas/V0500TokenSchema.java @@ -23,9 +23,8 @@ import com.hedera.hapi.node.base.ContractID; import com.hedera.hapi.node.base.SemanticVersion; import com.hedera.hapi.node.state.token.Account; -import com.hedera.node.app.spi.state.MigrationContext; -import com.hedera.node.app.spi.state.Schema; import com.hedera.pbj.runtime.io.buffer.Bytes; +import com.swirlds.state.spi.MigrationContext; import com.swirlds.state.spi.WritableKVState; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.SortedMap; @@ -36,7 +35,7 @@ * A schema that ensures the first contract storage key of each account matches what * is set in the shared migration context at key {@code "V0500_FIRST_STORAGE_KEYS"}. */ -public class V0500TokenSchema extends Schema { +public class V0500TokenSchema extends StakingInfoManagementSchema { private static final Logger log = LogManager.getLogger(V0500TokenSchema.class); private static final String SHARED_VALUES_KEY = "V0500_FIRST_STORAGE_KEYS"; diff --git a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/validators/StakingValidator.java b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/validators/StakingValidator.java index 2248bf22c032..f2bbcb399c42 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/validators/StakingValidator.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/com/hedera/node/app/service/token/impl/validators/StakingValidator.java @@ -24,7 +24,7 @@ import com.hedera.hapi.node.base.AccountID; import com.hedera.node.app.service.token.ReadableAccountStore; -import com.hedera.node.app.spi.info.NetworkInfo; +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; diff --git a/hedera-node/hedera-token-service-impl/src/main/java/module-info.java b/hedera-node/hedera-token-service-impl/src/main/java/module-info.java index fee3aa6a60a0..0e81eec4476b 100644 --- a/hedera-node/hedera-token-service-impl/src/main/java/module-info.java +++ b/hedera-node/hedera-token-service-impl/src/main/java/module-info.java @@ -8,19 +8,19 @@ requires transitive com.hedera.node.app.spi; requires transitive com.hedera.node.config; requires transitive com.hedera.node.hapi; - requires transitive com.hedera.pbj.runtime; requires transitive com.swirlds.config.api; requires transitive com.swirlds.merkle; requires transitive com.swirlds.state.api; requires transitive com.swirlds.virtualmap; + requires transitive com.hedera.pbj.runtime; requires transitive dagger; requires transitive javax.inject; requires com.hedera.node.app.hapi.utils; - requires com.google.common; - requires com.hedera.evm; requires com.swirlds.base; requires com.swirlds.common; requires com.swirlds.platform.core; + requires com.google.common; + requires com.hedera.evm; requires org.apache.commons.lang3; requires org.apache.logging.log4j; requires org.bouncycastle.provider; diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/TokenServiceImplTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/TokenServiceImplTest.java index aa744860edaa..963f3b675c46 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/TokenServiceImplTest.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/TokenServiceImplTest.java @@ -27,8 +27,8 @@ import com.hedera.node.app.service.token.TokenServiceDefinition; import com.hedera.node.app.service.token.impl.schemas.V0490TokenSchema; import com.hedera.node.app.service.token.impl.schemas.V0500TokenSchema; -import com.hedera.node.app.spi.state.Schema; -import com.hedera.node.app.spi.state.SchemaRegistry; +import com.swirlds.state.spi.Schema; +import com.swirlds.state.spi.SchemaRegistry; import java.util.Collections; import java.util.Set; import java.util.SortedSet; diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/schemas/V0490TokenSchemaTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/schemas/V0490TokenSchemaTest.java index 6c9a2d171891..50d016cdda61 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/schemas/V0490TokenSchemaTest.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/schemas/V0490TokenSchemaTest.java @@ -92,11 +92,7 @@ import com.hedera.node.app.spi.fixtures.util.LogCaptureExtension; import com.hedera.node.app.spi.fixtures.util.LoggingSubject; import com.hedera.node.app.spi.fixtures.util.LoggingTarget; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.info.NodeInfo; import com.hedera.node.app.spi.state.EmptyReadableStates; -import com.hedera.node.app.spi.state.StateDefinition; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; import com.hedera.node.app.workflows.handle.record.MigrationContextImpl; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.common.crypto.DigestType; @@ -106,8 +102,12 @@ import com.swirlds.merkledb.MerkleDbTableConfig; import com.swirlds.platform.state.spi.WritableSingletonStateBase; import com.swirlds.platform.test.fixtures.state.MapWritableKVState; +import com.swirlds.state.spi.StateDefinition; import com.swirlds.state.spi.WritableSingletonState; 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.workflows.record.GenesisRecordsBuilder; import com.swirlds.virtualmap.VirtualMap; import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; @@ -134,7 +134,6 @@ @ExtendWith({MockitoExtension.class, LogCaptureExtension.class}) final class V0490TokenSchemaTest { - private static final long BEGINNING_ENTITY_ID = 3000; private static final SemanticVersion VERSION = diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/schemas/V0500TokenSchemaTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/schemas/V0500TokenSchemaTest.java index 11e9e1871af7..28fcaa99f6c1 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/schemas/V0500TokenSchemaTest.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/schemas/V0500TokenSchemaTest.java @@ -26,9 +26,9 @@ import com.hedera.hapi.node.base.ContractID; import com.hedera.hapi.node.state.token.Account; import com.hedera.node.app.spi.fixtures.state.MapWritableStates; -import com.hedera.node.app.spi.state.MigrationContext; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.platform.test.fixtures.state.MapWritableKVState; +import com.swirlds.state.spi.MigrationContext; import java.util.HashMap; import java.util.Map; import java.util.SortedMap; diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/api/TokenServiceApiImplTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/api/TokenServiceApiImplTest.java index 83ad313ea1b0..1d554bd7994a 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/api/TokenServiceApiImplTest.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/api/TokenServiceApiImplTest.java @@ -42,7 +42,6 @@ import com.hedera.node.app.service.token.impl.validators.StakingValidator; import com.hedera.node.app.spi.fees.Fees; import com.hedera.node.app.spi.fixtures.state.MapWritableStates; -import com.hedera.node.app.spi.info.NetworkInfo; import com.hedera.node.app.spi.metrics.StoreMetricsService; import com.hedera.node.config.testfixtures.HederaTestConfigBuilder; import com.hedera.pbj.runtime.io.buffer.Bytes; @@ -52,6 +51,7 @@ import com.swirlds.platform.test.fixtures.state.MapWritableKVState; import com.swirlds.state.spi.WritableKVState; import com.swirlds.state.spi.WritableStates; +import com.swirlds.state.spi.info.NetworkInfo; import java.util.ArrayList; import java.util.List; import java.util.Map; diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/fixtures/FakeCryptoCreateRecordBuilder.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/fixtures/FakeCryptoCreateRecordBuilder.java index 67aa74cca3a1..121af090ad56 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/fixtures/FakeCryptoCreateRecordBuilder.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/fixtures/FakeCryptoCreateRecordBuilder.java @@ -22,7 +22,7 @@ import com.hedera.node.app.service.token.records.CryptoCreateRecordBuilder; import com.hedera.node.app.spi.workflows.record.SingleTransactionRecordBuilder; import com.hedera.pbj.runtime.io.buffer.Bytes; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Fake Crypto Create Record Builder @@ -45,7 +45,7 @@ public CryptoCreateRecordBuilder create() { private long transactionFee; private String memo; - @NotNull + @NonNull @Override public TransactionBody transactionBody() { return TransactionBody.DEFAULT; @@ -56,41 +56,41 @@ public long transactionFee() { return 0; } - @NotNull + @NonNull @Override public ResponseCodeEnum status() { return ResponseCodeEnum.SUCCESS; } - @NotNull + @NonNull @Override - public CryptoCreateRecordBuilder accountID(@NotNull final AccountID accountID) { + public CryptoCreateRecordBuilder accountID(@NonNull final AccountID accountID) { this.accountID = accountID; return this; } @Override - public SingleTransactionRecordBuilder status(@NotNull ResponseCodeEnum status) { + public SingleTransactionRecordBuilder status(@NonNull ResponseCodeEnum status) { return this; } - @NotNull + @NonNull @Override - public CryptoCreateRecordBuilder evmAddress(@NotNull final Bytes evmAddress) { + public CryptoCreateRecordBuilder evmAddress(@NonNull final Bytes evmAddress) { this.evmAddress = evmAddress; return this; } - @NotNull + @NonNull @Override - public CryptoCreateRecordBuilder transactionFee(@NotNull final long transactionFee) { + public CryptoCreateRecordBuilder transactionFee(@NonNull final long transactionFee) { this.transactionFee = transactionFee; return this; } - @NotNull + @NonNull @Override - public CryptoCreateRecordBuilder memo(@NotNull final String memo) { + public CryptoCreateRecordBuilder memo(@NonNull final String memo) { this.memo = memo; return this; } diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/fixtures/FakeCryptoTransferRecordBuilder.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/fixtures/FakeCryptoTransferRecordBuilder.java index 61983b371b86..92c5a8779c16 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/fixtures/FakeCryptoTransferRecordBuilder.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/fixtures/FakeCryptoTransferRecordBuilder.java @@ -26,10 +26,10 @@ import com.hedera.hapi.node.transaction.TransactionBody; import com.hedera.node.app.service.token.records.CryptoTransferRecordBuilder; import com.hedera.node.app.spi.workflows.record.SingleTransactionRecordBuilder; +import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.Arrays; import java.util.List; -import org.jetbrains.annotations.NotNull; /** * Fake Crypto Transfer Record Builder @@ -46,7 +46,7 @@ public FakeCryptoTransferRecordBuilder() {} */ public CryptoTransferRecordBuilder create() { return new CryptoTransferRecordBuilder() { - @NotNull + @NonNull @Override public TransactionBody transactionBody() { return TransactionBody.DEFAULT; @@ -62,7 +62,7 @@ public long transactionFee() { private List assessedCustomFees; @Override - public SingleTransactionRecordBuilder status(@NotNull ResponseCodeEnum status) { + public SingleTransactionRecordBuilder status(@NonNull ResponseCodeEnum status) { return this; } @@ -70,50 +70,50 @@ public SingleTransactionRecordBuilder status(@NotNull ResponseCodeEnum status) { private List automaticTokenAssociations; private ContractFunctionResult contractCallResult; - @NotNull + @NonNull @Override public ResponseCodeEnum status() { return ResponseCodeEnum.SUCCESS; } - @NotNull + @NonNull @Override - public CryptoTransferRecordBuilder transferList(@NotNull final TransferList hbarTransfers) { + public CryptoTransferRecordBuilder transferList(@NonNull final TransferList hbarTransfers) { this.transferList = hbarTransfers; return this; } - @NotNull + @NonNull @Override public CryptoTransferRecordBuilder tokenTransferLists( - @NotNull final List tokenTransferLists) { + @NonNull final List tokenTransferLists) { this.tokenTransferLists = tokenTransferLists; return this; } - @NotNull + @NonNull @Override public CryptoTransferRecordBuilder assessedCustomFees( - @NotNull final List assessedCustomFees) { + @NonNull final List assessedCustomFees) { this.assessedCustomFees = assessedCustomFees; return this; } @Override public CryptoTransferRecordBuilder paidStakingRewards( - @NotNull final List paidStakingRewards) { + @NonNull final List paidStakingRewards) { this.paidStakingRewards = paidStakingRewards; return this; } @Override public CryptoTransferRecordBuilder addAutomaticTokenAssociation( - @NotNull final TokenAssociation tokenAssociation) { + @NonNull final TokenAssociation tokenAssociation) { this.automaticTokenAssociations = Arrays.asList(tokenAssociation); return this; } - @NotNull + @NonNull @Override public CryptoTransferRecordBuilder contractCallResult(@Nullable ContractFunctionResult result) { this.contractCallResult = result; diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/fixtures/FakeNodeStakeUpdateRecordBuilder.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/fixtures/FakeNodeStakeUpdateRecordBuilder.java index 2440c2459f8a..0ed0ee80dc2e 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/fixtures/FakeNodeStakeUpdateRecordBuilder.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/fixtures/FakeNodeStakeUpdateRecordBuilder.java @@ -19,7 +19,7 @@ import com.hedera.hapi.node.base.ResponseCodeEnum; import com.hedera.hapi.node.base.Transaction; import com.hedera.node.app.service.token.records.NodeStakeUpdateRecordBuilder; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; /** * Fake Node Stake Update Record Builder @@ -35,20 +35,20 @@ public NodeStakeUpdateRecordBuilder create() { private Transaction txn; @Override - public NodeStakeUpdateRecordBuilder status(@NotNull ResponseCodeEnum status) { + public NodeStakeUpdateRecordBuilder status(@NonNull ResponseCodeEnum status) { return null; } - @NotNull + @NonNull @Override - public NodeStakeUpdateRecordBuilder transaction(@NotNull final Transaction txn) { + public NodeStakeUpdateRecordBuilder transaction(@NonNull final Transaction txn) { this.txn = txn; return this; } - @NotNull + @NonNull @Override - public NodeStakeUpdateRecordBuilder memo(@NotNull String memo) { + public NodeStakeUpdateRecordBuilder memo(@NonNull String memo) { this.memo = memo; return this; } diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoCreateHandlerTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoCreateHandlerTest.java index 941aad86e074..a25fdd7e39b2 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoCreateHandlerTest.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoCreateHandlerTest.java @@ -81,8 +81,6 @@ import com.hedera.node.app.spi.fees.Fees; import com.hedera.node.app.spi.fixtures.fees.FakeFeeCalculator; import com.hedera.node.app.spi.fixtures.workflows.FakePreHandleContext; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.info.NodeInfo; import com.hedera.node.app.spi.metrics.StoreMetricsService; import com.hedera.node.app.spi.validation.AttributeValidator; import com.hedera.node.app.spi.validation.ExpiryValidator; @@ -94,6 +92,8 @@ import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.common.utility.CommonUtils; import com.swirlds.config.api.Configuration; +import com.swirlds.state.spi.info.NetworkInfo; +import com.swirlds.state.spi.info.NodeInfo; import java.util.function.LongSupplier; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoGetAccountInfoHandlerTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoGetAccountInfoHandlerTest.java index 48965edae510..9ef5fba96548 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoGetAccountInfoHandlerTest.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoGetAccountInfoHandlerTest.java @@ -484,10 +484,8 @@ private void setupStakingRewardsStore() { private void setupConfig(boolean balancesInQueriesEnabled) { final var configBuilder = HederaTestConfigBuilder.create() .withValue("tokens.maxRelsPerInfoQuery", 2) - .withValue("ledger.id", "0x03"); - if (balancesInQueriesEnabled) { - configBuilder.withValue("tokens.balancesInQueries.enabled", true); - } + .withValue("ledger.id", "0x03") + .withValue("tokens.balancesInQueries.enabled", balancesInQueriesEnabled); given(context.configuration()).willReturn(configBuilder.getOrCreateConfig()); } diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoUpdateHandlerTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoUpdateHandlerTest.java index 85e0a1261497..efa20080be34 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoUpdateHandlerTest.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/CryptoUpdateHandlerTest.java @@ -73,8 +73,6 @@ import com.hedera.node.app.spi.fees.FeeContext; import com.hedera.node.app.spi.fees.Fees; import com.hedera.node.app.spi.fixtures.workflows.FakePreHandleContext; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.info.NodeInfo; import com.hedera.node.app.spi.metrics.StoreMetricsService; import com.hedera.node.app.spi.validation.AttributeValidator; import com.hedera.node.app.spi.validation.ExpiryValidator; @@ -87,6 +85,8 @@ import com.hedera.node.config.VersionedConfigImpl; import com.hedera.node.config.testfixtures.HederaTestConfigBuilder; import com.swirlds.config.api.Configuration; +import com.swirlds.state.spi.info.NetworkInfo; +import com.swirlds.state.spi.info.NodeInfo; import java.util.List; import java.util.Map; import java.util.function.LongSupplier; diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenAccountWipeHandlerTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenAccountWipeHandlerTest.java index 491fb98a537c..03d3698c1710 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenAccountWipeHandlerTest.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/handlers/TokenAccountWipeHandlerTest.java @@ -88,7 +88,6 @@ import com.swirlds.config.api.Configuration; import edu.umd.cs.findbugs.annotations.NonNull; import org.assertj.core.api.Assertions; -import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -117,7 +116,7 @@ public void setUp() { private long newTotalSupply; private TokenType tokenType; - @NotNull + @NonNull @Override public TransactionBody transactionBody() { return TransactionBody.DEFAULT; @@ -134,25 +133,25 @@ public long getNewTotalSupply() { } @Override - public SingleTransactionRecordBuilder status(@NotNull ResponseCodeEnum status) { + public SingleTransactionRecordBuilder status(@NonNull ResponseCodeEnum status) { return this; } - @NotNull + @NonNull @Override public TokenAccountWipeRecordBuilder newTotalSupply(final long supply) { newTotalSupply = supply; return this; } - @NotNull + @NonNull @Override public TokenBaseRecordBuilder tokenType(final @NonNull TokenType tokenType) { this.tokenType = tokenType; return this; } - @NotNull + @NonNull @Override public ResponseCodeEnum status() { return OK; diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/schemas/SyntheticRecordsGeneratorTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/schemas/SyntheticRecordsGeneratorTest.java index 5dcae2f0a4bb..d1d90fd36a7f 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/schemas/SyntheticRecordsGeneratorTest.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/schemas/SyntheticRecordsGeneratorTest.java @@ -33,9 +33,9 @@ import com.hedera.hapi.node.state.token.Account; import com.hedera.node.app.service.token.impl.schemas.SyntheticRecordsGenerator; import com.hedera.node.app.service.token.impl.schemas.V0490TokenSchema; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.config.api.Configuration; +import com.swirlds.state.spi.workflows.record.GenesisRecordsBuilder; import java.util.Collections; import java.util.SortedSet; import java.util.TreeSet; diff --git a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/schemas/V0490TokenSchemaTest.java b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/schemas/V0490TokenSchemaTest.java index 4bcf9ec7359d..651ab626cacf 100644 --- a/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/schemas/V0490TokenSchemaTest.java +++ b/hedera-node/hedera-token-service-impl/src/test/java/com/hedera/node/app/service/token/impl/test/schemas/V0490TokenSchemaTest.java @@ -62,10 +62,7 @@ import com.hedera.node.app.service.token.impl.schemas.V0490TokenSchema; import com.hedera.node.app.spi.fixtures.info.FakeNetworkInfo; import com.hedera.node.app.spi.fixtures.state.MapWritableStates; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.info.NodeInfo; import com.hedera.node.app.spi.state.EmptyReadableStates; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; import com.hedera.node.app.workflows.handle.record.MigrationContextImpl; import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.config.api.Configuration; @@ -73,6 +70,9 @@ import com.swirlds.platform.test.fixtures.state.MapWritableKVState; import com.swirlds.state.spi.WritableSingletonState; 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.workflows.record.GenesisRecordsBuilder; import edu.umd.cs.findbugs.annotations.NonNull; import java.time.Instant; import java.util.Collections; diff --git a/hedera-node/hedera-token-service/build.gradle.kts b/hedera-node/hedera-token-service/build.gradle.kts index 679f1828d36a..e9d47794dd53 100644 --- a/hedera-node/hedera-token-service/build.gradle.kts +++ b/hedera-node/hedera-token-service/build.gradle.kts @@ -25,3 +25,8 @@ description = "Hedera Token Service API" // Remove the following line to enable all 'javac' lint checks that we have turned on by default // and then fix the reported issues. tasks.withType().configureEach { options.compilerArgs.add("-Xlint:-exports") } + +testModuleInfo { + requires("org.assertj.core") + requires("org.junit.jupiter.api") +} diff --git a/hedera-node/hedera-token-service/src/main/java/com/hedera/node/app/service/token/TokenService.java b/hedera-node/hedera-token-service/src/main/java/com/hedera/node/app/service/token/TokenService.java index 3202b99d8fc4..8f7b5d13b041 100644 --- a/hedera-node/hedera-token-service/src/main/java/com/hedera/node/app/service/token/TokenService.java +++ b/hedera-node/hedera-token-service/src/main/java/com/hedera/node/app/service/token/TokenService.java @@ -16,8 +16,8 @@ package com.hedera.node.app.service.token; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.ServiceFactory; +import com.hedera.node.app.spi.RpcService; +import com.hedera.node.app.spi.RpcServiceFactory; import com.hedera.pbj.runtime.RpcServiceDefinition; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ServiceLoader; @@ -28,7 +28,7 @@ * href="https://github.com/hashgraph/hedera-protobufs/blob/main/services/token_service.proto">Token * Service. */ -public interface TokenService extends Service { +public interface TokenService extends RpcService { /** * The name of the service */ @@ -53,6 +53,6 @@ default Set rpcDefinitions() { */ @NonNull static TokenService getInstance() { - return ServiceFactory.loadService(TokenService.class, ServiceLoader.load(TokenService.class)); + return RpcServiceFactory.loadService(TokenService.class, ServiceLoader.load(TokenService.class)); } } diff --git a/hedera-node/hedera-token-service/src/main/java/com/hedera/node/app/service/token/api/TokenServiceApi.java b/hedera-node/hedera-token-service/src/main/java/com/hedera/node/app/service/token/api/TokenServiceApi.java index 131bb028b7c4..ccfd026b402d 100644 --- a/hedera-node/hedera-token-service/src/main/java/com/hedera/node/app/service/token/api/TokenServiceApi.java +++ b/hedera-node/hedera-token-service/src/main/java/com/hedera/node/app/service/token/api/TokenServiceApi.java @@ -22,11 +22,11 @@ import com.hedera.hapi.node.token.CryptoTransferTransactionBody; import com.hedera.node.app.service.token.ReadableAccountStore; import com.hedera.node.app.spi.fees.Fees; -import com.hedera.node.app.spi.info.NetworkInfo; import com.hedera.node.app.spi.validation.ExpiryValidator; import com.hedera.node.app.spi.workflows.HandleException; import com.hedera.node.app.spi.workflows.record.DeleteCapableTransactionRecordBuilder; import com.hedera.pbj.runtime.io.buffer.Bytes; +import com.swirlds.state.spi.info.NetworkInfo; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; diff --git a/hedera-node/hedera-token-service/src/main/java/module-info.java b/hedera-node/hedera-token-service/src/main/java/module-info.java index 78dd9ce66018..0cc1b13e8d9d 100644 --- a/hedera-node/hedera-token-service/src/main/java/module-info.java +++ b/hedera-node/hedera-token-service/src/main/java/module-info.java @@ -17,11 +17,12 @@ requires transitive com.hedera.node.app.spi; requires transitive com.hedera.node.hapi; - requires transitive com.hedera.pbj.runtime; requires transitive com.swirlds.config.api; + requires transitive com.swirlds.state.api; + requires transitive com.hedera.pbj.runtime; requires transitive org.apache.logging.log4j; requires com.hedera.node.app.hapi.utils; + requires com.swirlds.common; requires com.github.spotbugs.annotations; requires com.hedera.evm; - requires com.swirlds.common; } diff --git a/hedera-node/hedera-token-service/src/test/java/com/hedera/node/app/service/token/CryptoServiceDefinitionTest.java b/hedera-node/hedera-token-service/src/test/java/com/hedera/node/app/service/token/CryptoServiceDefinitionTest.java new file mode 100644 index 000000000000..20c96e4558f0 --- /dev/null +++ b/hedera-node/hedera-token-service/src/test/java/com/hedera/node/app/service/token/CryptoServiceDefinitionTest.java @@ -0,0 +1,56 @@ +/* + * 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.service.token; + +import com.hedera.hapi.node.base.Transaction; +import com.hedera.hapi.node.transaction.Query; +import com.hedera.hapi.node.transaction.Response; +import com.hedera.hapi.node.transaction.TransactionResponse; +import com.hedera.pbj.runtime.RpcMethodDefinition; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +class CryptoServiceDefinitionTest { + + @Test + void checkBasePath() { + Assertions.assertThat(CryptoServiceDefinition.INSTANCE.basePath()).isEqualTo("proto.CryptoService"); + } + + @Test + void methodsDefined() { + final var methods = CryptoServiceDefinition.INSTANCE.methods(); + Assertions.assertThat(methods) + .containsExactlyInAnyOrder( + new RpcMethodDefinition<>("createAccount", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("updateAccount", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("cryptoTransfer", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("cryptoDelete", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("approveAllowances", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("deleteAllowances", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("addLiveHash", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("deleteLiveHash", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("getLiveHash", Query.class, Response.class), + new RpcMethodDefinition<>("getAccountRecords", Query.class, Response.class), + new RpcMethodDefinition<>("cryptoGetBalance", Query.class, Response.class), + new RpcMethodDefinition<>("getAccountInfo", Query.class, Response.class), + new RpcMethodDefinition<>("getTransactionReceipts", Query.class, Response.class), + new RpcMethodDefinition<>("getFastTransactionRecord", Query.class, Response.class), + new RpcMethodDefinition<>("getTxRecordByTxID", Query.class, Response.class), + new RpcMethodDefinition<>("getStakersByAccountID", Query.class, Response.class)); + } +} diff --git a/hedera-node/hedera-token-service/src/test/java/com/hedera/node/app/service/token/TokenServiceDefinitionTest.java b/hedera-node/hedera-token-service/src/test/java/com/hedera/node/app/service/token/TokenServiceDefinitionTest.java new file mode 100644 index 000000000000..316c3d79203e --- /dev/null +++ b/hedera-node/hedera-token-service/src/test/java/com/hedera/node/app/service/token/TokenServiceDefinitionTest.java @@ -0,0 +1,63 @@ +/* + * 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.service.token; + +import com.hedera.hapi.node.base.Transaction; +import com.hedera.hapi.node.transaction.Query; +import com.hedera.hapi.node.transaction.Response; +import com.hedera.hapi.node.transaction.TransactionResponse; +import com.hedera.pbj.runtime.RpcMethodDefinition; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +class TokenServiceDefinitionTest { + + @Test + void checkBasePath() { + Assertions.assertThat(TokenServiceDefinition.INSTANCE.basePath()).isEqualTo("proto.TokenService"); + } + + @Test + void methodsDefined() { + final var methods = TokenServiceDefinition.INSTANCE.methods(); + Assertions.assertThat(methods) + .containsExactlyInAnyOrder( + new RpcMethodDefinition<>("createToken", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("updateToken", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("mintToken", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("burnToken", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("deleteToken", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("wipeTokenAccount", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("freezeTokenAccount", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("unfreezeTokenAccount", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>( + "grantKycToTokenAccount", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>( + "revokeKycFromTokenAccount", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("associateTokens", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("dissociateTokens", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>( + "updateTokenFeeSchedule", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("getTokenInfo", Query.class, Response.class), + new RpcMethodDefinition<>("getAccountNftInfos", Query.class, Response.class), + new RpcMethodDefinition<>("getTokenNftInfo", Query.class, Response.class), + new RpcMethodDefinition<>("getTokenNftInfos", Query.class, Response.class), + new RpcMethodDefinition<>("pauseToken", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("updateNfts", Transaction.class, TransactionResponse.class), + new RpcMethodDefinition<>("unpauseToken", Transaction.class, TransactionResponse.class)); + } +} diff --git a/hedera-node/hedera-token-service/src/testFixtures/java/module-info.java b/hedera-node/hedera-token-service/src/testFixtures/java/module-info.java index c8dd519b7f83..ec36d8bc3b9a 100644 --- a/hedera-node/hedera-token-service/src/testFixtures/java/module-info.java +++ b/hedera-node/hedera-token-service/src/testFixtures/java/module-info.java @@ -4,7 +4,7 @@ module com.hedera.node.app.service.token.test.fixtures { exports com.hedera.node.app.service.token.fixtures; - requires com.hedera.node.hapi; requires transitive com.hedera.node.app.service.token; + requires com.hedera.node.hapi; requires static com.github.spotbugs.annotations; } diff --git a/hedera-node/hedera-util-service-impl/src/main/java/com/hedera/node/app/service/util/impl/UtilServiceImpl.java b/hedera-node/hedera-util-service-impl/src/main/java/com/hedera/node/app/service/util/impl/UtilServiceImpl.java index c3a8a00b2de0..24f1e23029ab 100644 --- a/hedera-node/hedera-util-service-impl/src/main/java/com/hedera/node/app/service/util/impl/UtilServiceImpl.java +++ b/hedera-node/hedera-util-service-impl/src/main/java/com/hedera/node/app/service/util/impl/UtilServiceImpl.java @@ -17,7 +17,7 @@ package com.hedera.node.app.service.util.impl; import com.hedera.node.app.service.util.UtilService; -import com.hedera.node.app.spi.Service; +import com.hedera.node.app.spi.RpcService; -/** Standard implementation of the {@link UtilService} {@link Service}. */ +/** Standard implementation of the {@link UtilService} {@link RpcService}. */ public final class UtilServiceImpl implements UtilService {} diff --git a/hedera-node/hedera-util-service/build.gradle.kts b/hedera-node/hedera-util-service/build.gradle.kts index 6ddcaee9c195..f773e0272d02 100644 --- a/hedera-node/hedera-util-service/build.gradle.kts +++ b/hedera-node/hedera-util-service/build.gradle.kts @@ -24,3 +24,8 @@ description = "Hedera Util Service API" // Remove the following line to enable all 'javac' lint checks that we have turned on by default // and then fix the reported issues. tasks.withType().configureEach { options.compilerArgs.add("-Xlint:-exports") } + +testModuleInfo { + requires("org.assertj.core") + requires("org.junit.jupiter.api") +} diff --git a/hedera-node/hedera-util-service/src/main/java/com/hedera/node/app/service/util/UtilService.java b/hedera-node/hedera-util-service/src/main/java/com/hedera/node/app/service/util/UtilService.java index 5264728159d3..a712dab65f5a 100644 --- a/hedera-node/hedera-util-service/src/main/java/com/hedera/node/app/service/util/UtilService.java +++ b/hedera-node/hedera-util-service/src/main/java/com/hedera/node/app/service/util/UtilService.java @@ -16,9 +16,10 @@ package com.hedera.node.app.service.util; -import com.hedera.node.app.spi.Service; -import com.hedera.node.app.spi.ServiceFactory; +import com.hedera.node.app.spi.RpcService; +import com.hedera.node.app.spi.RpcServiceFactory; import com.hedera.pbj.runtime.RpcServiceDefinition; +import com.swirlds.state.spi.SchemaRegistry; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ServiceLoader; import java.util.Set; @@ -28,7 +29,7 @@ * href="https://github.com/hashgraph/hedera-protobufs/blob/main/services/util_service.proto">Util * Service. */ -public interface UtilService extends Service { +public interface UtilService extends RpcService { String NAME = "UtilService"; @@ -51,6 +52,11 @@ default Set rpcDefinitions() { */ @NonNull static UtilService getInstance() { - return ServiceFactory.loadService(UtilService.class, ServiceLoader.load(UtilService.class)); + return RpcServiceFactory.loadService(UtilService.class, ServiceLoader.load(UtilService.class)); + } + + @Override + default void registerSchemas(@NonNull SchemaRegistry registry) { + // no schemas to register } } diff --git a/hedera-node/hedera-util-service/src/main/java/module-info.java b/hedera-node/hedera-util-service/src/main/java/module-info.java index 48d2cd50dcad..ef489a6d493f 100644 --- a/hedera-node/hedera-util-service/src/main/java/module-info.java +++ b/hedera-node/hedera-util-service/src/main/java/module-info.java @@ -3,8 +3,9 @@ uses com.hedera.node.app.service.util.UtilService; - requires com.hedera.node.hapi; requires transitive com.hedera.node.app.spi; + requires transitive com.swirlds.state.api; requires transitive com.hedera.pbj.runtime; + requires com.hedera.node.hapi; requires static com.github.spotbugs.annotations; } diff --git a/hedera-node/hedera-util-service/src/test/java/com/hedera/node/app/service/util/UtilServiceDefinitionTest.java b/hedera-node/hedera-util-service/src/test/java/com/hedera/node/app/service/util/UtilServiceDefinitionTest.java new file mode 100644 index 000000000000..cdd726767579 --- /dev/null +++ b/hedera-node/hedera-util-service/src/test/java/com/hedera/node/app/service/util/UtilServiceDefinitionTest.java @@ -0,0 +1,39 @@ +/* + * 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.service.util; + +import com.hedera.hapi.node.base.Transaction; +import com.hedera.hapi.node.transaction.TransactionResponse; +import com.hedera.pbj.runtime.RpcMethodDefinition; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +class UtilServiceDefinitionTest { + + @Test + void checkBasePath() { + Assertions.assertThat(UtilServiceDefinition.INSTANCE.basePath()).isEqualTo("proto.UtilService"); + } + + @Test + void methodsDefined() { + final var methods = UtilServiceDefinition.INSTANCE.methods(); + Assertions.assertThat(methods) + .containsExactlyInAnyOrder( + new RpcMethodDefinition<>("prng", Transaction.class, TransactionResponse.class)); + } +} diff --git a/hedera-node/test-clients/src/itest/java/ConcurrentSuites.java b/hedera-node/test-clients/src/itest/java/ConcurrentSuites.java index 0ed4d2b07040..24ff66bcd527 100644 --- a/hedera-node/test-clients/src/itest/java/ConcurrentSuites.java +++ b/hedera-node/test-clients/src/itest/java/ConcurrentSuites.java @@ -59,7 +59,6 @@ import com.hedera.services.bdd.suites.contract.precompile.TokenAndTypeCheckSuite; import com.hedera.services.bdd.suites.contract.precompile.TokenExpiryInfoSuite; import com.hedera.services.bdd.suites.contract.precompile.TokenInfoHTSSuite; -import com.hedera.services.bdd.suites.contract.precompile.TokenUpdatePrecompileSuite; import com.hedera.services.bdd.suites.contract.precompile.WipeTokenAccountPrecompileSuite; import com.hedera.services.bdd.suites.contract.records.LogsSuite; import com.hedera.services.bdd.suites.contract.records.RecordsSuite; @@ -157,7 +156,6 @@ static Supplier[] all() { TokenAndTypeCheckSuite::new, TokenExpiryInfoSuite::new, TokenInfoHTSSuite::new, - TokenUpdatePrecompileSuite::new, WipeTokenAccountPrecompileSuite::new, ContractMintHTSV2SecurityModelSuite::new, AssociatePrecompileV2SecurityModelSuite::new, @@ -204,8 +202,6 @@ static Supplier[] ethereumSuites() { TokenAndTypeCheckSuite::new, TokenExpiryInfoSuite::new, TokenInfoHTSSuite::new, - TokenUpdatePrecompileSuite::new, - WipeTokenAccountPrecompileSuite::new, // contract opcodes CreateOperationSuite::new, GlobalPropertiesSuite::new, diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/SpecOperation.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/SpecOperation.java new file mode 100644 index 000000000000..6b5c00d2b700 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/SpecOperation.java @@ -0,0 +1,47 @@ +/* + * 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.services.bdd; + +import com.hedera.services.bdd.spec.HapiSpec; +import com.hederahashgraph.api.proto.java.HederaFunctionality; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.Optional; +import java.util.Set; + +/** + * Defines the interface for a single operation in a {@link HapiSpec}. + */ +public interface SpecOperation { + /** + * Execute the operation for the given {@link HapiSpec}, returning an optional that + * contains any failure (include assertion {@link Error}s) that were thrown. + * + * @param spec the {@link HapiSpec} to execute the operation for + * @return an optional containing any failure that was thrown + */ + Optional execFor(@NonNull HapiSpec spec); + + /** + * Returns whether the operation should be skipped when auto-scheduling is enabled. + * + * @param beingAutoScheduled the set of {@link HederaFunctionality} that are being auto-scheduled + * @return whether the operation should be skipped when auto-scheduling is enabled + */ + default boolean shouldSkipWhenAutoScheduling(@NonNull Set beingAutoScheduled) { + return false; + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/extensions/SpecEntityExtension.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/extensions/SpecEntityExtension.java new file mode 100644 index 000000000000..e05c2fcb5f98 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/extensions/SpecEntityExtension.java @@ -0,0 +1,189 @@ +/* + * 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.services.bdd.junit.extensions; + +import static java.lang.reflect.Modifier.isStatic; +import static org.junit.platform.commons.support.AnnotationSupport.findAnnotatedFields; + +import com.hedera.services.bdd.spec.dsl.SpecEntity; +import com.hedera.services.bdd.spec.dsl.annotations.AccountSpec; +import com.hedera.services.bdd.spec.dsl.annotations.ContractSpec; +import com.hedera.services.bdd.spec.dsl.annotations.FungibleTokenSpec; +import com.hedera.services.bdd.spec.dsl.annotations.KeySpec; +import com.hedera.services.bdd.spec.dsl.annotations.NonFungibleTokenSpec; +import com.hedera.services.bdd.spec.dsl.entities.SpecAccount; +import com.hedera.services.bdd.spec.dsl.entities.SpecContract; +import com.hedera.services.bdd.spec.dsl.entities.SpecFungibleToken; +import com.hedera.services.bdd.spec.dsl.entities.SpecKey; +import com.hedera.services.bdd.spec.dsl.entities.SpecNonFungibleToken; +import com.hedera.services.bdd.spec.dsl.entities.SpecToken; +import com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.lang.reflect.Field; +import java.util.EnumSet; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; + +public class SpecEntityExtension implements ParameterResolver, BeforeAllCallback { + @Override + public boolean supportsParameter( + @NonNull final ParameterContext parameterContext, @NonNull final ExtensionContext extensionContext) { + return SpecEntity.class.isAssignableFrom(parameterContext.getParameter().getType()); + } + + @Override + public Object resolveParameter( + @NonNull final ParameterContext parameterContext, @NonNull final ExtensionContext extensionContext) + throws ParameterResolutionException { + final var parameter = parameterContext.getParameter(); + final var entityType = parameterContext.getParameter().getType(); + if (entityType == SpecAccount.class) { + return accountFrom( + parameter.isAnnotationPresent(AccountSpec.class) + ? parameter.getAnnotation(AccountSpec.class) + : null, + parameter.getName()); + } else if (entityType == SpecContract.class) { + if (!parameter.isAnnotationPresent(ContractSpec.class)) { + throw new IllegalArgumentException("Missing @ContractSpec annotation"); + } + return contractFrom(parameter.getAnnotation(ContractSpec.class)); + } else if (entityType == SpecFungibleToken.class) { + if (!parameter.isAnnotationPresent(FungibleTokenSpec.class)) { + throw new IllegalArgumentException("Missing @FungibleTokenSpec annotation"); + } + return fungibleTokenFrom(parameter.getAnnotation(FungibleTokenSpec.class), parameter.getName()); + } else if (entityType == SpecNonFungibleToken.class) { + if (!parameter.isAnnotationPresent(NonFungibleTokenSpec.class)) { + throw new IllegalArgumentException("Missing @NonFungibleTokenSpec annotation"); + } + return nonFungibleTokenFrom(parameter.getAnnotation(NonFungibleTokenSpec.class), parameter.getName()); + } else if (entityType == SpecKey.class) { + return keyFrom( + parameter.isAnnotationPresent(KeySpec.class) ? parameter.getAnnotation(KeySpec.class) : null, + parameter.getName()); + } else { + throw new ParameterResolutionException("Unsupported entity type " + entityType); + } + } + + @Override + public void beforeAll(@NonNull final ExtensionContext context) throws Exception { + // Inject spec contracts into static fields annotated with @ContractSpec + for (final var field : findAnnotatedFields( + context.getRequiredTestClass(), ContractSpec.class, staticFieldSelector(SpecContract.class))) { + final var contract = contractFrom(field.getAnnotation(ContractSpec.class)); + injectValueIntoField(field, contract); + } + + // Inject spec fungible tokens into static fields annotated with @FungibleTokenSpec + for (final var field : findAnnotatedFields( + context.getRequiredTestClass(), + FungibleTokenSpec.class, + staticFieldSelector(SpecFungibleToken.class))) { + final var token = fungibleTokenFrom(field.getAnnotation(FungibleTokenSpec.class), field.getName()); + injectValueIntoField(field, token); + } + + // Inject spec non-fungible tokens into static fields annotated with @NonFungibleTokenSpec + for (final var field : findAnnotatedFields( + context.getRequiredTestClass(), + NonFungibleTokenSpec.class, + staticFieldSelector(SpecNonFungibleToken.class))) { + final var token = nonFungibleTokenFrom(field.getAnnotation(NonFungibleTokenSpec.class), field.getName()); + injectValueIntoField(field, token); + } + + // Inject spec accounts into static fields annotated with @AccountSpec + for (final var field : findAnnotatedFields( + context.getRequiredTestClass(), AccountSpec.class, staticFieldSelector(SpecAccount.class))) { + final var account = accountFrom(field.getAnnotation(AccountSpec.class), field.getName()); + injectValueIntoField(field, account); + } + + // Inject spec keys into static fields annotated with @KeySpec + for (final var field : findAnnotatedFields( + context.getRequiredTestClass(), KeySpec.class, staticFieldSelector(SpecKey.class))) { + final var key = keyFrom(field.getAnnotation(KeySpec.class), field.getName()); + injectValueIntoField(field, key); + } + } + + private SpecContract contractFrom(@NonNull final ContractSpec annotation) { + final var name = annotation.name().isBlank() ? annotation.contract() : annotation.name(); + return new SpecContract(name, annotation.contract(), annotation.creationGas()); + } + + private SpecNonFungibleToken nonFungibleTokenFrom( + @NonNull final NonFungibleTokenSpec annotation, @NonNull final String defaultName) { + final var token = new SpecNonFungibleToken(annotation.name().isBlank() ? defaultName : annotation.name()); + customizeToken(token, annotation.keys(), annotation.useAutoRenewAccount()); + token.setNumPreMints(annotation.numPreMints()); + return token; + } + + private SpecFungibleToken fungibleTokenFrom( + @NonNull final FungibleTokenSpec annotation, @NonNull final String defaultName) { + final var token = new SpecFungibleToken(annotation.name().isBlank() ? defaultName : annotation.name()); + customizeToken(token, annotation.keys(), annotation.useAutoRenewAccount()); + return token; + } + + private SpecAccount accountFrom(@Nullable final AccountSpec annotation, @NonNull final String defaultName) { + final var name = Optional.ofNullable(annotation) + .map(AccountSpec::name) + .filter(n -> !n.isBlank()) + .orElse(defaultName); + return new SpecAccount(name); + } + + private SpecKey keyFrom(@Nullable final KeySpec annotation, @NonNull final String defaultName) { + final var name = Optional.ofNullable(annotation) + .map(KeySpec::name) + .filter(n -> !n.isBlank()) + .orElse(defaultName); + final var type = Optional.ofNullable(annotation).map(KeySpec::type).orElse(SpecKey.Type.ED25519); + return new SpecKey(name, type); + } + + private void customizeToken( + @NonNull final SpecToken token, @NonNull final SpecTokenKey[] keys, final boolean useAutoRenewAccount) { + token.setKeys(EnumSet.copyOf(List.of(keys))); + if (useAutoRenewAccount) { + token.useAutoRenewAccount(); + } + } + + private void injectValueIntoField(@NonNull final Field field, @NonNull final Object value) + throws IllegalAccessException { + final var accessible = field.isAccessible(); + field.setAccessible(true); + field.set(null, value); + field.setAccessible(accessible); + } + + private static Predicate staticFieldSelector(@NonNull final Class type) { + return f -> isStatic(f.getModifiers()) && f.getType() == type; + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/support/SpecManager.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/support/SpecManager.java index 6901f5aae819..2af951c3dcb6 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/support/SpecManager.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/junit/support/SpecManager.java @@ -19,6 +19,7 @@ import static com.hedera.services.bdd.spec.HapiSpec.doTargetSpec; import static java.util.Objects.requireNonNull; +import com.hedera.services.bdd.SpecOperation; import com.hedera.services.bdd.junit.hedera.HederaNetwork; import com.hedera.services.bdd.spec.HapiSpec; import com.hedera.services.bdd.spec.HapiSpecOperation; @@ -38,7 +39,7 @@ public SpecManager(@NonNull final HederaNetwork targetNetwork) { this.targetNetwork = requireNonNull(targetNetwork); } - public void setup(@NonNull final HapiSpecOperation... ops) throws Throwable { + public void setup(@NonNull final SpecOperation... ops) throws Throwable { final var spec = new HapiSpec(SPEC_NAME, ops); doTargetSpec(spec, targetNetwork); spec.setSpecStateObserver(sharedStates::add); diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpec.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpec.java index 08f34f8de9e1..7e0848b19201 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpec.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpec.java @@ -38,6 +38,7 @@ import static com.hedera.services.bdd.spec.utilops.UtilStateChange.createEthereumAccountForSpec; import static com.hedera.services.bdd.spec.utilops.UtilStateChange.isEthereumAccountCreatedForSpec; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.blockingOrder; +import static com.hedera.services.bdd.spec.utilops.UtilVerbs.convertHapiCallsToEthereumCalls; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.noOp; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.overridingAllOf; import static com.hedera.services.bdd.spec.utilops.UtilVerbs.remembering; @@ -62,6 +63,7 @@ import com.google.common.io.ByteSource; import com.google.common.io.CharSink; import com.google.common.io.Files; +import com.hedera.services.bdd.SpecOperation; import com.hedera.services.bdd.junit.hedera.HederaNetwork; import com.hedera.services.bdd.junit.hedera.HederaNode; import com.hedera.services.bdd.junit.hedera.NodeSelector; @@ -79,7 +81,6 @@ import com.hedera.services.bdd.spec.transactions.HapiTxnOp; import com.hedera.services.bdd.spec.transactions.TxnFactory; import com.hedera.services.bdd.spec.utilops.UtilOp; -import com.hedera.services.bdd.spec.utilops.UtilVerbs; import com.hedera.services.bdd.spec.utilops.records.AutoSnapshotModeOp; import com.hedera.services.bdd.spec.utilops.records.SnapshotMatchMode; import com.hedera.services.bdd.spec.utilops.records.SnapshotModeOp; @@ -228,9 +229,9 @@ public boolean isOnlySpecToRunInSuite() { HapiSpecSetup hapiSetup; HapiClients hapiClients; HapiSpecRegistry hapiRegistry; - HapiSpecOperation[] given; - HapiSpecOperation[] when; - HapiSpecOperation[] then; + SpecOperation[] given; + SpecOperation[] when; + SpecOperation[] then; AtomicInteger adhoc = new AtomicInteger(0); AtomicBoolean allOpsSubmitted = new AtomicBoolean(false); ThreadPoolExecutor finalizingExecutor; @@ -382,7 +383,7 @@ public SidecarWatcher getSidecarWatcher() { return targetNetworkOrThrow().getRequiredNode(selector).getRecordStreamPath(); } - public HederaNetwork targetNetworkOrThrow() { + public @NonNull HederaNetwork targetNetworkOrThrow() { return requireNonNull(targetNetwork); } @@ -414,7 +415,7 @@ public void run() { return; } - List ops = new ArrayList<>(); + List ops = new ArrayList<>(); if (!suitePrefix.endsWith(ETH_SUFFIX)) { ops.addAll(Stream.of(given, when, then).flatMap(Arrays::stream).toList()); @@ -423,7 +424,7 @@ public void run() { ops.addAll(createEthereumAccountForSpec(this)); } final var adminKey = this.registry().getKey(DEFAULT_CONTRACT_SENDER); - ops.addAll(UtilVerbs.convertHapiCallsToEthereumCalls( + ops.addAll(convertHapiCallsToEthereumCalls( Stream.of(given, when, then).flatMap(Arrays::stream).toList(), SECP_256K1_SOURCE_KEY, adminKey, @@ -564,14 +565,14 @@ private void tearDown() { } @SuppressWarnings("java:S2629") - private void exec(List ops) { + private void exec(@NonNull List ops) { if (status == ERROR) { log.warn("'{}' failed to initialize, being skipped!", name); return; } if (hapiSetup.requiresPersistentEntities()) { - List creationOps = entities.requiredCreations(); + List creationOps = entities.requiredCreations(); if (!creationOps.isEmpty()) { if (!quietMode) { log.info("Inserting {} required creations to establish persistent entities.", creationOps.size()); @@ -609,7 +610,7 @@ private void exec(List ops) { ops = new ArrayList<>(ops); ops.add(0, (UtilOp) snapshotOp); } - for (HapiSpecOperation op : ops) { + for (var op : ops) { if (!autoScheduled.isEmpty() && op.shouldSkipWhenAutoScheduling(autoScheduled)) { continue; } @@ -629,7 +630,7 @@ private void exec(List ops) { if (quietMode) { turnLoggingOff(op); } - Optional error = op.execFor(this); + final var error = op.execFor(this); Failure asyncFailure = null; if (error.isPresent() || (asyncFailure = finishingError.get().orElse(null)) != null) { status = FAILED; @@ -722,7 +723,7 @@ private void exec(List ops) { * @param txn the transaction to auto-schedule * @return the sequence of operations that auto-schedules the transaction */ - private HapiSpecOperation autoScheduledSequenceFor(final HapiTxnOp txn) { + private SpecOperation autoScheduledSequenceFor(final HapiTxnOp txn) { // For the signatures to have the expected semantics, we must // incorporate any signature control overrides into this spec final var sigControlOverrides = txn.setKeyControlOverrides(this); @@ -747,7 +748,7 @@ private HapiSpecOperation autoScheduledSequenceFor(final HapiTxnOp txn) { final var indices = createAndSignIndicesGiven(numKeys, numSignTxns); // One slot for the ScheduleCreate, one for each ScheduleSign, // one for the GetScheduleInfo, and one for the GetTxnRecord - final var orderedOps = new HapiSpecOperation[1 + numSignTxns + 2]; + final var orderedOps = new SpecOperation[1 + numSignTxns + 2]; final var num = NEXT_AUTO_SCHEDULE_NUM.getAndIncrement(); final var schedule = "autoScheduled" + num; final var creation = "autoScheduleCreation" + num; @@ -1101,20 +1102,34 @@ public static Def.Setup onlyHapiSpec( new HapiSpec(name, true, setup, given, when, then, propertiesToPreserve, snapshotMatchModes)))); } - public static Stream hapiTest(@NonNull final HapiSpecOperation... ops) { + public static Stream hapiTest(@NonNull final SpecOperation... ops) { return Stream.of(DynamicTest.dynamicTest( AS_WRITTEN_DISPLAY_NAME, targeted(new HapiSpec( SPEC_NAME.get(), false, HapiSpecSetup.setupFrom(HapiSpecSetup.getDefaultPropertySource()), - new HapiSpecOperation[0], - new HapiSpecOperation[0], + new SpecOperation[0], + new SpecOperation[0], ops, List.of(), new SnapshotMatchMode[0])))); } + public static DynamicTest namedHapiTest(String name, @NonNull final SpecOperation... ops) { + return DynamicTest.dynamicTest( + name, + targeted(new HapiSpec( + name, + false, + HapiSpecSetup.setupFrom(HapiSpecSetup.getDefaultPropertySource()), + new SpecOperation[0], + new SpecOperation[0], + ops, + List.of(), + new SnapshotMatchMode[0]))); + } + private static HapiSpec targeted(@NonNull final HapiSpec spec) { final var targetNetwork = TARGET_NETWORK.get(); if (targetNetwork != null) { @@ -1135,13 +1150,13 @@ public static void doTargetSpec(@NonNull final HapiSpec spec, @NonNull final Hed spec.addOverrideProperties(Map.of("nodes", specNodes)); } - public HapiSpec(String name, HapiSpecOperation[] ops) { + public HapiSpec(String name, SpecOperation[] ops) { this( name, false, setupFrom(HapiSpecSetup.getDefaultPropertySource()), - new HapiSpecOperation[0], - new HapiSpecOperation[0], + new SpecOperation[0], + new SpecOperation[0], ops, List.of(), new SnapshotMatchMode[0]); @@ -1153,9 +1168,9 @@ public HapiSpec( String name, boolean onlySpecToRunInSuite, HapiSpecSetup hapiSetup, - HapiSpecOperation[] given, - HapiSpecOperation[] when, - HapiSpecOperation[] then, + SpecOperation[] given, + SpecOperation[] when, + SpecOperation[] then, List propertiesToPreserve, SnapshotMatchMode[] snapshotMatchModes) { this.snapshotMatchModes = snapshotMatchModes; @@ -1190,17 +1205,17 @@ interface Setup { @FunctionalInterface interface Given { - When given(HapiSpecOperation... ops); + When given(SpecOperation... ops); } @FunctionalInterface interface When { - Then when(HapiSpecOperation... ops); + Then when(SpecOperation... ops); } @FunctionalInterface interface Then { - Stream then(HapiSpecOperation... ops); + Stream then(SpecOperation... ops); } } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpecOperation.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpecOperation.java index a33b54fab799..07e3f118f5ec 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpecOperation.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/HapiSpecOperation.java @@ -34,6 +34,7 @@ import com.hedera.node.app.hapi.utils.fee.FeeBuilder; import com.hedera.node.app.hapi.utils.fee.FileFeeBuilder; import com.hedera.node.app.hapi.utils.fee.SmartContractFeeBuilder; +import com.hedera.services.bdd.SpecOperation; import com.hedera.services.bdd.spec.keys.ControlForKey; import com.hedera.services.bdd.spec.keys.SigControl; import com.hedera.services.bdd.spec.keys.SigMapGenerator; @@ -72,7 +73,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -public abstract class HapiSpecOperation { +public abstract class HapiSpecOperation implements SpecOperation { private static final Logger log = LogManager.getLogger(HapiSpecOperation.class); protected static final FileOpsUsage fileOpsUsage = new FileOpsUsage(); diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/EvmAddressableEntity.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/EvmAddressableEntity.java new file mode 100644 index 000000000000..3af47e72e53a --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/EvmAddressableEntity.java @@ -0,0 +1,34 @@ +/* + * 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.services.bdd.spec.dsl; + +import com.esaulpaugh.headlong.abi.Address; +import com.hedera.services.bdd.junit.hedera.HederaNetwork; +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Defines an entity that can be addressed within the EVM. + */ +public interface EvmAddressableEntity { + /** + * Returns the address of the entity on the given network. + * + * @param network the network + * @return the address + */ + Address addressOn(@NonNull HederaNetwork network); +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/SpecEntity.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/SpecEntity.java new file mode 100644 index 000000000000..c1ac1a2bfa4d --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/SpecEntity.java @@ -0,0 +1,95 @@ +/* + * 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.services.bdd.spec.dsl; + +import static java.util.Collections.emptyList; +import static java.util.Objects.requireNonNull; + +import com.hedera.services.bdd.junit.HapiTest; +import com.hedera.services.bdd.junit.hedera.HederaNetwork; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.infrastructure.HapiSpecRegistry; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.List; +import java.util.Optional; + +/** + * Encapsulates logic to manage a Hedera entity (e.g., account, file, or contract) in the + * context of a {@link HapiSpec}. + * + *

Hides details of interacting with the {@link HapiSpecRegistry} from {@link HapiTest} + * authors, who can simply inject POJO entities into their test classes and methods and + * call methods on those objects to perform HAPI operations scoped to the managed entities. + */ +public interface SpecEntity { + /** + * Returns the {@link HapiSpecRegistry} name of this entity. + * + * @return the name of the entity + */ + String name(); + + /** + * Returns the logic to register with a spec all information about this entity on a + * given {@link HederaNetwork}, if it exists there. + * + * @param network the network to get the registrar for + * @return the entity's registrar for the network, or {@code null} if it does not exist there + */ + @Nullable + SpecEntityRegistrar registrarFor(@NonNull HederaNetwork network); + + /** + * If this entity is already created on the spec's target {@link HederaNetwork}, registers + * its record (e.g., entity id, controlling key, and memo) with the given {@link HapiSpec}'s + * registry. + * + *

If the entity is not already created on the network, blocks until it is created using + * the given spec; then registers its record. + * + * @param spec the spec to use to create the entity if it is not already created + */ + default void registerOrCreateWith(@NonNull final HapiSpec spec) { + requireNonNull(spec); + Optional.ofNullable(registrarFor(spec.targetNetworkOrThrow())) + .orElseGet(() -> createWith(spec)) + .registerWith(spec); + } + + /** + * Creates this entity with the given {@link HapiSpec}, returning the registrar + * for the spec's target network. + * + * @param spec the spec to use to create the entity + */ + SpecEntityRegistrar createWith(@NonNull HapiSpec spec); + + /** + * Locks the entity's model, preventing further modification. + */ + void lock(); + + /** + * Returns a list of entities that must be created before this entity can be created. + * + * @return the prerequisite entities + */ + default List prerequisiteEntities() { + return emptyList(); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/SpecEntityRegistrar.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/SpecEntityRegistrar.java new file mode 100644 index 000000000000..87e8a432ca5a --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/SpecEntityRegistrar.java @@ -0,0 +1,28 @@ +/* + * 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.services.bdd.spec.dsl; + +import com.hedera.services.bdd.spec.HapiSpec; +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Defines a type able to register information (e.g. entity ids, keys, etc.) with a {@link HapiSpec}. + */ +@FunctionalInterface +public interface SpecEntityRegistrar { + void registerWith(@NonNull HapiSpec spec); +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/annotations/AccountSpec.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/annotations/AccountSpec.java new file mode 100644 index 000000000000..422894143427 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/annotations/AccountSpec.java @@ -0,0 +1,39 @@ +/* + * 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.services.bdd.spec.dsl.annotations; + +import com.hedera.services.bdd.junit.extensions.SpecEntityExtension; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * Describes a {@link com.hedera.services.bdd.spec.dsl.entities.SpecAccount}. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.PARAMETER}) +@ExtendWith(SpecEntityExtension.class) +public @interface AccountSpec { + /** + * If set, a different {@link com.hedera.services.bdd.spec.HapiSpec} name to use for the account. + * + * @return the spec name of the account + */ + String name() default ""; +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/annotations/ContractSpec.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/annotations/ContractSpec.java new file mode 100644 index 000000000000..576c09157f77 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/annotations/ContractSpec.java @@ -0,0 +1,53 @@ +/* + * 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.services.bdd.spec.dsl.annotations; + +import com.hedera.services.bdd.junit.extensions.SpecEntityExtension; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * Describes a {@link com.hedera.services.bdd.spec.dsl.entities.SpecContract}. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.PARAMETER}) +@ExtendWith(SpecEntityExtension.class) +public @interface ContractSpec { + /** + * If set, a different {@link com.hedera.services.bdd.spec.HapiSpec} name to use for the contract. + * + * @return the spec name of the contract + */ + String name() default ""; + + /** + * The name of the contract; must refer to a contract in the classpath resources. + * + * @return the name of the contract + */ + String contract(); + + /** + * The amount of gas to use when creating the contract. + * + * @return the amount of gas to use + */ + long creationGas() default 100_000L; +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/annotations/FungibleTokenSpec.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/annotations/FungibleTokenSpec.java new file mode 100644 index 000000000000..3999261641ad --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/annotations/FungibleTokenSpec.java @@ -0,0 +1,57 @@ +/* + * 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.services.bdd.spec.dsl.annotations; + +import static com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey.ADMIN_KEY; +import static com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey.SUPPLY_KEY; + +import com.hedera.services.bdd.junit.extensions.SpecEntityExtension; +import com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * Describes a {@link com.hedera.services.bdd.spec.dsl.entities.SpecToken}. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.PARAMETER}) +@ExtendWith(SpecEntityExtension.class) +public @interface FungibleTokenSpec { + /** + * If set, a {@link com.hedera.services.bdd.spec.HapiSpec} name to use for the token. + * + * @return the spec name of the contract + */ + String name() default ""; + + /** + * The types of keys to associate with the token. + * + * @return the types of keys to associate with the token + */ + SpecTokenKey[] keys() default {ADMIN_KEY, SUPPLY_KEY}; + + /** + * Whether to use an auto-renew account for the token. + * + * @return whether to use an auto-renew account for the token + */ + boolean useAutoRenewAccount() default false; +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/annotations/KeySpec.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/annotations/KeySpec.java new file mode 100644 index 000000000000..cc13ae09cf59 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/annotations/KeySpec.java @@ -0,0 +1,44 @@ +/* + * 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.services.bdd.spec.dsl.annotations; + +import com.hedera.services.bdd.junit.extensions.SpecEntityExtension; +import com.hedera.services.bdd.spec.dsl.entities.SpecKey; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.extension.ExtendWith; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.PARAMETER}) +@ExtendWith(SpecEntityExtension.class) +public @interface KeySpec { + /** + * If set, a {@link com.hedera.services.bdd.spec.HapiSpec} name to use for the key. + * + * @return the spec name of the key + */ + String name() default ""; + + /** + * The type of key to create. + * + * @return the type of key to create + */ + SpecKey.Type type() default SpecKey.Type.ED25519; +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/annotations/NonFungibleTokenSpec.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/annotations/NonFungibleTokenSpec.java new file mode 100644 index 000000000000..23c5e7db10a5 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/annotations/NonFungibleTokenSpec.java @@ -0,0 +1,64 @@ +/* + * 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.services.bdd.spec.dsl.annotations; + +import static com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey.ADMIN_KEY; +import static com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey.SUPPLY_KEY; + +import com.hedera.services.bdd.junit.extensions.SpecEntityExtension; +import com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.junit.jupiter.api.extension.ExtendWith; + +/** + * Describes a {@link com.hedera.services.bdd.spec.dsl.entities.SpecToken}. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.PARAMETER}) +@ExtendWith(SpecEntityExtension.class) +public @interface NonFungibleTokenSpec { + /** + * If set, a {@link com.hedera.services.bdd.spec.HapiSpec} name to use for the token. + * + * @return the spec name of the contract + */ + String name() default ""; + + /** + * The types of keys to associate with the token. + * + * @return the types of keys to associate with the token + */ + SpecTokenKey[] keys() default {ADMIN_KEY, SUPPLY_KEY}; + + /** + * The number of pre-mints to perform. + * + * @return the number of pre-mints to perform + */ + int numPreMints() default 0; + + /** + * Whether to use an auto-renew account for the token. + * + * @return whether to use an auto-renew account for the token + */ + boolean useAutoRenewAccount() default false; +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/AbstractSpecEntity.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/AbstractSpecEntity.java new file mode 100644 index 000000000000..d649f0da08d0 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/AbstractSpecEntity.java @@ -0,0 +1,200 @@ +/* + * 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.services.bdd.spec.dsl.entities; + +import static com.hedera.services.bdd.spec.utilops.CustomSpecAssert.allRunFor; +import static com.hedera.services.bdd.spec.utilops.CustomSpecAssert.handleExec; +import static java.util.Collections.emptyList; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.CompletableFuture.supplyAsync; + +import com.hedera.services.bdd.SpecOperation; +import com.hedera.services.bdd.junit.hedera.HederaNetwork; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.SpecEntity; +import com.hedera.services.bdd.spec.dsl.SpecEntityRegistrar; +import com.hedera.services.bdd.spec.dsl.operations.deferred.DoWithModelOperation; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; +import org.jetbrains.annotations.Nullable; + +/** + * Provides implementation support for a {@link SpecEntity}. + * + * @param the type of the operation used to create the entity + * @param the type of the entity's model + */ +public abstract class AbstractSpecEntity implements SpecEntity { + /** + * Represents the attempt to create an entity. + * + * @param op the operation used for the attempt + * @param model the model object to be created + * @param the type of the operation + * @param the type of the model + */ + protected record Creation(S op, R model) {} + + /** + * Represents the result of a successful entity creation. + * + * @param model the model object created + * @param registrar the registrar for the entity + * @param the type of the model + */ + protected record Result(R model, SpecEntityRegistrar registrar) {} + + /** + * Indicates whether the entity is locked. + */ + private volatile boolean locked = false; + /** + * The name of the entity. + */ + protected final String name; + /** + * A map of network names to atomic references to futures that will contain the results + * of entity creations on the target networks. + */ + private final Map>>> results = new ConcurrentHashMap<>(); + + protected AbstractSpecEntity(@NonNull final String name) { + this.name = requireNonNull(name); + } + + /** + * {@inheritDoc} + */ + @Override + public void lock() { + locked = true; + } + + /** + * {@inheritDoc} + */ + @Override + public String name() { + return name; + } + + /** + * {@inheritDoc} + */ + @Override + public @Nullable SpecEntityRegistrar registrarFor(@NonNull final HederaNetwork network) { + final var maybeResultFuture = results.computeIfAbsent(network.name(), k -> new AtomicReference<>()) + .get(); + return (maybeResultFuture != null) ? maybeResultFuture.join().registrar() : null; + } + + /** + * {@inheritDoc} + */ + @Override + public SpecEntityRegistrar createWith(@NonNull final HapiSpec spec) { + final var network = spec.targetNetworkOrThrow(); + final var resultFutureRef = requireNonNull(results.get(network.name())); + final CompletableFuture> resultFuture = supplyAsync(() -> { + final var creation = newCreation(spec); + // Throws if the creation op fails + handleExec(spec, creation.op()); + final var result = resultForSuccessful(creation, spec); + allRunFor(spec, postSuccessOps()); + return result; + }); + if (!resultFutureRef.compareAndSet(null, resultFuture)) { + resultFuture.cancel(true); + } + return resultFutureRef.get().join().registrar(); + } + + /** + * Throws an exception if the entity is locked. + */ + protected void throwIfLocked() { + if (locked) { + throw new IllegalStateException("Entity '" + name + "' is locked"); + } + } + + /** + * Retrieves the model corresponding to a network. + * + * @param network the network + * @return the model + */ + public M modelOrThrow(@NonNull final HederaNetwork network) { + requireNonNull(network); + return requireNonNull(requireNonNull(results.get(network.name())).get()) + .join() + .model(); + } + + /** + * Executes a deferred operation on the model. + * + * @param function the function that computes the operation + * @return the deferred operation + */ + public DoWithModelOperation doWith(@NonNull final Function function) { + return new DoWithModelOperation<>(this, function); + } + + /** + * Supplies a {@link SpecOperation} that can be used to create the entity within the given spec. + * + * @param spec the spec + * @return a new creation attempt + */ + protected abstract Creation newCreation(@NonNull HapiSpec spec); + + /** + * Computes the registrar corresponding to a successful creation operation. + * + * @param creation the successful creation + * @param spec the spec used to create the entity + * @return the registrar + */ + protected abstract Result resultForSuccessful(@NonNull Creation creation, @NonNull HapiSpec spec); + + /** + * Supplies a list of operations to be executed after a successful creation. + * + * @return the list of operations + */ + protected List postSuccessOps() { + return emptyList(); + } + + /** + * Replaces the result of a creation operation on a network. + * + * @param network the network + * @param result the result + */ + protected void replaceResult(@NonNull final HederaNetwork network, @NonNull final Result result) { + requireNonNull(network); + requireNonNull(result); + requireNonNull(results.get(network.name())).set(CompletableFuture.completedFuture(result)); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecAccount.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecAccount.java new file mode 100644 index 000000000000..be7e7d98d032 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecAccount.java @@ -0,0 +1,189 @@ +/* + * 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.services.bdd.spec.dsl.entities; + +import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.headlongAddressOf; +import static com.hedera.node.app.service.mono.pbj.PbjConverter.fromPbj; +import static com.hedera.node.app.service.mono.pbj.PbjConverter.toPbj; +import static com.hedera.services.bdd.spec.dsl.utils.DslUtils.atMostOnce; +import static com.hedera.services.bdd.spec.transactions.TxnVerbs.cryptoCreate; +import static java.util.Objects.requireNonNull; + +import com.esaulpaugh.headlong.abi.Address; +import com.hedera.hapi.node.base.AccountID; +import com.hedera.hapi.node.base.Key; +import com.hedera.hapi.node.state.token.Account; +import com.hedera.services.bdd.junit.hedera.HederaNetwork; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.EvmAddressableEntity; +import com.hedera.services.bdd.spec.dsl.SpecEntity; +import com.hedera.services.bdd.spec.dsl.operations.queries.GetBalanceOperation; +import com.hedera.services.bdd.spec.dsl.operations.transactions.AssociateTokensOperation; +import com.hedera.services.bdd.spec.dsl.operations.transactions.AuthorizeContractOperation; +import com.hedera.services.bdd.spec.dsl.operations.transactions.DeleteAccountOperation; +import com.hedera.services.bdd.spec.dsl.utils.KeyMetadata; +import com.hedera.services.bdd.spec.transactions.crypto.HapiCryptoCreate; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.List; + +/** + * Represents a Hedera account that may exist on one or more target networks and be + * registered with more than one {@link HapiSpec} if desired. + */ +public class SpecAccount extends AbstractSpecEntity + implements SpecEntity, EvmAddressableEntity { + private final Account.Builder builder = Account.newBuilder(); + + public SpecAccount(@NonNull final String name) { + super(name); + } + + /** + * Returns a builder for the model account to be created, or throws if the entity is locked. + * + * @return the builder + */ + public Account.Builder builder() { + throwIfLocked(); + return builder; + } + + /** + * Returns an operation to delete the account, transferring its balance to the given beneficiary. + * + * @param beneficiary the beneficiary + * @return the operation + */ + public DeleteAccountOperation deleteWithTransfer(@NonNull final SpecAccount beneficiary) { + requireNonNull(beneficiary); + return new DeleteAccountOperation(this, beneficiary); + } + + /** + * Returns an operation to associate the account with the given tokens. + * + * @param tokens the tokens to associate + * @return the operation + */ + public AssociateTokensOperation associateTokens(@NonNull final SpecToken... tokens) { + requireNonNull(tokens); + return new AssociateTokensOperation(this, List.of(tokens)); + } + + /** + * Returns an operation to authorize the given contract to act on behalf of this account. + * + * @param contract the contract to authorize + * @return the operation + */ + public AuthorizeContractOperation authorizeContract(@NonNull final SpecContract contract) { + requireNonNull(contract); + return new AuthorizeContractOperation(this, contract); + } + + /** + * Returns an operation to get the balance of the account. + * + * @return the operation + */ + public GetBalanceOperation getBalance() { + return new GetBalanceOperation(this); + } + + /** + * Gets the account model for the given network, or throws if it doesn't exist. + * + * @param network the network + * @return the account model + */ + public Account accountOrThrow(@NonNull final HederaNetwork network) { + return modelOrThrow(network); + } + + /** + * {@inheritDoc} + */ + @Override + public Address addressOn(@NonNull final HederaNetwork network) { + requireNonNull(network); + final var networkAccount = accountOrThrow(network); + return headlongAddressOf(networkAccount); + } + + @Override + public String toString() { + return "SpecAccount{" + "name='" + name + '\'' + '}'; + } + + /** + * Updates the key of the account on the given network. + * + * @param key the new key + * @param spec the active spec targeting the network + */ + public void updateKeyFrom(@NonNull final Key key, @NonNull final HapiSpec spec) { + requireNonNull(key); + requireNonNull(spec); + final var network = spec.targetNetworkOrThrow(); + final var networkAccount = accountOrThrow(network); + replaceResult(network, resultFor(networkAccount.copyBuilder().key(key).build(), spec)); + } + + /** + * {@inheritDoc} + */ + @Override + protected Creation newCreation(@NonNull final HapiSpec spec) { + final var model = builder.build(); + final var op = cryptoCreate(name).balance(model.tinybarBalance()); + return new Creation<>(op, model); + } + + /** + * {@inheritDoc} + */ + @Override + protected Result resultForSuccessful( + @NonNull final Creation creation, @NonNull final HapiSpec spec) { + return resultFor( + creation.model() + .copyBuilder() + .accountId(AccountID.newBuilder() + .accountNum(creation.op().numOfCreatedAccount()) + .build()) + .key(toPbj(creation.op().getKey())) + .build(), + spec); + } + + private Result resultFor(@NonNull final Account model, @NonNull final HapiSpec spec) { + final var keyMetadata = KeyMetadata.from(fromPbj(model.keyOrThrow()), spec); + return new Result<>(model, atMostOnce(siblingSpec -> { + keyMetadata.registerAs(name, siblingSpec); + siblingSpec + .registry() + .saveAccountId( + name, + com.hederahashgraph.api.proto.java.AccountID.newBuilder() + .setAccountNum(model.accountIdOrThrow().accountNumOrThrow()) + .build()); + if (model.receiverSigRequired()) { + siblingSpec.registry().saveSigRequirement(name, Boolean.TRUE); + } + })); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecContract.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecContract.java new file mode 100644 index 000000000000..92305695e3ef --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecContract.java @@ -0,0 +1,205 @@ +/* + * 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.services.bdd.spec.dsl.entities; + +import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.headlongAddressOf; +import static com.hedera.services.bdd.spec.dsl.utils.DslUtils.PBJ_IMMUTABILITY_SENTINEL_KEY; +import static com.hedera.services.bdd.spec.dsl.utils.DslUtils.PROTO_IMMUTABILITY_SENTINEL_KEY; +import static com.hedera.services.bdd.spec.dsl.utils.DslUtils.atMostOnce; +import static com.hedera.services.bdd.spec.dsl.utils.DslUtils.withSubstitutedTypes; +import static com.hedera.services.bdd.spec.transactions.TxnVerbs.contractCreate; +import static com.hedera.services.bdd.spec.utilops.UtilVerbs.blockingOrder; +import static com.hedera.services.bdd.spec.utilops.UtilVerbs.createLargeFile; +import static com.hedera.services.bdd.suites.HapiSuite.GENESIS; +import static com.hedera.services.bdd.suites.contract.Utils.getInitcodeOf; +import static java.util.Objects.requireNonNull; + +import com.esaulpaugh.headlong.abi.Address; +import com.hedera.hapi.node.base.AccountID; +import com.hedera.hapi.node.state.token.Account; +import com.hedera.services.bdd.SpecOperation; +import com.hedera.services.bdd.junit.hedera.HederaNetwork; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.EvmAddressableEntity; +import com.hedera.services.bdd.spec.dsl.SpecEntity; +import com.hedera.services.bdd.spec.dsl.operations.queries.GetContractInfoOperation; +import com.hedera.services.bdd.spec.dsl.operations.queries.StaticCallContractOperation; +import com.hedera.services.bdd.spec.dsl.operations.transactions.CallContractOperation; +import com.hedera.services.bdd.spec.dsl.utils.KeyMetadata; +import com.hedera.services.bdd.spec.transactions.contract.HapiContractCreate; +import com.hedera.services.bdd.spec.utilops.grouping.InBlockingOrder; +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Represents a Hedera account that may exist on one or more target networks and be + * registered with more than one {@link HapiSpec} if desired. + */ +public class SpecContract extends AbstractSpecEntity + implements SpecEntity, EvmAddressableEntity { + private static final int MAX_INLINE_INITCODE_SIZE = 4096; + + private final long creationGas; + private final String contractName; + private final Account.Builder builder = Account.newBuilder(); + /** + * The constructor arguments for the contract's creation call; if the arguments are + * not constant values, must be set imperatively within the HapiTest context instead + * of via @ContractSpec annotation attribute. + */ + private Object[] constructorArgs = new Object[0]; + + public SpecContract(@NonNull final String name, @NonNull final String contractName, final long creationGas) { + super(name); + this.creationGas = creationGas; + this.contractName = requireNonNull(contractName); + } + + /** + * Returns a builder for the model account to be created, or throws if the entity is locked. + * + * @return the builder + */ + public Account.Builder builder() { + throwIfLocked(); + return builder; + } + + /** + * {@inheritDoc} + */ + @Override + public Address addressOn(@NonNull final HederaNetwork network) { + requireNonNull(network); + final var networkContract = contractOrThrow(network); + return headlongAddressOf(networkContract); + } + + /** + * Returns an operation that retrieves the contract information. + * + * @return the operation + */ + public GetContractInfoOperation getInfo() { + return new GetContractInfoOperation(this); + } + + /** + * Returns an operation that calls a function on the contract. + * + * @param function the function name + * @param parameters the function parameters + * @return the operation + */ + public CallContractOperation call(@NonNull final String function, @NonNull final Object... parameters) { + return new CallContractOperation(this, function, parameters); + } + + /** + * Returns an operation that static calls a function on the contract. + * + * @param function the function name + * @param parameters the function parameters + * @return the operation + */ + public StaticCallContractOperation staticCall(@NonNull final String function, @NonNull final Object... parameters) { + return new StaticCallContractOperation(this, function, parameters); + } + + /** + * Sets the constructor arguments for the contract's creation call. + * + * @param args the arguments + */ + public void setConstructorArgs(@NonNull final Object... args) { + this.constructorArgs = args; + } + + /** + * Gets the contract model for the given network, or throws if it doesn't exist. + * + * @param network the network + * @return the contract model + */ + public Account contractOrThrow(@NonNull final HederaNetwork network) { + return modelOrThrow(network); + } + + /** + * {@inheritDoc} + */ + @Override + protected Creation newCreation(@NonNull final HapiSpec spec) { + final var model = builder.build(); + final var initcode = getInitcodeOf(contractName); + final SpecOperation op; + constructorArgs = withSubstitutedTypes(spec.targetNetworkOrThrow(), constructorArgs); + if (initcode.size() < MAX_INLINE_INITCODE_SIZE) { + op = contractCreate(name, constructorArgs).inlineInitCode(initcode); + } else { + op = blockingOrder( + createLargeFile(GENESIS, contractName, initcode), + contractCreate(name, constructorArgs).gas(creationGas).bytecode(contractName)); + } + return new Creation<>(op, model); + } + + /** + * {@inheritDoc} + */ + @Override + protected Result resultForSuccessful( + @NonNull Creation creation, @NonNull HapiSpec spec) { + final HapiContractCreate contractCreate; + if (creation.op() instanceof HapiContractCreate inlineCreate) { + contractCreate = inlineCreate; + } else { + contractCreate = (HapiContractCreate) ((InBlockingOrder) creation.op()).last(); + } + + final var newContractNum = contractCreate.numOfCreatedContractOrThrow(); + final var maybeKeyMetadata = contractCreate.getAdminKey().map(key -> KeyMetadata.from(key, spec)); + return new Result<>( + creation.model() + .copyBuilder() + .smartContract(true) + .accountId(AccountID.newBuilder() + .accountNum(newContractNum) + .build()) + .key(maybeKeyMetadata.map(KeyMetadata::pbjKey).orElse(PBJ_IMMUTABILITY_SENTINEL_KEY)) + .build(), + atMostOnce(siblingSpec -> { + maybeKeyMetadata.ifPresentOrElse( + keyMetadata -> keyMetadata.registerAs(name, siblingSpec), + () -> siblingSpec.registry().saveKey(name, PROTO_IMMUTABILITY_SENTINEL_KEY)); + siblingSpec + .registry() + .saveAccountId( + name, + com.hederahashgraph.api.proto.java.AccountID.newBuilder() + .setAccountNum(newContractNum) + .build()); + siblingSpec + .registry() + .saveContractId( + name, + com.hederahashgraph.api.proto.java.ContractID.newBuilder() + .setContractNum(newContractNum) + .build()); + siblingSpec.registry().saveContractInfo(name, contractCreate.infoOfCreatedContractOrThrow()); + })); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecFungibleToken.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecFungibleToken.java new file mode 100644 index 000000000000..8ec638975218 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecFungibleToken.java @@ -0,0 +1,31 @@ +/* + * 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.services.bdd.spec.dsl.entities; + +import static com.hedera.hapi.node.base.TokenType.FUNGIBLE_COMMON; + +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Represents a fungible token that may exist on one or more target networks and be + * registered with more than one {@link com.hedera.services.bdd.spec.HapiSpec} if desired. + */ +public class SpecFungibleToken extends SpecToken { + public SpecFungibleToken(@NonNull final String name) { + super(name, FUNGIBLE_COMMON); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecKey.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecKey.java new file mode 100644 index 000000000000..7d0792065e4e --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecKey.java @@ -0,0 +1,90 @@ +/* + * 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.services.bdd.spec.dsl.entities; + +import static com.hedera.node.app.service.mono.pbj.PbjConverter.toPbj; +import static com.hedera.services.bdd.spec.dsl.utils.DslUtils.atMostOnce; +import static com.hedera.services.bdd.spec.keys.DefaultKeyGen.DEFAULT_KEY_GEN; +import static com.hedera.services.bdd.spec.keys.SigControl.ED25519_ON; +import static com.hedera.services.bdd.spec.keys.SigControl.SECP256K1_ON; +import static com.hedera.services.bdd.spec.utilops.UtilVerbs.noOp; +import static java.util.Objects.requireNonNull; + +import com.hedera.hapi.node.base.Key; +import com.hedera.services.bdd.junit.hedera.HederaNetwork; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.utils.KeyMetadata; +import com.hedera.services.bdd.spec.utilops.NoOp; +import edu.umd.cs.findbugs.annotations.NonNull; + +public class SpecKey extends AbstractSpecEntity { + public enum Type { + ED25519, + SECP_256K1 + } + + private final Type type; + + public SpecKey(@NonNull String name, @NonNull final Type type) { + super(name); + this.type = requireNonNull(type); + } + + /** + * Returns the raw bytes of the key for the given network. + * + * @param network the network + * @return the raw bytes + */ + public byte[] asEncodedOn(@NonNull final HederaNetwork network) { + final var key = keyOrThrow(network); + return switch (type) { + case ED25519 -> key.ed25519OrThrow().toByteArray(); + case SECP_256K1 -> key.ecdsaSecp256k1OrThrow().toByteArray(); + }; + } + + /** + * Gets the key model for the given network, or throws if it doesn't exist. + * + * @param network the network + * @return the key model + */ + public Key keyOrThrow(@NonNull final HederaNetwork network) { + return modelOrThrow(network); + } + + @Override + protected Creation newCreation(@NonNull final HapiSpec spec) { + final var key = spec.keys() + .generateSubjectTo( + spec, + switch (type) { + case ED25519 -> ED25519_ON; + case SECP_256K1 -> SECP256K1_ON; + }, + DEFAULT_KEY_GEN); + return new Creation<>(noOp(), toPbj(key)); + } + + @Override + protected Result resultForSuccessful( + @NonNull final Creation creation, @NonNull final HapiSpec spec) { + final var keyMetadata = KeyMetadata.from(creation.model(), spec); + return new Result<>(keyMetadata.pbjKey(), atMostOnce(siblingSpec -> keyMetadata.registerAs(name, siblingSpec))); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecNft.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecNft.java new file mode 100644 index 000000000000..75c5bfe20841 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecNft.java @@ -0,0 +1,95 @@ +/* + * 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.services.bdd.spec.dsl.entities; + +import static com.hedera.node.app.service.mono.pbj.PbjConverter.toPbj; +import static com.hedera.services.bdd.spec.queries.QueryVerbs.getTokenNftInfo; + +import com.hedera.hapi.node.base.NftID; +import com.hedera.hapi.node.state.token.Nft; +import com.hedera.pbj.runtime.io.buffer.Bytes; +import com.hedera.services.bdd.junit.hedera.HederaNetwork; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.SpecEntity; +import com.hedera.services.bdd.spec.dsl.operations.assertions.AssertNftOwnerOperation; +import com.hedera.services.bdd.spec.queries.token.HapiGetTokenNftInfo; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.List; +import java.util.Objects; + +/** + * Represents a Hedera NFT correlated to a {@link SpecNonFungibleToken}. + */ +public class SpecNft extends AbstractSpecEntity implements SpecEntity { + private final SpecNonFungibleToken token; + private final long serialNo; + + public SpecNft(@NonNull final SpecNonFungibleToken token, final long serialNo) { + super(token.name() + "#" + serialNo); + this.token = Objects.requireNonNull(token); + this.serialNo = serialNo; + } + + @Override + public List prerequisiteEntities() { + return List.of(token); + } + + /** + * Returns an operation asserting that this NFT is owned by the given account. + * + * @param owner the account + * @return the operation + */ + public AssertNftOwnerOperation assertOwnerIs(@NonNull final SpecAccount owner) { + return new AssertNftOwnerOperation(this, owner); + } + + /** + * Returns the NFT model for the given network. + * + * @param network the network + * @return the model + */ + public Nft nftOrThrow(@NonNull final HederaNetwork network) { + return modelOrThrow(network); + } + + @Override + protected Creation newCreation(@NonNull final HapiSpec spec) { + final var nftId = + new NftID(token.tokenOrThrow(spec.targetNetworkOrThrow()).tokenIdOrThrow(), serialNo); + return new Creation<>( + getTokenNftInfo(token.name(), serialNo), + Nft.newBuilder().nftId(nftId).build()); + } + + @Override + protected Result resultForSuccessful( + @NonNull final Creation creation, @NonNull final HapiSpec spec) { + final var info = creation.op().getResponse().getTokenGetNftInfo().getNft(); + return new Result<>( + creation.model() + .copyBuilder() + .metadata(Bytes.wrap(info.getMetadata().toByteArray())) + .mintTime(toPbj(info.getCreationTime())) + .ownerId(toPbj(info.getAccountID())) + .spenderId(toPbj(info.getSpenderId())) + .build(), + siblingSpec -> {}); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecNonFungibleToken.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecNonFungibleToken.java new file mode 100644 index 000000000000..a263d074baeb --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecNonFungibleToken.java @@ -0,0 +1,75 @@ +/* + * 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.services.bdd.spec.dsl.entities; + +import static com.hedera.hapi.node.base.TokenType.NON_FUNGIBLE_UNIQUE; +import static com.hedera.services.bdd.spec.transactions.TxnVerbs.mintToken; +import static java.util.Collections.emptyList; + +import com.google.protobuf.ByteString; +import com.hedera.services.bdd.SpecOperation; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.List; +import java.util.stream.IntStream; + +/** + * Represents a non-fungible token that may exist on one or more target networks and be + * registered with more than one {@link com.hedera.services.bdd.spec.HapiSpec} if desired. + */ +public class SpecNonFungibleToken extends SpecToken { + private int numPreMints = 0; + + public SpecNonFungibleToken(@NonNull String name) { + super(name, NON_FUNGIBLE_UNIQUE); + } + + /** + * Returns a representation of the requested serial number for this token. + * + * @param serialNo the serial number + * @return the representation + */ + public SpecNft serialNo(final long serialNo) { + return new SpecNft(this, serialNo); + } + + /** + * Sets the number of pre-mints to perform. + * + * @param numPreMints the number of pre-mints to perform + */ + public void setNumPreMints(final int numPreMints) { + if (numPreMints > 10) { + throw new IllegalArgumentException("Cannot pre-mint more than 10 NFTs"); + } + this.numPreMints = numPreMints; + } + + /** + * {@inheritDoc} + */ + @Override + protected List postSuccessOps() { + return numPreMints > 0 ? List.of(mintToken(name, snMetadata(numPreMints))) : emptyList(); + } + + private List snMetadata(final int n) { + return IntStream.range(0, n) + .mapToObj(i -> ByteString.copyFromUtf8("SN#" + (i + 1))) + .toList(); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecToken.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecToken.java new file mode 100644 index 000000000000..f8370dc15f93 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecToken.java @@ -0,0 +1,273 @@ +/* + * 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.services.bdd.spec.dsl.entities; + +import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.headlongAddressOf; +import static com.hedera.node.app.service.mono.pbj.PbjConverter.toPbj; +import static com.hedera.services.bdd.spec.dsl.utils.DslUtils.atMostOnce; +import static com.hedera.services.bdd.spec.keys.KeyFactory.KeyType.SIMPLE; +import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenCreate; +import static java.util.Objects.requireNonNull; + +import com.esaulpaugh.headlong.abi.Address; +import com.hedera.hapi.node.base.TokenID; +import com.hedera.hapi.node.base.TokenType; +import com.hedera.hapi.node.state.token.Token; +import com.hedera.services.bdd.junit.hedera.HederaNetwork; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.EvmAddressableEntity; +import com.hedera.services.bdd.spec.dsl.SpecEntity; +import com.hedera.services.bdd.spec.dsl.operations.queries.GetTokenInfoOperation; +import com.hedera.services.bdd.spec.dsl.operations.transactions.AuthorizeContractOperation; +import com.hedera.services.bdd.spec.transactions.token.HapiTokenCreate; +import com.hederahashgraph.api.proto.java.ContractID; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Represents a Hedera token that may exist on one or more target networks and be + * registered with more than one {@link HapiSpec} if desired. + */ +public class SpecToken extends AbstractSpecEntity implements SpecEntity, EvmAddressableEntity { + public static final String DEFAULT_TREASURY_NAME_SUFFIX = "Treasury"; + public static final String DEFAULT_AUTO_RENEW_ACCOUNT_NAME_SUFFIX = "AutoRenew"; + + protected final Token.Builder builder = Token.newBuilder(); + + private long initialSupply = 0; + + @Nullable + private SpecAccount autoRenewAccount; + + private SpecAccount treasuryAccount; + + @Nullable + private Set keys; + + public SpecToken(@NonNull final String name, @NonNull final TokenType tokenType) { + super(name); + builder.tokenType(tokenType); + treasuryAccount = new SpecAccount(name + DEFAULT_TREASURY_NAME_SUFFIX); + } + + /** + * Indicates this token should use an auto-renew account. + */ + public void useAutoRenewAccount() { + autoRenewAccount = new SpecAccount(name + DEFAULT_AUTO_RENEW_ACCOUNT_NAME_SUFFIX); + } + + /** + * Returns an operation that retrieves the token information. + * + * @return the operation + */ + public GetTokenInfoOperation getInfo() { + return new GetTokenInfoOperation(this); + } + + /** + * {@inheritDoc} + */ + @Override + public List prerequisiteEntities() { + final List prerequisites = new ArrayList<>(); + prerequisites.add(treasuryAccount); + if (autoRenewAccount != null) { + prerequisites.add(autoRenewAccount); + } + return prerequisites; + } + + /** + * Returns a builder for the model token to be created, or throws if the entity is locked. + * + * @return the builder + */ + public Token.Builder builder() { + throwIfLocked(); + return builder; + } + + /** + * Gets the token model for the given network, or throws if it doesn't exist. + * + * @param network the network + * @return the token model + */ + public Token tokenOrThrow(@NonNull final HederaNetwork network) { + return modelOrThrow(network); + } + + /** + * Sets the account that will pay for auto-renewals of the token. + * + * @param autoRenewAccount the account + */ + public void setAutoRenewAccount(@NonNull final SpecAccount autoRenewAccount) { + this.autoRenewAccount = requireNonNull(autoRenewAccount); + } + + /** + * Sets the types of keys to associate with the token. + * + * @param keys the types of keys to associate with the token + */ + public void setKeys(@NonNull final Set keys) { + this.keys = requireNonNull(keys); + } + + /** + * Gets the treasury account. + * + * @return the treasury account + */ + public SpecAccount treasury() { + return treasuryAccount; + } + + /** + * Returns an operation to authorize the given contracts to act on behalf of this token. + * + * @param contracts the contracts to authorize + * @return the operation + */ + public AuthorizeContractOperation authorizeContracts(@NonNull final SpecContract... contracts) { + requireNonNull(contracts); + return new AuthorizeContractOperation(this, contracts); + } + + /** + * Sets the treasury account. + * + * @param treasuryAccount the treasury account + */ + public void setTreasury(@NonNull final SpecAccount treasuryAccount) { + this.treasuryAccount = requireNonNull(treasuryAccount); + } + + /** + * {@inheritDoc} + */ + @Override + public Address addressOn(@NonNull HederaNetwork network) { + requireNonNull(network); + final var networkToken = tokenOrThrow(network); + return headlongAddressOf(networkToken.tokenIdOrThrow()); + } + + @Override + public String toString() { + return "SpecToken{" + "name='" + name + '\'' + '}'; + } + + /** + * {@inheritDoc} + */ + @Override + protected Creation newCreation(@NonNull final HapiSpec spec) { + final var op = tokenCreate(name) + .tokenType(com.hederahashgraph.api.proto.java.TokenType.forNumber( + builder.build().tokenType().protoOrdinal())) + .treasury(requireNonNull(treasuryAccount).name()) + .initialSupply(initialSupply); + if (autoRenewAccount != null) { + op.autoRenewAccount(autoRenewAccount.name()); + } + if (keys != null) { + keys.forEach(key -> generateKeyInContext(key, spec, op)); + } + return new Creation<>(op, builder.build()); + } + + /** + * {@inheritDoc} + */ + @Override + protected Result resultForSuccessful( + @NonNull final Creation creation, @NonNull final HapiSpec spec) { + final var newTokenNum = creation.op().numOfCreatedTokenOrThrow(); + final var allKeyMetadata = creation.op().allCreatedKeyMetadata(spec); + return new Result<>( + creation.model() + .copyBuilder() + .tokenId(TokenID.newBuilder().tokenNum(newTokenNum).build()) + .build(), + atMostOnce(siblingSpec -> { + allKeyMetadata.forEach(keyMetadata -> keyMetadata.registerAs(name, siblingSpec)); + siblingSpec + .registry() + .saveTokenId( + name, + com.hederahashgraph.api.proto.java.TokenID.newBuilder() + .setTokenNum(newTokenNum) + .build()); + creation.op().registerAttributes(siblingSpec); + siblingSpec + .registry() + .saveContractId( + name, + ContractID.newBuilder() + .setContractNum(newTokenNum) + .build()); + })); + } + + private void generateKeyInContext( + @NonNull final SpecTokenKey tokenKey, @NonNull final HapiSpec spec, @NonNull final HapiTokenCreate op) { + final var key = spec.keys().generate(spec, SIMPLE); + final var keyName = name + "_" + tokenKey; + spec.registry().saveKey(keyName, key); + switch (tokenKey) { + case ADMIN_KEY -> { + op.adminKey(keyName); + builder.adminKey(toPbj(key)); + } + case KYC_KEY -> { + op.kycKey(keyName); + builder.kycKey(toPbj(key)); + } + case FREEZE_KEY -> { + op.freezeKey(keyName); + builder.freezeKey(toPbj(key)); + } + case WIPE_KEY -> { + op.wipeKey(keyName); + builder.wipeKey(toPbj(key)); + } + case SUPPLY_KEY -> { + op.supplyKey(keyName); + builder.supplyKey(toPbj(key)); + } + case FEE_SCHEDULE_KEY -> { + op.feeScheduleKey(keyName); + builder.feeScheduleKey(toPbj(key)); + } + case PAUSE_KEY -> { + op.pauseKey(keyName); + builder.pauseKey(toPbj(key)); + } + case METADATA_KEY -> { + op.metadataKey(keyName); + builder.metadataKey(toPbj(key)); + } + } + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecTokenKey.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecTokenKey.java new file mode 100644 index 000000000000..c8213b075c91 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/entities/SpecTokenKey.java @@ -0,0 +1,31 @@ +/* + * 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.services.bdd.spec.dsl.entities; + +/** + * Enumerates the types of keys that can be associated with a token. + */ +public enum SpecTokenKey { + ADMIN_KEY, + KYC_KEY, + FREEZE_KEY, + WIPE_KEY, + SUPPLY_KEY, + FEE_SCHEDULE_KEY, + PAUSE_KEY, + METADATA_KEY, +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/AbstractSpecOperation.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/AbstractSpecOperation.java new file mode 100644 index 000000000000..affb0a795525 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/AbstractSpecOperation.java @@ -0,0 +1,100 @@ +/* + * 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.services.bdd.spec.dsl.operations; + +import static java.util.Objects.requireNonNull; + +import com.hedera.services.bdd.SpecOperation; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.SpecEntity; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * Provides implementation support for a {@link SpecOperation} that depends on + * one or more {@link SpecEntity}s to be present in the {@link HapiSpec} before + * it can be executed. + */ +public abstract class AbstractSpecOperation implements SpecOperation { + protected final List requiredEntities; + + protected AbstractSpecOperation(@NonNull final List requiredEntities) { + this.requiredEntities = closureOf(requireNonNull(requiredEntities)); + } + + /** + * Computes the delegate operation for the given {@link HapiSpec}. + * + * @param spec the {@link HapiSpec} to compute the delegate operation for + * @return the delegate operation + */ + protected abstract @NonNull SpecOperation computeDelegate(@NonNull final HapiSpec spec); + + /** + * Hook to be called when the operation is successful. + * + * @param spec the {@link HapiSpec} the operation was successful for + */ + protected void onSuccess(@NonNull final HapiSpec spec) { + // No-op + } + + /** + * {@inheritDoc} + * + *

Executes the operation for the given {@link HapiSpec} by first ensuring all entities + * are registered with the spec, then computing its delegate operation and submitting it. + * + * @param spec the {@link HapiSpec} to execute the operation for + * @return an optional containing any failure that was thrown + */ + @Override + public Optional execFor(@NonNull final HapiSpec spec) { + requireNonNull(spec); + requiredEntities.forEach(entity -> entity.registerOrCreateWith(spec)); + final var delegate = computeDelegate(spec); + final var maybeFailure = delegate.execFor(spec); + if (maybeFailure.isEmpty()) { + onSuccess(spec); + } + return maybeFailure; + } + + /** + * Returns the closure of the given direct requirements, including all transitive prerequisites. + * The returned list may contain duplicates, but this doesn't matter for our purposes. + * + *

We also do not attempt to achieve a topological sort of the requirements; once again + * there is no likely scenario where this would be necessary, as specs will only use a handful of + * entities. + * + * @param directRequirements the direct requirements + * @return the closure of the direct requirements + */ + private List closureOf(@NonNull final List directRequirements) { + final List allRequirements = new ArrayList<>(); + directRequirements.forEach(entity -> addRequisites(allRequirements, entity)); + return allRequirements; + } + + private void addRequisites(@NonNull final List allRequirements, @NonNull final SpecEntity entity) { + entity.prerequisiteEntities().forEach(prerequisite -> addRequisites(allRequirements, prerequisite)); + allRequirements.add(entity); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/assertions/AssertNftOwnerOperation.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/assertions/AssertNftOwnerOperation.java new file mode 100644 index 000000000000..b8b82bdda254 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/assertions/AssertNftOwnerOperation.java @@ -0,0 +1,56 @@ +/* + * 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.services.bdd.spec.dsl.operations.assertions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.hedera.services.bdd.SpecOperation; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.entities.SpecAccount; +import com.hedera.services.bdd.spec.dsl.entities.SpecNft; +import com.hedera.services.bdd.spec.dsl.operations.AbstractSpecOperation; +import com.hedera.services.bdd.spec.utilops.RunnableOp; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.List; + +/** + * Asserts that a given account owns a given NFT. + */ +public class AssertNftOwnerOperation extends AbstractSpecOperation { + private final SpecNft nft; + private final SpecAccount owner; + + public AssertNftOwnerOperation(@NonNull final SpecNft nft, @NonNull final SpecAccount owner) { + super(List.of(nft, owner)); + this.nft = nft; + this.owner = owner; + } + + /** + * {@inheritDoc} + */ + @NonNull + @Override + protected SpecOperation computeDelegate(@NonNull final HapiSpec spec) { + return new RunnableOp(() -> { + final var network = spec.targetNetworkOrThrow(); + assertEquals( + owner.accountOrThrow(network).accountIdOrThrow(), + nft.nftOrThrow(network).ownerIdOrThrow()); + }); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/deferred/DoWithModelOperation.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/deferred/DoWithModelOperation.java new file mode 100644 index 000000000000..ef60d183205a --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/deferred/DoWithModelOperation.java @@ -0,0 +1,51 @@ +/* + * 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.services.bdd.spec.dsl.operations.deferred; + +import static java.util.Objects.requireNonNull; + +import com.hedera.services.bdd.SpecOperation; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.entities.AbstractSpecEntity; +import com.hedera.services.bdd.spec.dsl.operations.AbstractSpecOperation; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.List; +import java.util.function.Function; + +/** + * Represents an operation that is deferred until a model is available. + * + * @param the type of the model + */ +public class DoWithModelOperation extends AbstractSpecOperation implements SpecOperation { + private final AbstractSpecEntity source; + private final Function function; + + public DoWithModelOperation( + @NonNull final AbstractSpecEntity source, @NonNull final Function function) { + super(List.of(source)); + this.source = source; + this.function = requireNonNull(function); + } + + @NonNull + @Override + protected SpecOperation computeDelegate(@NonNull final HapiSpec spec) { + requireNonNull(spec); + return function.apply(source.modelOrThrow(spec.targetNetworkOrThrow())); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/queries/AbstractSpecQuery.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/queries/AbstractSpecQuery.java new file mode 100644 index 000000000000..d7f4f7b0394a --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/queries/AbstractSpecQuery.java @@ -0,0 +1,65 @@ +/* + * 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.services.bdd.spec.dsl.operations.queries; + +import static java.util.Objects.requireNonNull; + +import com.hedera.services.bdd.spec.dsl.SpecEntity; +import com.hedera.services.bdd.spec.dsl.operations.AbstractSpecOperation; +import com.hedera.services.bdd.spec.queries.HapiQueryOp; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; + +public abstract class AbstractSpecQuery, T extends HapiQueryOp> + extends AbstractSpecOperation { + @Nullable + private Consumer assertions; + + protected AbstractSpecQuery(@NonNull final List requiredEntities) { + super(requiredEntities); + } + + /** + * Set the assertions to be made on the transaction. + * + * @param assertions the assertions + * @return this + */ + public S andAssert(@NonNull final Consumer assertions) { + this.assertions = requireNonNull(assertions); + return self(); + } + + /** + * Get the assertions to be made on the transaction, if present. + * + * @return the assertions + */ + protected Optional> maybeAssertions() { + return Optional.ofNullable(assertions); + } + + /** + * Return this object as an instance of its concrete type. + * + * @return this + */ + protected abstract S self(); +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/queries/GetBalanceOperation.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/queries/GetBalanceOperation.java new file mode 100644 index 000000000000..1f539f393f08 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/queries/GetBalanceOperation.java @@ -0,0 +1,54 @@ +/* + * 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.services.bdd.spec.dsl.operations.queries; + +import static com.hedera.services.bdd.spec.queries.QueryVerbs.getAccountBalance; + +import com.hedera.services.bdd.SpecOperation; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.entities.SpecAccount; +import com.hedera.services.bdd.spec.dsl.operations.AbstractSpecOperation; +import com.hedera.services.bdd.spec.queries.crypto.HapiGetAccountBalance; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; + +public class GetBalanceOperation extends AbstractSpecOperation implements SpecOperation { + private final SpecAccount target; + + @Nullable + private Consumer assertions = null; + + public GetBalanceOperation(@NonNull final SpecAccount target) { + super(List.of(target)); + this.target = target; + } + + public GetBalanceOperation andAssert(@NonNull final Consumer assertions) { + this.assertions = assertions; + return this; + } + + @Override + protected @NonNull SpecOperation computeDelegate(@NonNull final HapiSpec spec) { + final var op = getAccountBalance(target.name()); + Optional.ofNullable(assertions).ifPresent(a -> a.accept(op)); + return op; + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/queries/GetContractInfoOperation.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/queries/GetContractInfoOperation.java new file mode 100644 index 000000000000..5b1d8420ca90 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/queries/GetContractInfoOperation.java @@ -0,0 +1,54 @@ +/* + * 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.services.bdd.spec.dsl.operations.queries; + +import static com.hedera.services.bdd.spec.queries.QueryVerbs.getContractInfo; + +import com.hedera.services.bdd.SpecOperation; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.entities.SpecContract; +import com.hedera.services.bdd.spec.dsl.operations.AbstractSpecOperation; +import com.hedera.services.bdd.spec.queries.contract.HapiGetContractInfo; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; + +public class GetContractInfoOperation extends AbstractSpecOperation implements SpecOperation { + private final SpecContract target; + + @Nullable + private Consumer assertions = null; + + public GetContractInfoOperation(@NonNull final SpecContract target) { + super(List.of(target)); + this.target = target; + } + + public GetContractInfoOperation andAssert(@NonNull final Consumer assertions) { + this.assertions = assertions; + return this; + } + + @Override + protected @NonNull SpecOperation computeDelegate(@NonNull final HapiSpec spec) { + final var op = getContractInfo(target.name()); + Optional.ofNullable(assertions).ifPresent(a -> a.accept(op)); + return op; + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/queries/GetTokenInfoOperation.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/queries/GetTokenInfoOperation.java new file mode 100644 index 000000000000..3d02871f8798 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/queries/GetTokenInfoOperation.java @@ -0,0 +1,55 @@ +/* + * 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.services.bdd.spec.dsl.operations.queries; + +import static com.hedera.services.bdd.spec.queries.QueryVerbs.getTokenInfo; + +import com.hedera.services.bdd.SpecOperation; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.entities.SpecToken; +import com.hedera.services.bdd.spec.dsl.operations.AbstractSpecOperation; +import com.hedera.services.bdd.spec.queries.token.HapiGetTokenInfo; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; + +public class GetTokenInfoOperation extends AbstractSpecOperation implements SpecOperation { + private final SpecToken target; + + @Nullable + private Consumer assertions = null; + + public GetTokenInfoOperation(@NonNull final SpecToken target) { + super(List.of(target)); + this.target = target; + } + + public GetTokenInfoOperation andAssert(@NonNull final Consumer assertions) { + this.assertions = assertions; + return this; + } + + @NonNull + @Override + protected SpecOperation computeDelegate(@NonNull HapiSpec spec) { + final var op = getTokenInfo(target.name()); + Optional.ofNullable(assertions).ifPresent(a -> a.accept(op)); + return op; + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/queries/StaticCallContractOperation.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/queries/StaticCallContractOperation.java new file mode 100644 index 000000000000..352aaecfcf5a --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/queries/StaticCallContractOperation.java @@ -0,0 +1,63 @@ +/* + * 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.services.bdd.spec.dsl.operations.queries; + +import static com.hedera.services.bdd.spec.dsl.utils.DslUtils.allRequiredCallEntities; +import static com.hedera.services.bdd.spec.dsl.utils.DslUtils.withSubstitutedTypes; +import static com.hedera.services.bdd.spec.queries.QueryVerbs.contractCallLocal; +import static java.util.Objects.requireNonNull; + +import com.hedera.services.bdd.SpecOperation; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.entities.SpecContract; +import com.hedera.services.bdd.spec.queries.contract.HapiContractCallLocal; +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Represents a static call to a smart contract. + */ +public class StaticCallContractOperation extends AbstractSpecQuery + implements SpecOperation { + private final SpecContract target; + private final String function; + private final Object[] parameters; + + public StaticCallContractOperation( + @NonNull final SpecContract target, @NonNull final String function, @NonNull final Object... parameters) { + super(allRequiredCallEntities(target, parameters)); + this.target = requireNonNull(target); + this.function = requireNonNull(function); + this.parameters = requireNonNull(parameters); + } + + @NonNull + @Override + protected SpecOperation computeDelegate(@NonNull HapiSpec spec) { + final var op = contractCallLocal( + target.name(), function, withSubstitutedTypes(spec.targetNetworkOrThrow(), parameters)); + maybeAssertions().ifPresent(a -> a.accept(op)); + return op; + } + + /** + * {@inheritDoc} + */ + @Override + protected StaticCallContractOperation self() { + return this; + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/AbstractSpecTransaction.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/AbstractSpecTransaction.java new file mode 100644 index 000000000000..c534bae5533f --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/AbstractSpecTransaction.java @@ -0,0 +1,65 @@ +/* + * 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.services.bdd.spec.dsl.operations.transactions; + +import static java.util.Objects.requireNonNull; + +import com.hedera.services.bdd.spec.dsl.SpecEntity; +import com.hedera.services.bdd.spec.dsl.operations.AbstractSpecOperation; +import com.hedera.services.bdd.spec.transactions.HapiTxnOp; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; + +public abstract class AbstractSpecTransaction, T extends HapiTxnOp> + extends AbstractSpecOperation { + @Nullable + private Consumer assertions; + + protected AbstractSpecTransaction(@NonNull final List requiredEntities) { + super(requiredEntities); + } + + /** + * Set the assertions to be made on the transaction. + * + * @param assertions the assertions + * @return this + */ + public S andAssert(@NonNull final Consumer assertions) { + this.assertions = requireNonNull(assertions); + return self(); + } + + /** + * Get the assertions to be made on the transaction, if present. + * + * @return the assertions + */ + protected Optional> maybeAssertions() { + return Optional.ofNullable(assertions); + } + + /** + * Return this object as an instance of its concrete type. + * + * @return this + */ + protected abstract S self(); +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/AssociateTokensOperation.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/AssociateTokensOperation.java new file mode 100644 index 000000000000..9d63a4e4cd28 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/AssociateTokensOperation.java @@ -0,0 +1,68 @@ +/* + * 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.services.bdd.spec.dsl.operations.transactions; + +import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenAssociate; + +import com.hedera.services.bdd.SpecOperation; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.entities.SpecAccount; +import com.hedera.services.bdd.spec.dsl.entities.SpecToken; +import com.hedera.services.bdd.spec.transactions.token.HapiTokenAssociate; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.ArrayList; +import java.util.List; + +/** + * Associates an account with one or more tokens. + */ +public class AssociateTokensOperation extends AbstractSpecTransaction + implements SpecOperation { + private final SpecAccount account; + private final List tokens; + + // non-standard ArrayList initializer + @SuppressWarnings({"java:S3599", "java:S1171"}) + public AssociateTokensOperation(@NonNull final SpecAccount account, @NonNull final List tokens) { + super(new ArrayList<>() { + { + add(account); + addAll(tokens); + } + }); + this.account = account; + this.tokens = tokens; + } + + /** + * {@inheritDoc} + */ + @Override + protected AssociateTokensOperation self() { + return this; + } + + /** + * {@inheritDoc} + */ + @NonNull + @Override + protected SpecOperation computeDelegate(@NonNull final HapiSpec spec) { + return tokenAssociate( + account.name(), tokens.stream().map(SpecToken::name).toArray(String[]::new)); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/AuthorizeContractOperation.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/AuthorizeContractOperation.java new file mode 100644 index 000000000000..8eb66ed88499 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/AuthorizeContractOperation.java @@ -0,0 +1,112 @@ +/* + * 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.services.bdd.spec.dsl.operations.transactions; + +import static com.hedera.node.app.service.mono.pbj.PbjConverter.toPbj; +import static com.hedera.services.bdd.spec.keys.DefaultKeyGen.DEFAULT_KEY_GEN; +import static com.hedera.services.bdd.spec.keys.KeyShape.CONTRACT; +import static com.hedera.services.bdd.spec.keys.KeyShape.ED25519; +import static com.hedera.services.bdd.spec.keys.KeyShape.sigs; +import static com.hedera.services.bdd.spec.keys.SigControl.ED25519_ON; +import static com.hedera.services.bdd.spec.transactions.TxnVerbs.cryptoUpdate; +import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenUpdate; +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.joining; + +import com.hedera.services.bdd.SpecOperation; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.SpecEntity; +import com.hedera.services.bdd.spec.dsl.entities.SpecAccount; +import com.hedera.services.bdd.spec.dsl.entities.SpecContract; +import com.hedera.services.bdd.spec.dsl.entities.SpecToken; +import com.hedera.services.bdd.spec.dsl.operations.AbstractSpecOperation; +import com.hedera.services.bdd.spec.keys.KeyShape; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Authorizes a contract to act on behalf of an entity. + */ +public class AuthorizeContractOperation extends AbstractSpecOperation implements SpecOperation { + private final String managedKeyName; + private final SpecEntity target; + private final SpecContract[] contracts; + + // non-standard ArrayList initializer + @SuppressWarnings({"java:S3599", "java:S1171"}) + public AuthorizeContractOperation(@NonNull final SpecEntity target, @NonNull final SpecContract... contracts) { + super(new ArrayList<>() { + { + add(target); + addAll(List.of(contracts)); + } + }); + this.target = target; + this.contracts = requireNonNull(contracts); + this.managedKeyName = target.name() + "_" + + Arrays.stream(contracts).map(SpecContract::name).collect(joining("|")) + "ManagedKey"; + } + + /** + * {@inheritDoc} + */ + @NonNull + @Override + protected SpecOperation computeDelegate(@NonNull final HapiSpec spec) { + final var controller = managedKeyShape().signedWith(sigControl()); + final var key = spec.keys().generateSubjectTo(spec, controller, DEFAULT_KEY_GEN); + spec.registry().saveKey(managedKeyName, key); + return switch (target) { + case SpecAccount account -> cryptoUpdate(account.name()).key(managedKeyName); + case SpecToken token -> tokenUpdate(token.name()).adminKey(managedKeyName); + default -> throw new IllegalStateException("Cannot authorize contracts for " + target); + }; + } + + /** + * {@inheritDoc} + */ + @Override + protected void onSuccess(@NonNull final HapiSpec spec) { + switch (target) { + case SpecAccount account -> account.updateKeyFrom( + toPbj(spec.registry().getKey(managedKeyName)), spec); + case SpecToken token -> { + // (FUTURE) - update the admin key on the token model + } + default -> throw new IllegalStateException("Cannot authorize contract for " + target); + } + } + + private KeyShape managedKeyShape() { + final var shapes = new KeyShape[contracts.length + 1]; + Arrays.fill(shapes, CONTRACT); + shapes[0] = ED25519; + return KeyShape.threshOf(1, shapes); + } + + private Object sigControl() { + final var controls = new Object[contracts.length + 1]; + controls[0] = ED25519_ON; + for (var i = 0; i < contracts.length; i++) { + controls[i + 1] = contracts[i].name(); + } + return sigs(controls); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/CallContractOperation.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/CallContractOperation.java new file mode 100644 index 000000000000..d648ffa13a49 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/CallContractOperation.java @@ -0,0 +1,60 @@ +/* + * 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.services.bdd.spec.dsl.operations.transactions; + +import static com.hedera.services.bdd.spec.dsl.utils.DslUtils.allRequiredCallEntities; +import static com.hedera.services.bdd.spec.dsl.utils.DslUtils.withSubstitutedTypes; +import static com.hedera.services.bdd.spec.transactions.TxnVerbs.contractCall; +import static java.util.Objects.requireNonNull; + +import com.hedera.services.bdd.SpecOperation; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.entities.SpecContract; +import com.hedera.services.bdd.spec.transactions.contract.HapiContractCall; +import edu.umd.cs.findbugs.annotations.NonNull; + +/** + * Represents a call to a smart contract. + */ +public class CallContractOperation extends AbstractSpecTransaction + implements SpecOperation { + private final SpecContract target; + private final String function; + private final Object[] parameters; + + public CallContractOperation( + @NonNull final SpecContract target, @NonNull final String function, @NonNull final Object... parameters) { + super(allRequiredCallEntities(target, parameters)); + this.target = requireNonNull(target); + this.function = requireNonNull(function); + this.parameters = requireNonNull(parameters); + } + + @NonNull + @Override + protected SpecOperation computeDelegate(@NonNull final HapiSpec spec) { + final var op = + contractCall(target.name(), function, withSubstitutedTypes(spec.targetNetworkOrThrow(), parameters)); + maybeAssertions().ifPresent(a -> a.accept(op)); + return op; + } + + @Override + protected CallContractOperation self() { + return this; + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/DeleteAccountOperation.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/DeleteAccountOperation.java new file mode 100644 index 000000000000..22634e48ede6 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/operations/transactions/DeleteAccountOperation.java @@ -0,0 +1,50 @@ +/* + * 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.services.bdd.spec.dsl.operations.transactions; + +import static com.hedera.services.bdd.spec.transactions.TxnVerbs.cryptoDelete; + +import com.hedera.services.bdd.SpecOperation; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.entities.SpecAccount; +import com.hedera.services.bdd.spec.dsl.operations.AbstractSpecOperation; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.List; + +/** + * Deletes an account and transfers its balance to a beneficiary account. + */ +public class DeleteAccountOperation extends AbstractSpecOperation implements SpecOperation { + private final SpecAccount target; + private final SpecAccount beneficiary; + + public DeleteAccountOperation(@NonNull final SpecAccount target, @NonNull final SpecAccount beneficiary) { + super(List.of(target, beneficiary)); + this.target = target; + this.beneficiary = beneficiary; + } + + @Override + protected @NonNull SpecOperation computeDelegate(@NonNull final HapiSpec spec) { + return cryptoDelete(target.name()).transfer(beneficiary.name()); + } + + @Override + public String toString() { + return "DeleteAccountOperation{" + "target=" + target + ", beneficiary=" + beneficiary + '}'; + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/utils/DslUtils.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/utils/DslUtils.java new file mode 100644 index 000000000000..07a2b1e478fe --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/utils/DslUtils.java @@ -0,0 +1,116 @@ +/* + * 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.services.bdd.spec.dsl.utils; + +import com.hedera.hapi.node.base.ContractID; +import com.hedera.hapi.node.base.Key; +import com.hedera.hapi.node.base.KeyList; +import com.hedera.hapi.node.state.token.Account; +import com.hedera.node.app.service.mono.pbj.PbjConverter; +import com.hedera.services.bdd.junit.hedera.HederaNetwork; +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.EvmAddressableEntity; +import com.hedera.services.bdd.spec.dsl.SpecEntity; +import com.hedera.services.bdd.spec.dsl.SpecEntityRegistrar; +import com.hedera.services.bdd.spec.dsl.entities.SpecContract; +import com.hedera.services.bdd.spec.dsl.entities.SpecKey; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +public class DslUtils { + private DslUtils() { + throw new UnsupportedOperationException("Utility Class"); + } + + public static final Key PBJ_IMMUTABILITY_SENTINEL_KEY = + Key.newBuilder().keyList(KeyList.DEFAULT).build(); + public static final com.hederahashgraph.api.proto.java.Key PROTO_IMMUTABILITY_SENTINEL_KEY = + PbjConverter.fromPbj(PBJ_IMMUTABILITY_SENTINEL_KEY); + + /** + * Substitutes spec entities with their EVM applicable types in the given arguments, as follows: + *

    + *
  • Substitutes {@link EvmAddressableEntity} entities with their addresses.
  • + *
  • Substitutes {@link SpecKey} entities with their raw bytes.
  • + *
+ * + * @param network the network + * @param args the arguments + * @return the arguments with entities substituted with their EVM applicable types + */ + public static Object[] withSubstitutedTypes(@NonNull final HederaNetwork network, @NonNull final Object... args) { + return Arrays.stream(args) + .map(arg -> switch (arg) { + case EvmAddressableEntity evmAddressableEntity -> evmAddressableEntity.addressOn(network); + case SpecKey key -> key.asEncodedOn(network); + default -> arg; + }) + .toArray(Object[]::new); + } + + /** + * Returns a registrar that ensures that a spec entity is registered at most once per spec. + * + * @param registrar the registrar + * @return a registrar that ensures that a spec entity is registered at most once per spec + */ + public static SpecEntityRegistrar atMostOnce(@NonNull final SpecEntityRegistrar registrar) { + final Set specs = new LinkedHashSet<>(); + return spec -> { + if (specs.add(spec)) { + registrar.registerWith(spec); + } + }; + } + + /** + * Returns a list of all required entities for a call to a smart contract. + * + * @param target the target contract + * @param parameters the parameters of the call + * @return a list of all required entities + */ + public static List allRequiredCallEntities( + @NonNull final SpecContract target, @NonNull final Object[] parameters) { + List requiredEntities = new ArrayList<>(); + requiredEntities.add(target); + for (final var parameter : parameters) { + if (parameter instanceof SpecEntity entity) { + requiredEntities.add(entity); + } + } + return requiredEntities; + } + + /** + * Returns a contract id key activated by exclusively the given contract. + * + * @param contract the contract + * @return a contract id key activated by exclusively the given contract + */ + public static Key contractIdKeyFor(@NonNull final Account contract) { + return Key.newBuilder() + .contractID(ContractID.newBuilder() + .contractNum(contract.accountIdOrThrow().accountNumOrThrow()) + .build()) + .build(); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/utils/KeyMetadata.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/utils/KeyMetadata.java new file mode 100644 index 000000000000..47fbb92d7aad --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/dsl/utils/KeyMetadata.java @@ -0,0 +1,125 @@ +/* + * 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.services.bdd.spec.dsl.utils; + +import static com.hedera.node.app.service.mono.pbj.PbjConverter.fromPbj; +import static com.hedera.node.app.service.mono.pbj.PbjConverter.toPbj; +import static java.util.Objects.requireNonNull; + +import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.infrastructure.HapiSpecRegistry; +import com.hedera.services.bdd.spec.keys.SigControl; +import com.hederahashgraph.api.proto.java.Key; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.security.PrivateKey; +import java.util.Map; + +/** + * Encapsulates metadata about a key, including its protoc and PBJ representations, a map of + * hex-encoded public keys to their corresponding private keys, and the {@link SigControl} + * instance that determines how constituent primitive keys should sign. + * + * @param protoKey the protoc representation of the key + * @param pbjKey the PBJ representation of the key + * @param privateKeyMap a map of hex-encoded public keys to their corresponding private keys + * @param sigControl the signing control + */ +public record KeyMetadata( + Key protoKey, + com.hedera.hapi.node.base.Key pbjKey, + Map privateKeyMap, + SigControl sigControl, + Registration registration) { + + public interface Registration { + void save(HapiSpecRegistry registry, String name, Key key); + } + + private static final Registration DEFAULT_REGISTRATION = HapiSpecRegistry::saveKey; + + /** + * Constructs a {@link KeyMetadata} instance from the given protoc key and {@link HapiSpec} + * with the default registration. + * + * @param protoKey the protoc key + * @param spec the HapiSpec + * @return the key metadata + */ + public static KeyMetadata from(@NonNull final Key protoKey, @NonNull final HapiSpec spec) { + return from(protoKey, spec, DEFAULT_REGISTRATION); + } + + /** + * Constructs a {@link KeyMetadata} instance from the given PBJ key and {@link HapiSpec} + * with the default registration. + * + * @param pbjKey the protoc key + * @param spec the HapiSpec + * @return the key metadata + */ + public static KeyMetadata from(@NonNull final com.hedera.hapi.node.base.Key pbjKey, @NonNull final HapiSpec spec) { + requireNonNull(pbjKey); + return from(null, pbjKey, spec, DEFAULT_REGISTRATION); + } + + /** + * Constructs a {@link KeyMetadata} instance from the given protoc key, {@link HapiSpec}, and + * registration function. + * + * @param protoKey the protoc key + * @param spec the HapiSpec + * @param registration the registration function + * @return the key metadata + */ + public static KeyMetadata from( + @NonNull final Key protoKey, @NonNull final HapiSpec spec, @NonNull final Registration registration) { + requireNonNull(protoKey); + return from(protoKey, null, spec, registration); + } + + private static KeyMetadata from( + @Nullable final Key maybeProtoKey, + @Nullable final com.hedera.hapi.node.base.Key maybePbjKey, + @NonNull final HapiSpec spec, + @NonNull final Registration registration) { + requireNonNull(spec); + requireNonNull(registration); + final var pbjKey = maybePbjKey != null ? maybePbjKey : toPbj(requireNonNull(maybeProtoKey)); + final var protoKey = maybeProtoKey != null ? maybeProtoKey : fromPbj(requireNonNull(maybePbjKey)); + return new KeyMetadata( + protoKey, + pbjKey, + spec.keys().privateKeyMapFor(pbjKey), + spec.keys().controlFor(protoKey), + registration); + } + + /** + * Registers this key metadata under the given name in the given {@link HapiSpec}. + * + * @param name the name + * @param spec the HapiSpec + */ + public void registerAs(@NonNull final String name, @NonNull final HapiSpec spec) { + requireNonNull(spec); + requireNonNull(name); + registration.save(spec.registry(), name, protoKey); + spec.keys().setControl(protoKey, sigControl); + spec.keys().addPrivateKeyMap(privateKeyMap); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/infrastructure/HapiSpecRegistry.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/infrastructure/HapiSpecRegistry.java index 29494b58a778..c8890509eb32 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/infrastructure/HapiSpecRegistry.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/infrastructure/HapiSpecRegistry.java @@ -22,9 +22,7 @@ import static com.hedera.services.bdd.spec.keys.KeyFactory.payerKey; import static com.hedera.services.bdd.suites.HapiSuite.DEFAULT_CONTRACT_RECEIVER; import static com.hedera.services.bdd.suites.HapiSuite.DEFAULT_CONTRACT_SENDER; -import static java.util.stream.Collectors.counting; -import static java.util.stream.Collectors.groupingBy; -import static java.util.stream.Collectors.toList; +import static java.util.Objects.requireNonNull; import com.google.protobuf.ByteString; import com.hedera.services.bdd.spec.HapiPropertySource; @@ -479,30 +477,6 @@ public Long getAmount(String name) { return get(name, Long.class); } - public void saveIntValue(String name, Integer intVal) { - put(name, intVal); - } - - public Integer getIntValue(String name) { - return get(name, Integer.class); - } - - public void saveFloatValue(String name, Float floatVal) { - put(name, floatVal); - } - - public Float getFloatValue(String name) { - return get(name, Float.class); - } - - public void saveDoubleValue(String name, Double doubleVal) { - put(name, doubleVal); - } - - public Double getDoubleValue(String name) { - return get(name, Double.class); - } - public void saveSigRequirement(String name, Boolean isRequired) { put(name, isRequired); } @@ -550,6 +524,12 @@ public Optional getMaybeTxnId(String name) { return Optional.ofNullable(getOrElse(name, TransactionID.class, null)); } + public void saveRecord(@NonNull final String name, @NonNull final T registryRecord) { + requireNonNull(name); + requireNonNull(registryRecord); + put(name, registryRecord); + } + public void saveAccountId(String name, AccountID id) { put(name, id); put(asAccountString(id), name); @@ -760,13 +740,6 @@ public void saveTransactionRecord(String name, TransactionRecord txnRecord) { put(name, txnRecord); } - public void removeTransactionRecord(String name) { - try { - remove(name, TransactionRecord.class); - } catch (Exception ignore) { - } - } - public boolean hasTransactionRecord(String name) { return has(name, TransactionRecord.class); } @@ -779,13 +752,6 @@ public void saveFileInfo(String name, FileGetInfoResponse.FileInfo info) { put(name, info); } - public void removeFileInfo(String name) { - try { - remove(name, ContractGetInfoResponse.ContractInfo.class); - } catch (Exception ignore) { - } - } - public FileGetInfoResponse.FileInfo getFileInfo(String name) { return get(name, FileGetInfoResponse.FileInfo.class); } @@ -798,13 +764,6 @@ public void saveAccountDetails(String name, GetAccountDetailsResponse.AccountDet put(name, details); } - public void removeAccountInfo(String name) { - try { - remove(name, CryptoGetInfoResponse.AccountInfo.class); - } catch (Exception ignore) { - } - } - public CryptoGetInfoResponse.AccountInfo getAccountInfo(String name) { return get(name, CryptoGetInfoResponse.AccountInfo.class); } @@ -888,18 +847,6 @@ private String full(String name, Class type) { return typeName + "-" + name; } - public Map typeCounts() { - return registry.values().stream().collect(groupingBy(Object::getClass, counting())); - } - - public List stringValues() { - return registry.entrySet().stream() - .filter(entry -> entry.getValue().getClass().equals(String.class)) - .map(entry -> String.format( - "%s -> %s", entry.getKey(), entry.getValue().toString())) - .collect(toList()); - } - public void forgetMetadataKey(String name) { remove(name + "Metadata", Key.class); } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/keys/KeyFactory.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/keys/KeyFactory.java index 7cdac4f6e788..5b6f31f26fd6 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/keys/KeyFactory.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/keys/KeyFactory.java @@ -22,6 +22,7 @@ import static com.hedera.services.bdd.spec.persistence.SpecKey.mnemonicToEd25519Key; import static com.hedera.services.bdd.spec.transactions.TxnUtils.asContractId; import static java.util.Map.Entry; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toList; import com.google.protobuf.ByteString; @@ -37,6 +38,7 @@ import com.hederahashgraph.api.proto.java.SignaturePair; import com.hederahashgraph.api.proto.java.ThresholdKey; import com.hederahashgraph.api.proto.java.Transaction; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.io.Serializable; import java.io.UncheckedIOException; @@ -90,6 +92,13 @@ public KeyFactory(final HapiSpecSetup setup, final HapiSpecRegistry registry) th incorporate(setup.genesisAccountName(), genesisKey, KeyShape.listSigs(ON)); } + public Map privateKeyMapFor(@NonNull final com.hedera.hapi.node.base.Key key) { + requireNonNull(key); + final Map keyMap = new HashMap<>(); + addPrivateKeys(key, keyMap); + return keyMap; + } + public void exportSimpleKey(final String loc, final String name) { exportSimpleKey(loc, name, key -> key.getEd25519().toByteArray()); } @@ -167,6 +176,11 @@ public void setControl(final Key key, final SigControl control) { controlMap.put(key, control); } + public void addPrivateKeyMap(@NonNull final Map keyMap) { + requireNonNull(keyMap); + pkMap.putAll(keyMap); + } + public int controlledKeyCount(final Key key, final Map overrides) { return asAuthor(key, overrides).getValue().numSimpleKeys(); } @@ -333,7 +347,7 @@ public static EdDSAPrivateKey payerKey(final HapiSpecSetup setup) { public static String mnemonicFromFile(final String wordsLoc) { try { - return java.nio.file.Files.lines(Paths.get(wordsLoc)) + return Files.lines(Paths.get(wordsLoc)) .map(String::strip) .collect(Collectors.joining(" ")) .strip(); @@ -467,18 +481,42 @@ public Key generate(final HapiSpec spec, final KeyType type, final KeyGenerator KeyShape.threshSigs( setup.defaultThresholdM(), IntStream.range(0, setup.defaultThresholdN()) - .mapToObj(ignore -> SigControl.ON) + .mapToObj(ignore -> ON) .toArray(SigControl[]::new)), keyGen); case LIST: return generateSubjectTo( spec, KeyShape.listSigs(IntStream.range(0, setup.defaultListN()) - .mapToObj(ignore -> SigControl.ON) + .mapToObj(ignore -> ON) .toArray(SigControl[]::new)), keyGen); default: return generateSubjectTo(spec, ON, keyGen); } } + + private void addPrivateKeys( + @NonNull final com.hedera.hapi.node.base.Key key, @NonNull final Map keyMap) { + switch (key.key().kind()) { + case ED25519 -> { + final var hexedPubKey = com.swirlds.common.utility.CommonUtils.hex( + key.ed25519OrThrow().toByteArray()); + keyMap.put(hexedPubKey, requireNonNull(pkMap.get(hexedPubKey))); + } + case ECDSA_SECP256K1 -> { + final var hexedPubKey = com.swirlds.common.utility.CommonUtils.hex( + key.ecdsaSecp256k1OrThrow().toByteArray()); + keyMap.put(hexedPubKey, requireNonNull(pkMap.get(hexedPubKey))); + } + case KEY_LIST -> key.keyListOrThrow().keys().forEach(k -> addPrivateKeys(k, keyMap)); + case THRESHOLD_KEY -> key.thresholdKeyOrThrow() + .keysOrThrow() + .keys() + .forEach(k -> addPrivateKeys(k, keyMap)); + default -> { + // No-op + } + } + } } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/persistence/EntityManager.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/persistence/EntityManager.java index cc9ac655b911..103b5094a629 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/persistence/EntityManager.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/persistence/EntityManager.java @@ -17,8 +17,8 @@ package com.hedera.services.bdd.spec.persistence; import static com.hedera.services.bdd.spec.utilops.CustomSpecAssert.allRunFor; -import static java.util.stream.Collectors.toList; +import com.hedera.services.bdd.SpecOperation; import com.hedera.services.bdd.spec.HapiPropertySource; import com.hedera.services.bdd.spec.HapiSpec; import com.hedera.services.bdd.spec.HapiSpecOperation; @@ -213,11 +213,12 @@ private Optional extractCreated(HapiSpecOperation veiledCreationOp) { return Optional.ofNullable(createdEntityId); } - public List requiredCreations() { + public List requiredCreations() { return entities.stream() .filter(Entity::needsCreation) .map(Entity::createOp) - .collect(toList()); + .map(op -> (SpecOperation) op) + .toList(); } static class EntityMeta { diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/queries/contract/HapiGetContractRecords.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/queries/contract/HapiGetContractRecords.java index 8d8396e5c75c..aa80ee6f5cc5 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/queries/contract/HapiGetContractRecords.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/queries/contract/HapiGetContractRecords.java @@ -16,20 +16,11 @@ package com.hedera.services.bdd.spec.queries.contract; -import static com.hedera.services.bdd.spec.HapiSpec.ensureDir; -import static com.hedera.services.bdd.spec.assertions.AssertUtils.rethrowSummaryError; import static com.hedera.services.bdd.spec.queries.QueryUtils.answerCostHeader; import static com.hedera.services.bdd.spec.queries.QueryUtils.answerHeader; import com.google.common.base.MoreObjects; -import com.google.common.io.ByteSink; -import com.google.common.io.ByteSource; -import com.google.common.io.CharSink; -import com.google.common.io.CharSource; -import com.google.common.io.Files; import com.hedera.services.bdd.spec.HapiSpec; -import com.hedera.services.bdd.spec.assertions.ErroringAssertsProvider; -import com.hedera.services.bdd.spec.exceptions.HapiQueryCheckStateException; import com.hedera.services.bdd.spec.queries.HapiQueryOp; import com.hedera.services.bdd.spec.transactions.TxnUtils; import com.hederahashgraph.api.proto.java.ContractGetRecordsQuery; @@ -37,31 +28,11 @@ import com.hederahashgraph.api.proto.java.Query; import com.hederahashgraph.api.proto.java.ResponseType; import com.hederahashgraph.api.proto.java.Transaction; -import com.hederahashgraph.api.proto.java.TransactionRecord; import edu.umd.cs.findbugs.annotations.NonNull; -import java.io.File; -import java.nio.charset.Charset; -import java.util.List; import java.util.Optional; -import java.util.function.BiConsumer; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.junit.jupiter.api.Assertions; public class HapiGetContractRecords extends HapiQueryOp { - private static final Logger log = LogManager.getLogger(HapiGetContractRecords.class); - private Optional snapshotDirPath = Optional.empty(); - private Optional saveRecordNum = Optional.empty(); - private Optional expectationsDirPath = Optional.empty(); - private Optional>> customLog = Optional.empty(); - private final String contract; - private Optional>> expectation = Optional.empty(); - - public HapiGetContractRecords has(ErroringAssertsProvider> provider) { - expectation = Optional.of(provider); - return this; - } @Override public HederaFunctionality type() { @@ -77,57 +48,14 @@ public HapiGetContractRecords(String contract) { this.contract = contract; } - public HapiGetContractRecords withLogging(BiConsumer> customLog) { - verboseLoggingOn = true; - this.customLog = Optional.of(customLog); - return this; - } - - public HapiGetContractRecords savingTo(String dirPath) { - snapshotDirPath = Optional.of(dirPath); - return this; - } - - public HapiGetContractRecords saveRecordNumToRegistry(String key) { - saveRecordNum = Optional.of(key); - return this; - } - - public HapiGetContractRecords checkingAgainst(String dirPath) { - expectationsDirPath = Optional.of(dirPath); - return this; - } - @Override protected void assertExpectationsGiven(HapiSpec spec) throws Throwable { - if (expectation.isPresent()) { - List actualRecords = - response.getContractGetRecordsResponse().getRecordsList(); - List errors = expectation.get().assertsFor(spec).errorsIn(actualRecords); - rethrowSummaryError(log, "Bad contract records!", errors); - } + // No-op, this query no longer supported } @Override protected void processAnswerOnlyResponse(@NonNull final HapiSpec spec) { - List records = - response.getContractGetRecordsResponse().getRecordsList(); - if (verboseLoggingOn) { - if (customLog.isPresent()) { - customLog.get().accept(log, records); - } else { - log.info(records); - } - } - if (snapshotDirPath.isPresent()) { - saveSnapshots(spec, records); - } - if (saveRecordNum.isPresent()) { - spec.registry().saveIntValue(saveRecordNum.get(), records.size()); - } - if (expectationsDirPath.isPresent()) { - checkExpectations(spec, records); - } + // No-op, this query no longer supported } /** @@ -169,56 +97,4 @@ protected MoreObjects.ToStringHelper toStringHelper() { r.getContractGetRecordsResponse().getRecordsList().size())); return helper; } - - private void saveSnapshots(HapiSpec spec, List records) { - String specSnapshotDir = specScopedDir(spec, snapshotDirPath); - ensureDir(specSnapshotDir); - String snapshotDir = specSnapshotDir + "/" + contract; - ensureDir(snapshotDir); - - try { - File countFile = new File(snapshotDir + "/n.txt"); - CharSink charSink = Files.asCharSink(countFile, Charset.forName("UTF-8")); - int n = records.size(); - charSink.write("" + n); - - for (int i = 0; i < n; i++) { - File recordFile = new File(snapshotDir + "/record" + i + ".bin"); - ByteSink byteSink = Files.asByteSink(recordFile); - byteSink.write(records.get(i).toByteArray()); - } - String message = String.format("Saved %d records to %s", n, snapshotDir); - log.info(message); - } catch (Exception e) { - log.error("Couldn't save record snapshots!", e); - } - } - - private String specScopedDir(HapiSpec spec, Optional prefix) { - return prefix.map(d -> d + "/" + spec.getName()).get(); - } - - private void checkExpectations(HapiSpec spec, List records) { - String specExpectationsDir = specScopedDir(spec, expectationsDirPath); - try { - String expectationsDir = specExpectationsDir + "/" + contract; - File countFile = new File(expectationsDir + "/n.txt"); - CharSource charSource = Files.asCharSource(countFile, Charset.forName("UTF-8")); - int n = Integer.parseInt(charSource.readFirstLine()); - Assertions.assertEquals(n, records.size(), "Bad number of records!"); - for (int i = 0; i < n; i++) { - File recordFile = new File(expectationsDir + "/record" + i + ".bin"); - ByteSource byteSource = Files.asByteSource(recordFile); - TransactionRecord expected = TransactionRecord.parseFrom(byteSource.read()); - Assertions.assertEquals(expected, records.get(i), "Wrong record #" + i); - } - } catch (Exception e) { - if (log.isDebugEnabled()) { - log.error("Something amiss with the expected records...", e); - } else { - log.error("Something amiss with the expected records {}", records); - } - throw new HapiQueryCheckStateException("Impossible to meet expectations (on records)!"); - } - } } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/queries/token/HapiGetTokenInfo.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/queries/token/HapiGetTokenInfo.java index 46a7aeded456..17f31429f3e1 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/queries/token/HapiGetTokenInfo.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/queries/token/HapiGetTokenInfo.java @@ -16,6 +16,7 @@ package com.hedera.services.bdd.spec.queries.token; +import static com.hedera.node.app.service.mono.pbj.PbjConverter.fromPbj; import static com.hedera.services.bdd.spec.queries.QueryUtils.answerCostHeader; import static com.hedera.services.bdd.spec.queries.QueryUtils.answerHeader; import static java.util.stream.Collectors.toCollection; @@ -83,12 +84,40 @@ public HapiGetTokenInfo(String token) { private Optional expectedSymbol = Optional.empty(); private Optional expectedName = Optional.empty(); private Optional expectedTreasury = Optional.empty(); + + @Nullable + private com.hedera.hapi.node.base.Key explicitAdminKey; + private Optional expectedAdminKey = Optional.empty(); + + @Nullable + private com.hedera.hapi.node.base.Key explicitKycKey; + private Optional expectedKycKey = Optional.empty(); + + @Nullable + private com.hedera.hapi.node.base.Key explicitFreezeKey; + private Optional expectedFreezeKey = Optional.empty(); + + @Nullable + private com.hedera.hapi.node.base.Key explicitSupplyKey; + private Optional expectedSupplyKey = Optional.empty(); + + @Nullable + private com.hedera.hapi.node.base.Key explicitWipeKey; + private Optional expectedWipeKey = Optional.empty(); + + @Nullable + private com.hedera.hapi.node.base.Key explicitFeeScheduleKey; + private Optional expectedFeeScheduleKey = Optional.empty(); + + @Nullable + private com.hedera.hapi.node.base.Key explicitPauseKey; + private Optional expectedPauseKey = Optional.empty(); private boolean emptyAdminKey = false; private boolean emptyWipeKey = false; @@ -240,11 +269,26 @@ public HapiGetTokenInfo hasFreezeKey(String name) { return this; } + public HapiGetTokenInfo hasFreezeKey(com.hedera.hapi.node.base.Key key) { + explicitFreezeKey = key; + return this; + } + public HapiGetTokenInfo hasAdminKey(String name) { expectedAdminKey = Optional.of(name); return this; } + public HapiGetTokenInfo hasAdminKey(com.hedera.hapi.node.base.Key key) { + explicitAdminKey = key; + return this; + } + + public HapiGetTokenInfo hasPauseKey(com.hedera.hapi.node.base.Key key) { + explicitPauseKey = key; + return this; + } + public HapiGetTokenInfo hasPauseKey(String name) { expectedPauseKey = Optional.of(name); return this; @@ -277,21 +321,41 @@ public HapiGetTokenInfo hasPauseStatus(TokenPauseStatus status) { return this; } + public HapiGetTokenInfo hasKycKey(com.hedera.hapi.node.base.Key key) { + explicitKycKey = key; + return this; + } + public HapiGetTokenInfo hasKycKey(String name) { expectedKycKey = Optional.of(name); return this; } + public HapiGetTokenInfo hasSupplyKey(com.hedera.hapi.node.base.Key key) { + explicitSupplyKey = key; + return this; + } + public HapiGetTokenInfo hasSupplyKey(String name) { expectedSupplyKey = Optional.of(name); return this; } + public HapiGetTokenInfo hasWipeKey(com.hedera.hapi.node.base.Key key) { + explicitWipeKey = key; + return this; + } + public HapiGetTokenInfo hasWipeKey(String name) { expectedWipeKey = Optional.of(name); return this; } + public HapiGetTokenInfo hasFeeScheduleKey(com.hedera.hapi.node.base.Key key) { + explicitFeeScheduleKey = key; + return this; + } + public HapiGetTokenInfo hasFeeScheduleKey(String name) { expectedFeeScheduleKey = Optional.of(name); return this; @@ -474,6 +538,8 @@ protected void assertExpectationsGiven(HapiSpec spec) { assertForRemovedKey(actualInfo.getFreezeKey()); } else if (invalidFreezeKey) { assertForAllZerosInvalidKey(actualInfo.getFreezeKey()); + } else if (explicitFreezeKey != null) { + Assertions.assertEquals(fromPbj(explicitFreezeKey), actualInfo.getFreezeKey(), "Wrong token freeze key!"); } else { assertFor( actualInfo.getFreezeKey(), @@ -487,6 +553,8 @@ protected void assertExpectationsGiven(HapiSpec spec) { assertForRemovedKey(actualInfo.getAdminKey()); } else if (invalidAdminKey) { assertForAllZerosInvalidKey(actualInfo.getAdminKey()); + } else if (explicitAdminKey != null) { + Assertions.assertEquals(fromPbj(explicitAdminKey), actualInfo.getAdminKey(), "Wrong token admin key!"); } else { assertFor( actualInfo.getAdminKey(), @@ -500,6 +568,8 @@ protected void assertExpectationsGiven(HapiSpec spec) { assertForRemovedKey(actualInfo.getWipeKey()); } else if (invalidWipeKey) { assertForAllZerosInvalidKey(actualInfo.getWipeKey()); + } else if (explicitWipeKey != null) { + Assertions.assertEquals(fromPbj(explicitWipeKey), actualInfo.getWipeKey(), "Wrong token wipe key!"); } else { assertFor( actualInfo.getWipeKey(), @@ -513,6 +583,8 @@ protected void assertExpectationsGiven(HapiSpec spec) { assertForRemovedKey(actualInfo.getKycKey()); } else if (invalidKycKey) { assertForAllZerosInvalidKey(actualInfo.getKycKey()); + } else if (explicitKycKey != null) { + Assertions.assertEquals(fromPbj(explicitKycKey), actualInfo.getKycKey(), "Wrong token KYC key!"); } else { assertFor( actualInfo.getKycKey(), @@ -526,6 +598,8 @@ protected void assertExpectationsGiven(HapiSpec spec) { assertForRemovedKey(actualInfo.getSupplyKey()); } else if (invalidSupplyKey) { assertForAllZerosInvalidKey(actualInfo.getSupplyKey()); + } else if (explicitSupplyKey != null) { + Assertions.assertEquals(fromPbj(explicitSupplyKey), actualInfo.getSupplyKey(), "Wrong token supply key!"); } else { assertFor( actualInfo.getSupplyKey(), @@ -539,6 +613,9 @@ protected void assertExpectationsGiven(HapiSpec spec) { assertForRemovedKey(actualInfo.getFeeScheduleKey()); } else if (invalidFeeScheduleKey) { assertForAllZerosInvalidKey(actualInfo.getFeeScheduleKey()); + } else if (explicitFeeScheduleKey != null) { + Assertions.assertEquals( + fromPbj(explicitFeeScheduleKey), actualInfo.getFeeScheduleKey(), "Wrong token fee schedule key!"); } else { assertFor( actualInfo.getFeeScheduleKey(), @@ -552,6 +629,8 @@ protected void assertExpectationsGiven(HapiSpec spec) { assertForRemovedKey(actualInfo.getPauseKey()); } else if (invalidPauseKey) { assertForAllZerosInvalidKey(actualInfo.getPauseKey()); + } else if (explicitPauseKey != null) { + Assertions.assertEquals(fromPbj(explicitPauseKey), actualInfo.getPauseKey(), "Wrong token pause key!"); } else { assertFor( actualInfo.getPauseKey(), diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/TxnUtils.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/TxnUtils.java index 8986d4f134a2..61e55dbb3f9d 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/TxnUtils.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/TxnUtils.java @@ -38,6 +38,7 @@ import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.SUCCESS; import static com.swirlds.common.stream.LinkedObjectStreamUtilities.getPeriod; import static java.lang.System.arraycopy; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; @@ -49,9 +50,9 @@ import com.google.protobuf.TextFormat; import com.hedera.node.app.hapi.fees.usage.SigUsage; import com.hedera.node.app.hapi.utils.fee.SigValueObj; +import com.hedera.services.bdd.SpecOperation; import com.hedera.services.bdd.spec.HapiPropertySource; import com.hedera.services.bdd.spec.HapiSpec; -import com.hedera.services.bdd.spec.HapiSpecOperation; import com.hedera.services.bdd.spec.keys.KeyFactory; import com.hedera.services.bdd.spec.keys.KeyGenerator; import com.hedera.services.bdd.spec.keys.SigControl; @@ -136,7 +137,8 @@ public static Key netOf( return netOf(spec, keyName, keyShape, Optional.empty(), keyGenSupplier); } - public static void turnLoggingOff(@NonNull final HapiSpecOperation op) { + public static void turnLoggingOff(@NonNull final SpecOperation op) { + requireNonNull(op); if (op instanceof HapiTxnOp txnOp) { txnOp.noLogging(); } else if (op instanceof HapiQueryOp queryOp) { diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/TxnVerbs.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/TxnVerbs.java index 346081088b26..63d77cf8028b 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/TxnVerbs.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/TxnVerbs.java @@ -42,6 +42,7 @@ import com.esaulpaugh.headlong.abi.Address; import com.esaulpaugh.headlong.abi.Tuple; import com.google.protobuf.ByteString; +import com.hedera.services.bdd.SpecOperation; import com.hedera.services.bdd.spec.HapiPropertySource; import com.hedera.services.bdd.spec.HapiSpec; import com.hedera.services.bdd.spec.HapiSpecOperation; @@ -602,7 +603,7 @@ public static HapiSpecOperation uploadInitCode(final String... contractsNames) { public static HapiSpecOperation uploadInitCode(final Optional payer, final String... contractsNames) { return withOpContext((spec, ctxLog) -> { - List ops = new ArrayList<>(); + final List ops = new ArrayList<>(); for (String contractName : contractsNames) { final var path = getResourcePath(contractName, ".bin"); final var file = new HapiFileCreate(contractName); @@ -617,15 +618,15 @@ public static HapiSpecOperation uploadInitCode(final Optional payer, fin /** * This method enables uploading a contract bytecode with the constructor parameters (if present) appended at the end of the file * Used for ethereum create conversion when we need to pass constructor arguments - * @param contractName - * @param abi - * @param args - * @return + * @param contractName the name of the contract, which is to be deployed + * @param abi the abi of the contract + * @param args the constructor arguments + * @return HapiSpecOperation */ - public static HapiSpecOperation updateInitCodeWithConstructorArgs( + public static SpecOperation updateInitCodeWithConstructorArgs( final Optional payer, final String contractName, final String abi, final Object... args) { return withOpContext((spec, ctxLog) -> { - List ops = new ArrayList<>(); + List ops = new ArrayList<>(); final var path = getResourcePath(contractName, ".bin"); @@ -641,7 +642,7 @@ public static HapiSpecOperation updateInitCodeWithConstructorArgs( public static HapiSpecOperation uploadSingleInitCode( final String contractName, final long expiry, final String payingWith, final LongConsumer exposingTo) { return withOpContext((spec, ctxLog) -> { - List ops = new ArrayList<>(); + final List ops = new ArrayList<>(); final var path = getResourcePath(contractName, ".bin"); final var file = new HapiFileCreate(contractName) .payingWith(payingWith) @@ -657,7 +658,7 @@ public static HapiSpecOperation uploadSingleInitCode( public static HapiSpecOperation uploadSingleInitCode( final String contractName, final ResponseCodeEnum... statuses) { return withOpContext((spec, ctxLog) -> { - List ops = new ArrayList<>(); + final List ops = new ArrayList<>(); final var path = getResourcePath(contractName, ".bin"); final var file = new HapiFileCreate(contractName).hasRetryPrecheckFrom(statuses); final var updatedFile = updateLargeFile(GENESIS, contractName, extractByteCode(path)); @@ -678,7 +679,7 @@ public static HapiSpecOperation uploadSingleInitCode( public static HapiSpecOperation uploadInitCodeWithConstructorArguments( final String contractName, final String abi, final Object... args) { return withOpContext((spec, ctxLog) -> { - List ops = new ArrayList<>(); + final List ops = new ArrayList<>(); final var path = getResourcePath(contractName, ".bin"); final var file = new HapiFileCreate(contractName); diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/contract/HapiBaseContractCreate.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/contract/HapiBaseContractCreate.java index 3aa41c813837..3aff986d8fa3 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/contract/HapiBaseContractCreate.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/contract/HapiBaseContractCreate.java @@ -177,4 +177,8 @@ public long numOfCreatedContract() { .map(receipt -> receipt.getContractID().getContractNum()) .orElse(-1L); } + + public Optional getAdminKey() { + return (!omitAdminKey && !useDeprecatedAdminKey) ? Optional.of(adminKey) : Optional.empty(); + } } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/contract/HapiContractCall.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/contract/HapiContractCall.java index cca5f69b45d2..3c066443d21f 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/contract/HapiContractCall.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/contract/HapiContractCall.java @@ -17,6 +17,7 @@ package com.hedera.services.bdd.spec.transactions.contract; import static com.hedera.node.app.hapi.utils.CommonUtils.extractTransactionBody; +import static com.hedera.services.bdd.spec.assertions.TransactionRecordAsserts.recordWith; import static com.hedera.services.bdd.spec.keys.TrieSigMapGenerator.uniqueWithFullPrefixesFor; import static com.hedera.services.bdd.spec.queries.QueryVerbs.getTxnRecord; import static com.hedera.services.bdd.spec.transactions.TxnUtils.extractTxnId; @@ -27,6 +28,7 @@ import com.google.common.base.MoreObjects; import com.google.protobuf.ByteString; import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.assertions.TransactionRecordAsserts; import com.hedera.services.bdd.spec.infrastructure.meta.ActionableContractCall; import com.hedera.services.bdd.spec.transactions.TxnUtils; import com.hederahashgraph.api.proto.java.AccountID; @@ -39,6 +41,7 @@ import com.hederahashgraph.api.proto.java.TransactionBody; import com.hederahashgraph.api.proto.java.TransactionRecord; import com.swirlds.common.utility.CommonUtils; +import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.ArrayList; import java.util.Arrays; @@ -60,6 +63,9 @@ public class HapiContractCall extends HapiBaseCall { private Optional details = Optional.empty(); private Optional> paramsFn = Optional.empty(); + @Nullable + private List childStatuses; + @Nullable private Function tupleFn = null; @@ -77,6 +83,14 @@ public HapiContractCall withExplicitRawParams(final byte[] params) { return withExplicitParams(() -> CommonUtils.hex(params)); } + public HapiContractCall hasKnownStatuses(@NonNull final ResponseCodeEnum... statuses) { + if (statuses.length < 1) { + throw new IllegalArgumentException("There must be at least a parent status"); + } + childStatuses = List.of(Arrays.copyOfRange(statuses, 1, statuses.length)); + return hasKnownStatus(statuses[0]); + } + public static HapiContractCall fromDetails(String actionable) { HapiContractCall call = new HapiContractCall(); call.details = Optional.of(actionable); @@ -327,6 +341,21 @@ protected void updateStateOf(HapiSpec spec) throws Throwable { } } + @Override + protected void assertExpectationsGiven(@NonNull final HapiSpec spec) throws Throwable { + if (childStatuses != null) { + final var lastTxnId = extractTxnId(txnSubmitted); + allRunFor( + spec, + getTxnRecord(lastTxnId) + .assertingNothingAboutHashes() + .andAllChildRecords() + .hasChildRecords(childStatuses.stream() + .map(status -> recordWith().status(status)) + .toArray(TransactionRecordAsserts[]::new))); + } + } + @Override protected List> defaultSigners() { final var signers = new ArrayList>(); diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/contract/HapiContractCreate.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/contract/HapiContractCreate.java index 6aec1397128e..251aa07fa03d 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/contract/HapiContractCreate.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/contract/HapiContractCreate.java @@ -269,18 +269,7 @@ protected void updateStateOf(HapiSpec spec) throws Throwable { } spec.registry().saveKey(contract, (omitAdminKey || useDeprecatedAdminKey) ? MISSING_ADMIN_KEY : adminKey); spec.registry().saveContractId(contract, newId); - final var otherInfoBuilder = ContractGetInfoResponse.ContractInfo.newBuilder() - .setContractAccountID(solidityIdFrom(lastReceipt.getContractID())) - .setMemo(memo.orElse(spec.setup().defaultMemo())) - .setAutoRenewPeriod(Duration.newBuilder() - .setSeconds(autoRenewPeriodSecs.orElse( - spec.setup().defaultAutoRenewPeriod().getSeconds())) - .build()); - if (!omitAdminKey && !useDeprecatedAdminKey) { - otherInfoBuilder.setAdminKey(adminKey); - } - final var otherInfo = otherInfoBuilder.build(); - spec.registry().saveContractInfo(contract, otherInfo); + spec.registry().saveContractInfo(contract, infoOfCreatedContractOrThrow()); successCb.ifPresent(cb -> cb.accept(spec.registry())); if (advertiseCreation) { String banner = "\n\n" @@ -294,6 +283,27 @@ protected void updateStateOf(HapiSpec spec) throws Throwable { } } + /** + * Returns the contract info of the created contract. + * + * @return the contract info + */ + public ContractGetInfoResponse.ContractInfo infoOfCreatedContractOrThrow() { + if (lastReceipt == null || memo.isEmpty() || autoRenewPeriodSecs.isEmpty()) { + throw new IllegalStateException("Contract was not created"); + } + final var builder = ContractGetInfoResponse.ContractInfo.newBuilder() + .setContractAccountID(solidityIdFrom(lastReceipt.getContractID())) + .setMemo(memo.get()) + .setAutoRenewPeriod(Duration.newBuilder() + .setSeconds(autoRenewPeriodSecs.get()) + .build()); + if (!omitAdminKey && !useDeprecatedAdminKey) { + builder.setAdminKey(adminKey); + } + return builder.build(); + } + @Override protected Consumer opBodyDef(HapiSpec spec) throws Throwable { if (this.spec != null) { @@ -324,6 +334,13 @@ protected Consumer opBodyDef(HapiSpec spec) throws Thro ? Optional.of(encodeParametersForConstructor(args.get(), abi.get())) : Optional.empty(); } + if (memo.isEmpty()) { + memo = Optional.of(spec.setup().defaultMemo()); + } + if (autoRenewPeriodSecs.isEmpty()) { + autoRenewPeriodSecs = + Optional.of(spec.setup().defaultAutoRenewPeriod().getSeconds()); + } ContractCreateTransactionBody opBody = spec.txns() .body( ContractCreateTransactionBody.class, b -> { @@ -393,6 +410,12 @@ public long numOfCreatedContract() { .orElse(-1L); } + public long numOfCreatedContractOrThrow() { + return Optional.ofNullable(lastReceipt) + .map(receipt -> receipt.getContractID().getContractNum()) + .orElseThrow(); + } + public boolean getDeferStatusResolution() { return deferStatusResolution; } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/crypto/HapiCryptoCreate.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/crypto/HapiCryptoCreate.java index bf9b97688c6d..d0f205e6cb8d 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/crypto/HapiCryptoCreate.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/crypto/HapiCryptoCreate.java @@ -48,7 +48,6 @@ import com.hederahashgraph.api.proto.java.Key; import com.hederahashgraph.api.proto.java.RealmID; import com.hederahashgraph.api.proto.java.ShardID; -import com.hederahashgraph.api.proto.java.TokenID; import com.hederahashgraph.api.proto.java.Transaction; import com.hederahashgraph.api.proto.java.TransactionBody; import java.util.Arrays; @@ -88,7 +87,6 @@ public class HapiCryptoCreate extends HapiTxnOp { private Optional> balanceFn = Optional.empty(); private Optional maxAutomaticTokenAssociations = Optional.empty(); private Optional> newAccountIdObserver = Optional.empty(); - private final Optional> newTokenIdObserver = Optional.empty(); private Optional stakedAccountId = Optional.empty(); private Optional stakedNodeId = Optional.empty(); private boolean isDeclinedReward = false; @@ -329,7 +327,6 @@ protected void updateStateOf(final HapiSpec spec) { return; } final var createdAccountId = lastReceipt.getAccountID(); - final var createdTokenId = lastReceipt.getTokenID(); if (recharging) { spec.registry() .setRecharging(account, initialBalance.orElse(spec.setup().defaultBalance())); @@ -340,7 +337,6 @@ protected void updateStateOf(final HapiSpec spec) { spec.registry().saveKey(account, key); spec.registry().saveAccountId(account, createdAccountId); newAccountIdObserver.ifPresent(obs -> obs.accept(createdAccountId)); - newTokenIdObserver.ifPresent(obs -> obs.accept(createdTokenId)); receiverSigRequired.ifPresent(r -> spec.registry().saveSigRequirement(account, r)); Optional.ofNullable(addressObserver) .ifPresent(obs -> evmAddress.ifPresentOrElse( @@ -378,6 +374,10 @@ public long numOfCreatedAccount() { .orElse(-1L); } + public Key getKey() { + return key; + } + public HapiCryptoCreate withMatchingEvmAddress() { setEvmAddressAliasFromKey = true; return this; diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/token/HapiTokenCreate.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/token/HapiTokenCreate.java index e3ac5128192b..620c96267711 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/token/HapiTokenCreate.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/token/HapiTokenCreate.java @@ -17,6 +17,7 @@ package com.hedera.services.bdd.spec.transactions.token; import static com.hedera.node.app.hapi.fees.usage.token.TokenOpsUsageUtils.TOKEN_OPS_USAGE_UTILS; +import static com.hedera.node.app.hapi.utils.CommonUtils.extractTransactionBodyUnchecked; import static com.hedera.services.bdd.spec.HapiPropertySource.idAsHeadlongAddress; import static com.hedera.services.bdd.spec.transactions.TxnFactory.bannerWith; import static com.hedera.services.bdd.spec.transactions.TxnUtils.suFrom; @@ -30,7 +31,6 @@ import com.esaulpaugh.headlong.abi.Address; import com.google.common.base.MoreObjects; import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; import com.hedera.node.app.hapi.fees.usage.BaseTransactionMeta; import com.hedera.node.app.hapi.fees.usage.state.UsageAccumulator; import com.hedera.node.app.hapi.fees.usage.token.TokenOpsUsage; @@ -38,7 +38,9 @@ import com.hedera.node.app.hapi.utils.fee.SigValueObj; import com.hedera.services.bdd.spec.HapiPropertySource; import com.hedera.services.bdd.spec.HapiSpec; +import com.hedera.services.bdd.spec.dsl.utils.KeyMetadata; import com.hedera.services.bdd.spec.fees.AdapterUtils; +import com.hedera.services.bdd.spec.infrastructure.HapiSpecRegistry; import com.hedera.services.bdd.spec.transactions.HapiTxnOp; import com.hedera.services.bdd.spec.transactions.TxnUtils; import com.hedera.services.bdd.suites.utils.contracts.precompile.TokenKeyType; @@ -56,6 +58,7 @@ import com.hederahashgraph.api.proto.java.TokenType; import com.hederahashgraph.api.proto.java.Transaction; import com.hederahashgraph.api.proto.java.TransactionBody; +import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -357,6 +360,9 @@ protected Consumer opBodyDef(final HapiSpec spec) throw expiry.ifPresent(t -> b.setExpiry( Timestamp.newBuilder().setSeconds(t).build())); } + if (treasury.isEmpty()) { + treasury = Optional.of(spec.setup().defaultPayerName()); + } treasury.ifPresent(a -> { final var treasuryId = TxnUtils.asId(a, spec); b.setTreasury(treasuryId); @@ -410,63 +416,34 @@ protected void updateStateOf(final HapiSpec spec) { if (actualStatus != SUCCESS) { return; } - final var registry = spec.registry(); - symbol.ifPresent(s -> registry.saveSymbol(token, s)); - name.ifPresent(s -> registry.saveName(token, s)); - registry.saveMemo(token, memo.orElse("")); - registry.saveMetadata(token, metadata.orElse("")); final TokenID tokenID = lastReceipt.getTokenID(); - registry.saveTokenId(token, tokenID); - registry.saveTreasury(token, treasury.orElse(spec.setup().defaultPayerName())); createdIdObs.ifPresent(obs -> obs.accept(HapiPropertySource.asTokenString(tokenID))); Optional.ofNullable(createdAddressObs).ifPresent(obs -> obs.accept(idAsHeadlongAddress(tokenID))); - - try { - final var submittedBody = CommonUtils.extractTransactionBody(txnSubmitted); - final var op = submittedBody.getTokenCreation(); - if (op.hasKycKey()) { - registry.saveKycKey(token, op.getKycKey()); - } - if (op.hasWipeKey()) { - registry.saveWipeKey(token, op.getWipeKey()); - } - if (op.hasAdminKey()) { - registry.saveAdminKey(token, op.getAdminKey()); - } - if (op.hasSupplyKey()) { - registry.saveSupplyKey(token, op.getSupplyKey()); - } - if (op.hasFreezeKey()) { - registry.saveFreezeKey(token, op.getFreezeKey()); - } - if (op.hasFeeScheduleKey()) { - registry.saveFeeScheduleKey(token, op.getFeeScheduleKey()); - } - if (op.hasPauseKey()) { - registry.savePauseKey(token, op.getPauseKey()); - } - if (op.hasMetadataKey()) { - registry.saveMetadataKey(token, op.getMetadataKey()); - } - } catch (final InvalidProtocolBufferException impossible) { - } - + registerKeys(spec); + registerAttributes(spec); if (advertiseCreation) { final String banner = "\n\n" + bannerWith(String.format("Created token '%s' with id '0.0.%d'.", token, tokenID.getTokenNum())); log.info(banner); } if (asCallableContract) { - registry.saveContractId( - token, - ContractID.newBuilder() - .setShardNum(tokenID.getShardNum()) - .setRealmNum(tokenID.getRealmNum()) - .setContractNum(tokenID.getTokenNum()) - .build()); + spec.registry() + .saveContractId( + token, + ContractID.newBuilder() + .setShardNum(tokenID.getShardNum()) + .setRealmNum(tokenID.getRealmNum()) + .setContractNum(tokenID.getTokenNum()) + .build()); } } + public long numOfCreatedTokenOrThrow() { + return Optional.ofNullable(lastReceipt) + .map(receipt -> receipt.getTokenID().getTokenNum()) + .orElseThrow(); + } + @Override protected MoreObjects.ToStringHelper toStringHelper() { final MoreObjects.ToStringHelper helper = super.toStringHelper().add("token", token); @@ -477,4 +454,82 @@ protected MoreObjects.ToStringHelper toStringHelper() { }); return helper; } + + private void registerKeys(@NonNull final HapiSpec spec) { + if (lastReceipt == null) { + throw new IllegalStateException("Token has not been created"); + } + final var registry = spec.registry(); + final var submittedBody = extractTransactionBodyUnchecked(txnSubmitted); + final var op = submittedBody.getTokenCreation(); + if (op.hasKycKey()) { + registry.saveKycKey(token, op.getKycKey()); + } + if (op.hasWipeKey()) { + registry.saveWipeKey(token, op.getWipeKey()); + } + if (op.hasAdminKey()) { + registry.saveAdminKey(token, op.getAdminKey()); + } + if (op.hasSupplyKey()) { + registry.saveSupplyKey(token, op.getSupplyKey()); + } + if (op.hasFreezeKey()) { + registry.saveFreezeKey(token, op.getFreezeKey()); + } + if (op.hasFeeScheduleKey()) { + registry.saveFeeScheduleKey(token, op.getFeeScheduleKey()); + } + if (op.hasPauseKey()) { + registry.savePauseKey(token, op.getPauseKey()); + } + if (op.hasMetadataKey()) { + registry.saveMetadataKey(token, op.getMetadataKey()); + } + } + + public void registerAttributes(@NonNull final HapiSpec spec) { + if (lastReceipt == null || treasury.isEmpty()) { + throw new IllegalStateException("Token has not been created"); + } + final var registry = spec.registry(); + symbol.ifPresent(s -> registry.saveSymbol(token, s)); + name.ifPresent(s -> registry.saveName(token, s)); + registry.saveMemo(token, memo.orElse("")); + registry.saveMetadata(token, metadata.orElse("")); + final TokenID tokenID = lastReceipt.getTokenID(); + registry.saveTokenId(token, tokenID); + registry.saveTreasury(token, treasury.get()); + } + + public List allCreatedKeyMetadata(@NonNull final HapiSpec spec) { + final List metadata = new ArrayList<>(); + final var submittedBody = extractTransactionBodyUnchecked(txnSubmitted); + final var op = submittedBody.getTokenCreation(); + if (op.hasKycKey()) { + metadata.add(KeyMetadata.from(op.getKycKey(), spec, HapiSpecRegistry::saveKycKey)); + } + if (op.hasWipeKey()) { + metadata.add(KeyMetadata.from(op.getWipeKey(), spec, HapiSpecRegistry::saveWipeKey)); + } + if (op.hasAdminKey()) { + metadata.add(KeyMetadata.from(op.getAdminKey(), spec, HapiSpecRegistry::saveAdminKey)); + } + if (op.hasSupplyKey()) { + metadata.add(KeyMetadata.from(op.getSupplyKey(), spec, HapiSpecRegistry::saveSupplyKey)); + } + if (op.hasFreezeKey()) { + metadata.add(KeyMetadata.from(op.getFreezeKey(), spec, HapiSpecRegistry::saveFreezeKey)); + } + if (op.hasFeeScheduleKey()) { + metadata.add(KeyMetadata.from(op.getFeeScheduleKey(), spec, HapiSpecRegistry::saveFeeScheduleKey)); + } + if (op.hasPauseKey()) { + metadata.add(KeyMetadata.from(op.getPauseKey(), spec, HapiSpecRegistry::savePauseKey)); + } + if (op.hasMetadataKey()) { + metadata.add(KeyMetadata.from(op.getMetadataKey(), spec, HapiSpecRegistry::saveMetadataKey)); + } + return metadata; + } } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/token/HapiTokenUpdate.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/token/HapiTokenUpdate.java index 998f48cf2620..9f3d3c3cf7ad 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/token/HapiTokenUpdate.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/transactions/token/HapiTokenUpdate.java @@ -30,6 +30,7 @@ import com.hedera.node.app.hapi.fees.usage.token.TokenUpdateUsage; import com.hedera.services.bdd.spec.HapiSpec; import com.hedera.services.bdd.spec.fees.FeeCalculator; +import com.hedera.services.bdd.spec.infrastructure.RegistryNotFound; import com.hedera.services.bdd.spec.transactions.HapiTxnOp; import com.hedera.services.bdd.spec.transactions.TxnUtils; import com.hedera.services.bdd.suites.HapiSuite; @@ -507,6 +508,15 @@ protected Consumer opBodyDef(HapiSpec spec) throws Thro protected List> defaultSigners() { List> signers = new ArrayList<>(); signers.add(spec -> spec.registry().getKey(effectivePayer(spec))); + signers.add(spec -> { + try { + return spec.registry().getAdminKey(token); + } catch (RegistryNotFound ignore) { + // Some tests attempt to update an immutable token, + // skip the admin key if it's not found + return Key.getDefaultInstance(); + } + }); newTreasury.ifPresent(n -> signers.add((spec -> spec.registry().getKey(n)))); newAdminKey.ifPresent(n -> signers.add(spec -> spec.registry().getKey(n))); autoRenewAccount.ifPresent(a -> signers.add(spec -> spec.registry().getKey(a))); diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/CustomSpecAssert.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/CustomSpecAssert.java index 4258124722c8..e027398f1e55 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/CustomSpecAssert.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/CustomSpecAssert.java @@ -21,17 +21,16 @@ import static com.hedera.services.bdd.spec.utilops.UtilVerbs.convertHapiCallsToEthereumCalls; import static com.hedera.services.bdd.suites.HapiSuite.SECP_256K1_SOURCE_KEY; +import com.hedera.services.bdd.SpecOperation; import com.hedera.services.bdd.spec.HapiSpec; -import com.hedera.services.bdd.spec.HapiSpecOperation; import java.util.List; -import java.util.Optional; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class CustomSpecAssert extends UtilOp { static final Logger log = LogManager.getLogger(CustomSpecAssert.class); - public static void allRunFor(final HapiSpec spec, final List ops) { + public static void allRunFor(final HapiSpec spec, final List ops) { if (spec.isUsingEthCalls()) { if (!isEthereumAccountCreatedForSpec(spec)) { ops.addAll(createEthereumAccountForSpec(spec)); @@ -42,13 +41,13 @@ public static void allRunFor(final HapiSpec spec, final List } } - private static void executeHederaOps(final HapiSpec spec, final List ops) { - for (final HapiSpecOperation op : ops) { + private static void executeHederaOps(final HapiSpec spec, final List ops) { + for (final var op : ops) { handleExec(spec, op); } } - private static void executeEthereumOps(final HapiSpec spec, final List ops) { + private static void executeEthereumOps(final HapiSpec spec, final List ops) { final var convertedOps = convertHapiCallsToEthereumCalls( ops, SECP_256K1_SOURCE_KEY, @@ -60,15 +59,15 @@ private static void executeEthereumOps(final HapiSpec spec, final List error = op.execFor(spec); + public static void handleExec(final HapiSpec spec, final SpecOperation op) { + final var error = op.execFor(spec); if (error.isPresent()) { log.error("Operation '{}' :: {}", op, error.get().getMessage()); throw new IllegalStateException(error.get()); } } - public static void allRunFor(HapiSpec spec, HapiSpecOperation... ops) { + public static void allRunFor(HapiSpec spec, SpecOperation... ops) { allRunFor(spec, List.of(ops)); } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/RunnableOp.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/RunnableOp.java new file mode 100644 index 000000000000..3f75a79e162e --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/RunnableOp.java @@ -0,0 +1,41 @@ +/* + * 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.services.bdd.spec.utilops; + +import com.hedera.services.bdd.spec.HapiSpec; + +/** + * A util op that simple runs a runnable. + */ +public class RunnableOp extends UtilOp { + private final Runnable runnable; + + public RunnableOp(Runnable runnable) { + this.runnable = runnable; + } + + @Override + protected boolean submitOp(HapiSpec spec) throws Throwable { + runnable.run(); + return false; + } + + @Override + public String toString() { + return "RunnableOp"; + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/UtilStateChange.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/UtilStateChange.java index d4b9693a3b8b..7081c84baddf 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/UtilStateChange.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/UtilStateChange.java @@ -24,8 +24,8 @@ import static com.hedera.services.bdd.suites.HapiSuite.SECP_256K1_RECEIVER_SOURCE_KEY; import static com.hedera.services.bdd.suites.HapiSuite.SECP_256K1_SOURCE_KEY; +import com.hedera.services.bdd.SpecOperation; import com.hedera.services.bdd.spec.HapiSpec; -import com.hedera.services.bdd.spec.HapiSpecOperation; import com.hedera.services.bdd.spec.assertions.StateChange; import com.hedera.services.bdd.spec.assertions.StorageChange; import com.hedera.services.bdd.spec.keys.KeyShape; @@ -72,14 +72,14 @@ public static List stateChangesToGrpc(List sta return additions; } - public static List createEthereumAccountForSpec(final HapiSpec spec) { + public static List createEthereumAccountForSpec(final HapiSpec spec) { final var acc1 = createEthereumAccount(SECP_256K1_SOURCE_KEY, DEFAULT_CONTRACT_SENDER); final var acc2 = createEthereumAccount(SECP_256K1_RECEIVER_SOURCE_KEY, DEFAULT_CONTRACT_RECEIVER); specToInitializedEthereumAccount.putIfAbsent(spec.getSuitePrefix() + spec.getName(), true); return Stream.concat(acc1.stream(), acc2.stream()).toList(); } - private static List createEthereumAccount(final String secp256k1Key, final String txnName) { + private static List createEthereumAccount(final String secp256k1Key, final String txnName) { final var newSpecKey = new NewSpecKey(secp256k1Key).shape(secp256k1Shape); final var cryptoTransfer = new HapiCryptoTransfer( tinyBarsFromAccountToAlias(GENESIS, secp256k1Key, 20 * ONE_MILLION_HBARS)) diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/UtilVerbs.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/UtilVerbs.java index 5041d96482d6..f2988080d30b 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/UtilVerbs.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/UtilVerbs.java @@ -80,6 +80,7 @@ import com.esaulpaugh.headlong.abi.Address; import com.esaulpaugh.headlong.abi.Tuple; import com.google.protobuf.ByteString; +import com.hedera.services.bdd.SpecOperation; import com.hedera.services.bdd.junit.hedera.NodeSelector; import com.hedera.services.bdd.junit.support.RecordStreamValidator; import com.hedera.services.bdd.spec.HapiPropertySource; @@ -272,7 +273,7 @@ public static HapiSpecOperation validateAllLogsAfter(@NonNull final Duration del } /* Some fairly simple utility ops */ - public static InBlockingOrder blockingOrder(HapiSpecOperation... ops) { + public static InBlockingOrder blockingOrder(SpecOperation... ops) { return new InBlockingOrder(ops); } @@ -677,7 +678,7 @@ public static HapiSpecOperation resetToDefault(String... properties) { */ public static HapiSpecOperation withHeadlongAddressesFor( @NonNull final List accountOrTokens, - @NonNull final Function, List> opFn) { + @NonNull final Function, List> opFn) { return withOpContext((spec, opLog) -> { final Map addresses = new HashMap<>(); // FUTURE - populate this map @@ -695,8 +696,7 @@ public static HapiSpecOperation withHeadlongAddressesFor( * @return the operation that computes and executes the list of operations */ public static HapiSpecOperation withKeyValuesFor( - @NonNull final List keys, - @NonNull final Function, List> opFn) { + @NonNull final List keys, @NonNull final Function, List> opFn) { return withOpContext((spec, opLog) -> { final Map keyValues = new HashMap<>(); // FUTURE - populate this map @@ -856,7 +856,7 @@ public static HapiSpecOperation chunkAFile(String filePath, int chunkSize, Strin public static HapiSpecOperation chunkAFile( String filePath, int chunkSize, String payer, String topic, AtomicLong count) { return withOpContext((spec, ctxLog) -> { - List opsList = new ArrayList<>(); + List opsList = new ArrayList<>(); String overriddenFile = filePath; int overriddenChunkSize = chunkSize; String overriddenTopic = topic; @@ -1190,7 +1190,7 @@ private static void finishAppendsFor( final var appendsLeft = totalBytesLeft / bytesPerOp + Math.min(1, totalBytesLeft % bytesPerOp); final var appendsHere = new AtomicInteger(Math.min(appendsPerBurst, appendsLeft)); boolean isFirstAppend = true; - final List theBurst = new ArrayList<>(); + final List theBurst = new ArrayList<>(); final CountDownLatch burstLatch = new CountDownLatch(1); final AtomicReference burstStart = new AtomicReference<>(); while (appendsHere.getAndDecrement() > 0) { @@ -1266,7 +1266,7 @@ public static HapiSpecOperation updateLargeFile( Consumer updateCustomizer, ObjIntConsumer appendCustomizer) { return withOpContext((spec, ctxLog) -> { - List opsList = new ArrayList<>(); + List opsList = new ArrayList<>(); int fileSize = byteString.size(); int position = Math.min(BYTES_4K, fileSize); @@ -1331,7 +1331,7 @@ public static HapiSpecOperation updateLargeFileWithZeroOfferedFee( public static HapiSpecOperation contractListWithPropertiesInheritedFrom( final String contractList, final long expectedSize, final String parent) { return withOpContext((spec, ctxLog) -> { - List opsList = new ArrayList<>(); + List opsList = new ArrayList<>(); long contractListSize = spec.registry().getAmount(contractList + "Size"); Assertions.assertEquals(expectedSize, contractListSize, contractList + " has bad size!"); if (contractListSize > 1) { @@ -1711,13 +1711,13 @@ public static Tuple nftTransfer( isApproval); } - public static List convertHapiCallsToEthereumCalls( - final List ops, + public static List convertHapiCallsToEthereumCalls( + final List ops, final String privateKeyRef, final Key adminKey, final long defaultGas, final HapiSpec spec) { - final var convertedOps = new ArrayList(ops.size()); + final var convertedOps = new ArrayList(ops.size()); for (final var op : ops) { if (op instanceof HapiContractCall callOp && callOp.isConvertableToEthCall() diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/grouping/InBlockingOrder.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/grouping/InBlockingOrder.java index 328c353c2436..2782ea5d6ec9 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/grouping/InBlockingOrder.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/spec/utilops/grouping/InBlockingOrder.java @@ -18,14 +18,14 @@ import static com.hedera.services.bdd.spec.utilops.CustomSpecAssert.allRunFor; +import com.hedera.services.bdd.SpecOperation; import com.hedera.services.bdd.spec.HapiSpec; -import com.hedera.services.bdd.spec.HapiSpecOperation; import com.hedera.services.bdd.spec.utilops.UtilOp; public class InBlockingOrder extends UtilOp { - private final HapiSpecOperation[] ops; + private final SpecOperation[] ops; - public InBlockingOrder(HapiSpecOperation... ops) { + public InBlockingOrder(SpecOperation... ops) { this.ops = ops; } @@ -35,7 +35,7 @@ protected boolean submitOp(HapiSpec spec) { return false; } - public HapiSpecOperation last() { + public SpecOperation last() { return ops[ops.length - 1]; } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/HapiSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/HapiSuite.java index 726f8ca26f10..9891c2057df2 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/HapiSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/HapiSuite.java @@ -16,9 +16,11 @@ package com.hedera.services.bdd.suites; +import static com.hedera.services.bdd.spec.transactions.contract.HapiParserUtil.asHeadlongAddress; import static com.hedera.services.bdd.suites.HapiSuite.FinalOutcome.SUITE_FAILED; import static com.hedera.services.bdd.suites.HapiSuite.FinalOutcome.SUITE_PASSED; +import com.esaulpaugh.headlong.abi.Address; import com.hedera.services.bdd.spec.HapiSpec; import com.hedera.services.bdd.spec.HapiSpecOperation; import com.hedera.services.bdd.spec.HapiSpecSetup; @@ -53,6 +55,7 @@ public abstract class HapiSuite { public static final String EVM_VERSION_PROPERTY = "contracts.evm.version"; public static final String EVM_VERSION_046 = "v0.46"; public static final String EVM_VERSION_050 = "v0.50"; + public static final Address ZERO_ADDRESS = asHeadlongAddress(new byte[20]); protected static String ALICE = "ALICE"; protected static String BOB = "BOB"; protected static String CAROL = "CAROL"; diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/Utils.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/Utils.java index 4a6ee7251b4b..3c33f7aadfe4 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/Utils.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/Utils.java @@ -181,6 +181,24 @@ public static byte[] asAddressInTopic(final byte[] solidityAddress) { return topicAddress; } + /** + * Returns the bytecode of the contract by the name of the contract from the classpath resource. + * + * @param contractName the name of the contract + * @return the bytecode of the contract + * @throws IllegalArgumentException if the contract is not found + * @throws UncheckedIOException if an I/O error occurs + */ + public static ByteString getInitcodeOf(@NonNull final String contractName) { + final var path = getResourcePath(contractName, ".bin"); + try { + final var bytes = Files.readAllBytes(relocatedIfNotPresentInWorkingDir(Path.of(path))); + return ByteString.copyFrom(bytes); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + public static ByteString extractByteCode(String path) { try { final var bytes = Files.readAllBytes(relocatedIfNotPresentInWorkingDir(Path.of(path))); diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/opcodes/SStoreSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/opcodes/SStoreSuite.java index 698ff9c1eda9..63305282c31e 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/opcodes/SStoreSuite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/opcodes/SStoreSuite.java @@ -34,6 +34,7 @@ import static com.hedera.services.bdd.suites.contract.Utils.FunctionType.FUNCTION; import static com.hedera.services.bdd.suites.contract.Utils.getABIFor; +import com.hedera.services.bdd.SpecOperation; import com.hedera.services.bdd.junit.HapiTest; import com.hedera.services.bdd.spec.HapiSpecOperation; import com.hedera.services.bdd.spec.assertions.ContractFnResultAsserts; @@ -69,7 +70,7 @@ final Stream multipleSStoreOpsSucceed() { .given(uploadInitCode(contract), contractCreate(contract)) .when(withOpContext((spec, opLog) -> { final var step = 16; - List subOps = new ArrayList<>(); + final List subOps = new ArrayList<>(); for (int sizeNow = step; sizeNow < MAX_CONTRACT_STORAGE_KB; sizeNow += step) { final var subOp1 = contractCall(contract, "growTo", BigInteger.valueOf(sizeNow)) @@ -81,7 +82,7 @@ final Stream multipleSStoreOpsSucceed() { })) .then(withOpContext((spec, opLog) -> { final var numberOfIterations = 10; - List subOps = new ArrayList<>(); + final List subOps = new ArrayList<>(); for (int i = 0; i < numberOfIterations; i++) { final var subOp1 = contractCall( diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/openzeppelin/ERC1155ContractInteractions.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/openzeppelin/ERC1155ContractInteractions.java index 506dc47cf9f1..f6860f909090 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/openzeppelin/ERC1155ContractInteractions.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/openzeppelin/ERC1155ContractInteractions.java @@ -36,8 +36,8 @@ import com.google.protobuf.ByteString; import com.hedera.node.app.service.evm.utils.EthSigsUtils; +import com.hedera.services.bdd.SpecOperation; import com.hedera.services.bdd.junit.HapiTest; -import com.hedera.services.bdd.spec.HapiSpecOperation; import com.hederahashgraph.api.proto.java.ResponseCodeEnum; import java.math.BigInteger; import java.util.ArrayList; @@ -89,7 +89,7 @@ final Stream erc1155() { .getAccountInfo(ACCOUNT2 + "Info") .getContractAccountID(); - final var ops = new ArrayList(); + final var ops = new ArrayList(); /* approve for other accounts */ final var approveCall = contractCall( diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/TokenUpdatePrecompileSuite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/TokenUpdatePrecompileSuite.java deleted file mode 100644 index 88e5abcc37e1..000000000000 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/TokenUpdatePrecompileSuite.java +++ /dev/null @@ -1,1587 +0,0 @@ -/* - * Copyright (C) 2022-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.services.bdd.suites.contract.precompile; - -import static com.hedera.node.app.service.evm.utils.EthSigsUtils.recoverAddressFromPubKey; -import static com.hedera.services.bdd.junit.TestTags.SMART_CONTRACT; -import static com.hedera.services.bdd.junit.TestTags.TOKEN; -import static com.hedera.services.bdd.spec.HapiSpec.defaultHapiSpec; -import static com.hedera.services.bdd.spec.assertions.AccountInfoAsserts.accountWith; -import static com.hedera.services.bdd.spec.assertions.TransactionRecordAsserts.recordWith; -import static com.hedera.services.bdd.spec.keys.KeyShape.CONTRACT; -import static com.hedera.services.bdd.spec.keys.KeyShape.DELEGATE_CONTRACT; -import static com.hedera.services.bdd.spec.keys.KeyShape.ED25519; -import static com.hedera.services.bdd.spec.keys.KeyShape.SECP256K1; -import static com.hedera.services.bdd.spec.keys.KeyShape.sigs; -import static com.hedera.services.bdd.spec.keys.SigControl.ED25519_ON; -import static com.hedera.services.bdd.spec.keys.SigControl.OFF; -import static com.hedera.services.bdd.spec.keys.SigControl.ON; -import static com.hedera.services.bdd.spec.queries.QueryVerbs.contractCallLocal; -import static com.hedera.services.bdd.spec.queries.QueryVerbs.getAccountBalance; -import static com.hedera.services.bdd.spec.queries.QueryVerbs.getAliasedAccountInfo; -import static com.hedera.services.bdd.spec.queries.QueryVerbs.getTokenInfo; -import static com.hedera.services.bdd.spec.queries.QueryVerbs.getTokenNftInfo; -import static com.hedera.services.bdd.spec.transactions.TxnVerbs.contractCall; -import static com.hedera.services.bdd.spec.transactions.TxnVerbs.contractCreate; -import static com.hedera.services.bdd.spec.transactions.TxnVerbs.cryptoCreate; -import static com.hedera.services.bdd.spec.transactions.TxnVerbs.cryptoTransfer; -import static com.hedera.services.bdd.spec.transactions.TxnVerbs.cryptoUpdate; -import static com.hedera.services.bdd.spec.transactions.TxnVerbs.grantTokenKyc; -import static com.hedera.services.bdd.spec.transactions.TxnVerbs.mintToken; -import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenAssociate; -import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenCreate; -import static com.hedera.services.bdd.spec.transactions.TxnVerbs.tokenUpdate; -import static com.hedera.services.bdd.spec.transactions.TxnVerbs.uploadInitCode; -import static com.hedera.services.bdd.spec.transactions.token.TokenMovement.moving; -import static com.hedera.services.bdd.spec.transactions.token.TokenMovement.movingHbar; -import static com.hedera.services.bdd.spec.utilops.CustomSpecAssert.allRunFor; -import static com.hedera.services.bdd.spec.utilops.UtilVerbs.childRecordsCheck; -import static com.hedera.services.bdd.spec.utilops.UtilVerbs.emptyChildRecordsCheck; -import static com.hedera.services.bdd.spec.utilops.UtilVerbs.newKeyNamed; -import static com.hedera.services.bdd.spec.utilops.UtilVerbs.sourcing; -import static com.hedera.services.bdd.spec.utilops.UtilVerbs.withOpContext; -import static com.hedera.services.bdd.suites.HapiSuite.ONE_HBAR; -import static com.hedera.services.bdd.suites.HapiSuite.ONE_HUNDRED_HBARS; -import static com.hedera.services.bdd.suites.HapiSuite.ONE_MILLION_HBARS; -import static com.hedera.services.bdd.suites.HapiSuite.SECP_256K1_SHAPE; -import static com.hedera.services.bdd.suites.HapiSuite.SECP_256K1_SOURCE_KEY; -import static com.hedera.services.bdd.suites.HapiSuite.TOKEN_TREASURY; -import static com.hedera.services.bdd.suites.contract.Utils.asAddress; -import static com.hedera.services.bdd.suites.contract.Utils.asToken; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.BUSY; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.CONTRACT_REVERT_EXECUTED; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_SIGNATURE; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_TOKEN_ID; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.KEY_NOT_PROVIDED; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.SUCCESS; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_HAS_NO_FEE_SCHEDULE_KEY; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_HAS_NO_FREEZE_KEY; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_HAS_NO_KYC_KEY; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_HAS_NO_PAUSE_KEY; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_HAS_NO_SUPPLY_KEY; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_HAS_NO_WIPE_KEY; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_IS_IMMUTABLE; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_NAME_TOO_LONG; -import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_SYMBOL_TOO_LONG; -import static com.hederahashgraph.api.proto.java.TokenType.FUNGIBLE_COMMON; -import static com.hederahashgraph.api.proto.java.TokenType.NON_FUNGIBLE_UNIQUE; - -import com.google.protobuf.ByteString; -import com.hedera.services.bdd.junit.HapiTest; -import com.hedera.services.bdd.spec.assertions.TransactionRecordAsserts; -import com.hedera.services.bdd.spec.keys.KeyShape; -import com.hedera.services.bdd.spec.transactions.TxnUtils; -import com.hedera.services.bdd.spec.transactions.contract.HapiParserUtil; -import com.hederahashgraph.api.proto.java.AccountID; -import com.hederahashgraph.api.proto.java.TokenID; -import com.hederahashgraph.api.proto.java.TokenPauseStatus; -import com.hederahashgraph.api.proto.java.TokenSupplyType; -import com.hederahashgraph.api.proto.java.TokenType; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Stream; -import org.junit.jupiter.api.DynamicTest; -import org.junit.jupiter.api.Tag; - -@Tag(SMART_CONTRACT) -public class TokenUpdatePrecompileSuite { - private static final long GAS_TO_OFFER = 4_000_000L; - private static final long AUTO_RENEW_PERIOD = 8_000_000L; - private static final String ACCOUNT = "account"; - private static final String ACCOUNT_TO_ASSOCIATE = "account3"; - private static final String ACCOUNT_TO_ASSOCIATE_KEY = "associateKey"; - private static final String AUTO_RENEW_ACCOUNT = "autoRenewAccount"; - private static final String VANILLA_TOKEN = "TokenD"; - private static final String NFT_TOKEN = "TokenD"; - private static final String MULTI_KEY = "multiKey"; - private static final String NEW_KEY = "newKey"; - private static final String CONTRACT_KEY = "contractKey"; - private static final KeyShape THRESHOLD_ED_AND_CONTRACT = KeyShape.threshOf(1, ED25519, CONTRACT); - private static final KeyShape THRESHOLD_ED2_AND_CONTRACT = KeyShape.threshOf(1, ED25519, ED25519, CONTRACT); - private static final String UPDATE_KEY_FUNC = "tokenUpdateKeys"; - private static final String GET_KEY_FUNC = "getKeyFromToken"; - public static final String TOKEN_UPDATE_CONTRACT = "UpdateTokenInfoContract"; - private static final String UPDATE_TXN = "updateTxn"; - private static final String NO_ADMIN_KEY = "noAdminKeyTxn"; - private static final long DEFAULT_AMOUNT_TO_SEND = 20 * ONE_HBAR; - private static final String ED25519KEY = "ed25519key"; - private static final String ECDSA_KEY = "ecdsa"; - public static final String TOKEN_UPDATE_AS_KEY = "tokenCreateContractAsKey"; - private static final String DELEGATE_KEY = "tokenUpdateAsKeyDelegate"; - public static final String CUSTOM_NAME = "customName"; - public static final String CUSTOM_SYMBOL = "Ω"; - public static final String CUSTOM_MEMO = "Omega"; - private static final long ADMIN_KEY_TYPE = 1L; - private static final long SUPPLY_KEY_TYPE = 16L; - private static final KeyShape KEY_SHAPE = KeyShape.threshOf(1, ED25519, CONTRACT); - - @HapiTest - public Stream updateNftTreasuryWithAndWithoutAdminKey() { - final var newTokenTreasury = "newTokenTreasury"; - final var NO_ADMIN_TOKEN = "noAdminKeyToken"; - final AtomicReference noAdminKeyToken = new AtomicReference<>(); - final AtomicReference nftToken = new AtomicReference<>(); - return defaultHapiSpec("updateNftTreasuryWithAndWithoutAdminKey") - .given( - cryptoCreate(TOKEN_TREASURY), - cryptoCreate(newTokenTreasury).keyShape(ED25519_ON).maxAutomaticTokenAssociations(6), - newKeyNamed(MULTI_KEY).shape(ED25519_ON), - cryptoCreate(ACCOUNT).key(MULTI_KEY).balance(ONE_MILLION_HBARS), - uploadInitCode(TOKEN_UPDATE_CONTRACT), - contractCreate(TOKEN_UPDATE_CONTRACT) - .autoRenewAccountId(ACCOUNT) - .gas(GAS_TO_OFFER), - tokenCreate(NO_ADMIN_TOKEN) - .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .treasury(TOKEN_TREASURY) - .initialSupply(0) - .supplyKey(MULTI_KEY) - .exposingCreatedIdTo(id -> noAdminKeyToken.set(asToken(id))), - tokenCreate(VANILLA_TOKEN) - .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .treasury(TOKEN_TREASURY) - .initialSupply(0) - .adminKey(MULTI_KEY) - .supplyKey(MULTI_KEY) - .pauseKey(MULTI_KEY) - .exposingCreatedIdTo(id -> nftToken.set(asToken(id))), - mintToken(VANILLA_TOKEN, List.of(ByteString.copyFromUtf8("nft0"))), - tokenAssociate(newTokenTreasury, VANILLA_TOKEN), - mintToken(NO_ADMIN_TOKEN, List.of(ByteString.copyFromUtf8("nft1")))) - .when(withOpContext((spec, opLog) -> allRunFor( - spec, - newKeyNamed(CONTRACT_KEY) - .shape(THRESHOLD_ED_AND_CONTRACT.signedWith(sigs(ED25519_ON, TOKEN_UPDATE_CONTRACT))), - tokenUpdate(VANILLA_TOKEN).adminKey(CONTRACT_KEY).signedByPayerAnd(MULTI_KEY, CONTRACT_KEY), - cryptoUpdate(ACCOUNT).key(CONTRACT_KEY), - cryptoUpdate(newTokenTreasury).key(CONTRACT_KEY), - contractCall( - TOKEN_UPDATE_CONTRACT, - "updateTokenTreasury", - HapiParserUtil.asHeadlongAddress(asAddress(noAdminKeyToken.get())), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getAccountID(newTokenTreasury)))) - .via("noAdminKey") - .gas(GAS_TO_OFFER) - .sending(DEFAULT_AMOUNT_TO_SEND) - .signedBy(ACCOUNT, newTokenTreasury) - .payingWith(ACCOUNT) - .alsoSigningWithFullPrefix(ACCOUNT, newTokenTreasury) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED), - contractCall( - TOKEN_UPDATE_CONTRACT, - "updateTokenTreasury", - HapiParserUtil.asHeadlongAddress(asAddress(nftToken.get())), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getAccountID(newTokenTreasury)))) - .via("tokenUpdateTxn") - .gas(GAS_TO_OFFER) - .sending(DEFAULT_AMOUNT_TO_SEND) - .signedBy(CONTRACT_KEY) - .payingWith(ACCOUNT) - .alsoSigningWithFullPrefix(CONTRACT_KEY)))) - .then( - childRecordsCheck( - "noAdminKey", - CONTRACT_REVERT_EXECUTED, - TransactionRecordAsserts.recordWith().status(TOKEN_IS_IMMUTABLE)), - getTokenNftInfo(VANILLA_TOKEN, 1) - .hasAccountID(newTokenTreasury) - .logged(), - getAccountBalance(TOKEN_TREASURY).hasTokenBalance(VANILLA_TOKEN, 0), - getAccountBalance(newTokenTreasury).hasTokenBalance(VANILLA_TOKEN, 1), - getTokenInfo(VANILLA_TOKEN) - .hasTreasury(newTokenTreasury) - .hasPauseStatus(TokenPauseStatus.Unpaused) - .logged(), - getTokenNftInfo(VANILLA_TOKEN, 1) - .hasAccountID(newTokenTreasury) - .logged()); - } - - @HapiTest - public Stream updateWithTooLongNameAndSymbol() { - final var tooLongString = "ORIGINAL" + TxnUtils.randomUppercase(101); - final var tooLongSymbolTxn = "tooLongSymbolTxn"; - final AtomicReference vanillaTokenID = new AtomicReference<>(); - return defaultHapiSpec("updateWithTooLongNameAndSymbol") - .given( - cryptoCreate(TOKEN_TREASURY), - newKeyNamed(MULTI_KEY).shape(ED25519_ON), - cryptoCreate(ACCOUNT).key(MULTI_KEY).balance(ONE_MILLION_HBARS), - uploadInitCode(TOKEN_UPDATE_CONTRACT), - contractCreate(TOKEN_UPDATE_CONTRACT).gas(GAS_TO_OFFER), - tokenCreate(VANILLA_TOKEN) - .tokenType(FUNGIBLE_COMMON) - .treasury(TOKEN_TREASURY) - .initialSupply(1000) - .adminKey(MULTI_KEY) - .supplyKey(MULTI_KEY) - .pauseKey(MULTI_KEY) - .exposingCreatedIdTo(id -> vanillaTokenID.set(asToken(id))), - tokenAssociate(ACCOUNT, VANILLA_TOKEN)) - .when(withOpContext((spec, opLog) -> allRunFor( - spec, - newKeyNamed(CONTRACT_KEY) - .shape(THRESHOLD_ED_AND_CONTRACT.signedWith(sigs(ON, TOKEN_UPDATE_CONTRACT))), - tokenUpdate(VANILLA_TOKEN).adminKey(CONTRACT_KEY).signedByPayerAnd(MULTI_KEY, CONTRACT_KEY), - cryptoUpdate(ACCOUNT).key(CONTRACT_KEY), - contractCall( - TOKEN_UPDATE_CONTRACT, - "checkNameAndSymbolLength", - HapiParserUtil.asHeadlongAddress(asAddress(vanillaTokenID.get())), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getAccountID(ACCOUNT))), - tooLongString, - CUSTOM_SYMBOL) - .via(UPDATE_TXN) - .gas(GAS_TO_OFFER) - .sending(DEFAULT_AMOUNT_TO_SEND) - .signedBy(ACCOUNT) - .payingWith(ACCOUNT) - .alsoSigningWithFullPrefix(ACCOUNT) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED), - contractCall( - TOKEN_UPDATE_CONTRACT, - "checkNameAndSymbolLength", - HapiParserUtil.asHeadlongAddress(asAddress(vanillaTokenID.get())), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getAccountID(ACCOUNT))), - CUSTOM_NAME, - tooLongString) - .via(tooLongSymbolTxn) - .gas(GAS_TO_OFFER) - .sending(DEFAULT_AMOUNT_TO_SEND) - .signedBy(ACCOUNT) - .alsoSigningWithFullPrefix(ACCOUNT) - .payingWith(ACCOUNT) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED)))) - .then(withOpContext((spec, opLog) -> allRunFor( - spec, - childRecordsCheck( - UPDATE_TXN, - CONTRACT_REVERT_EXECUTED, - TransactionRecordAsserts.recordWith().status(TOKEN_NAME_TOO_LONG)), - childRecordsCheck( - tooLongSymbolTxn, - CONTRACT_REVERT_EXECUTED, - TransactionRecordAsserts.recordWith().status(TOKEN_SYMBOL_TOO_LONG))))); - } - - @HapiTest - final Stream updateTokenWithKeysNegative() { - final var updateTokenWithKeysFunc = "updateTokenWithKeys"; - final var NO_FEE_SCHEDULE_KEY_TXN = "NO_FEE_SCHEDULE_KEY_TXN"; - final var NO_PAUSE_KEY_TXN = "NO_PAUSE_KEY_TXN"; - final var NO_KYC_KEY_TXN = "NO_KYC_KEY_TXN"; - final var NO_WIPE_KEY_TXN = "NO_WIPE_KEY_TXN"; - final var NO_FREEZE_KEY_TXN = "NO_FREEZE_KEY_TXN"; - final var NO_SUPPLY_KEY_TXN = "NO_SUPPLY_KEY_TXN"; - final List> tokenList = new ArrayList<>(); - final AtomicReference vanillaTokenID = new AtomicReference<>(); - final AtomicReference ed25519 = new AtomicReference<>(); - return defaultHapiSpec("updateTokenWithKeysNegative") - .given( - newKeyNamed(ED25519KEY).shape(ED25519), - newKeyNamed(ECDSA_KEY).shape(SECP256K1), - newKeyNamed(ACCOUNT_TO_ASSOCIATE_KEY), - newKeyNamed(MULTI_KEY).shape(ED25519_ON), - cryptoCreate(TOKEN_TREASURY), - cryptoCreate(ACCOUNT) - .balance(ONE_MILLION_HBARS) - .key(MULTI_KEY) - .maxAutomaticTokenAssociations(100), - cryptoCreate(ACCOUNT_TO_ASSOCIATE).key(ACCOUNT_TO_ASSOCIATE_KEY), - uploadInitCode(TOKEN_UPDATE_CONTRACT), - contractCreate(TOKEN_UPDATE_CONTRACT).gas(GAS_TO_OFFER), - tokenCreate(VANILLA_TOKEN) - .tokenType(FUNGIBLE_COMMON) - .treasury(TOKEN_TREASURY) - .adminKey(MULTI_KEY) - .exposingCreatedIdTo(id -> vanillaTokenID.set(asToken(id))), - tokenCreate(VANILLA_TOKEN) - .tokenType(FUNGIBLE_COMMON) - .treasury(TOKEN_TREASURY) - .adminKey(MULTI_KEY) - .feeScheduleKey(MULTI_KEY) - .exposingCreatedIdTo(id -> tokenList.add(new AtomicReference<>(asToken(id)))), - tokenCreate(VANILLA_TOKEN) - .tokenType(FUNGIBLE_COMMON) - .treasury(TOKEN_TREASURY) - .adminKey(MULTI_KEY) - .feeScheduleKey(MULTI_KEY) - .supplyKey(MULTI_KEY) - .exposingCreatedIdTo(id -> tokenList.add(new AtomicReference<>(asToken(id)))), - tokenCreate(VANILLA_TOKEN) - .tokenType(FUNGIBLE_COMMON) - .treasury(TOKEN_TREASURY) - .adminKey(MULTI_KEY) - .feeScheduleKey(MULTI_KEY) - .supplyKey(MULTI_KEY) - .wipeKey(MULTI_KEY) - .exposingCreatedIdTo(id -> tokenList.add(new AtomicReference<>(asToken(id)))), - tokenCreate(VANILLA_TOKEN) - .tokenType(FUNGIBLE_COMMON) - .treasury(TOKEN_TREASURY) - .adminKey(MULTI_KEY) - .pauseKey(MULTI_KEY) - .feeScheduleKey(MULTI_KEY) - .supplyKey(MULTI_KEY) - .wipeKey(MULTI_KEY) - .exposingCreatedIdTo(id -> tokenList.add(new AtomicReference<>(asToken(id)))), - tokenCreate(VANILLA_TOKEN) - .tokenType(FUNGIBLE_COMMON) - .treasury(TOKEN_TREASURY) - .freezeKey(MULTI_KEY) - .adminKey(MULTI_KEY) - .pauseKey(MULTI_KEY) - .feeScheduleKey(MULTI_KEY) - .supplyKey(MULTI_KEY) - .wipeKey(MULTI_KEY) - .exposingCreatedIdTo(id -> tokenList.add(new AtomicReference<>(asToken(id)))), - newKeyNamed(CONTRACT_KEY) - .shape(THRESHOLD_ED2_AND_CONTRACT.signedWith(sigs(ON, OFF, TOKEN_UPDATE_CONTRACT))) - .exposingKeyTo(key -> { - ed25519.set(key.getThresholdKey() - .getKeys() - .getKeys(0) - .getEd25519() - .toByteArray()); - })) - .when(withOpContext((spec, opLog) -> allRunFor( - spec, - tokenUpdate(VANILLA_TOKEN).adminKey(CONTRACT_KEY).signedByPayerAnd(MULTI_KEY, CONTRACT_KEY), - cryptoUpdate(ACCOUNT).key(CONTRACT_KEY), - contractCall( - TOKEN_UPDATE_CONTRACT, - updateTokenWithKeysFunc, - HapiParserUtil.asHeadlongAddress(asAddress(vanillaTokenID.get())), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getAccountID(ACCOUNT))), - ed25519.get(), - spec.registry() - .getKey(ECDSA_KEY) - .getECDSASecp256K1() - .toByteArray(), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getContractId(TOKEN_UPDATE_CONTRACT)))) - .via(NO_FEE_SCHEDULE_KEY_TXN) - .gas(GAS_TO_OFFER) - .sending(DEFAULT_AMOUNT_TO_SEND) - .signedBy(ACCOUNT) - .alsoSigningWithFullPrefix(ACCOUNT) - .payingWith(ACCOUNT) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED), - contractCall( - TOKEN_UPDATE_CONTRACT, - updateTokenWithKeysFunc, - HapiParserUtil.asHeadlongAddress( - asAddress(tokenList.get(0).get())), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getAccountID(ACCOUNT))), - ed25519.get(), - spec.registry() - .getKey(ECDSA_KEY) - .getECDSASecp256K1() - .toByteArray(), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getContractId(TOKEN_UPDATE_CONTRACT)))) - .via(NO_SUPPLY_KEY_TXN) - .gas(GAS_TO_OFFER) - .sending(DEFAULT_AMOUNT_TO_SEND) - .signedBy(ACCOUNT) - .alsoSigningWithFullPrefix(ACCOUNT) - .payingWith(ACCOUNT) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED), - contractCall( - TOKEN_UPDATE_CONTRACT, - updateTokenWithKeysFunc, - HapiParserUtil.asHeadlongAddress( - asAddress(tokenList.get(1).get())), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getAccountID(ACCOUNT))), - ed25519.get(), - spec.registry() - .getKey(ECDSA_KEY) - .getECDSASecp256K1() - .toByteArray(), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getContractId(TOKEN_UPDATE_CONTRACT)))) - .via(NO_WIPE_KEY_TXN) - .gas(GAS_TO_OFFER) - .sending(DEFAULT_AMOUNT_TO_SEND) - .signedBy(ACCOUNT) - .alsoSigningWithFullPrefix(ACCOUNT) - .payingWith(ACCOUNT) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED), - contractCall( - TOKEN_UPDATE_CONTRACT, - updateTokenWithKeysFunc, - HapiParserUtil.asHeadlongAddress( - asAddress(tokenList.get(2).get())), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getAccountID(ACCOUNT))), - ed25519.get(), - spec.registry() - .getKey(ECDSA_KEY) - .getECDSASecp256K1() - .toByteArray(), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getContractId(TOKEN_UPDATE_CONTRACT)))) - .via(NO_PAUSE_KEY_TXN) - .gas(GAS_TO_OFFER) - .sending(DEFAULT_AMOUNT_TO_SEND) - .signedBy(ACCOUNT) - .alsoSigningWithFullPrefix(ACCOUNT) - .payingWith(ACCOUNT) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED), - contractCall( - TOKEN_UPDATE_CONTRACT, - updateTokenWithKeysFunc, - HapiParserUtil.asHeadlongAddress( - asAddress(tokenList.get(3).get())), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getAccountID(ACCOUNT))), - ed25519.get(), - spec.registry() - .getKey(ECDSA_KEY) - .getECDSASecp256K1() - .toByteArray(), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getContractId(TOKEN_UPDATE_CONTRACT)))) - .via(NO_FREEZE_KEY_TXN) - .gas(GAS_TO_OFFER) - .sending(DEFAULT_AMOUNT_TO_SEND) - .signedBy(ACCOUNT) - .alsoSigningWithFullPrefix(ACCOUNT) - .payingWith(ACCOUNT) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED), - contractCall( - TOKEN_UPDATE_CONTRACT, - updateTokenWithKeysFunc, - HapiParserUtil.asHeadlongAddress( - asAddress(tokenList.get(4).get())), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getAccountID(ACCOUNT))), - ed25519.get(), - spec.registry() - .getKey(ECDSA_KEY) - .getECDSASecp256K1() - .toByteArray(), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getContractId(TOKEN_UPDATE_CONTRACT)))) - .via(NO_KYC_KEY_TXN) - .gas(GAS_TO_OFFER) - .sending(DEFAULT_AMOUNT_TO_SEND) - .signedBy(ACCOUNT) - .alsoSigningWithFullPrefix(ACCOUNT) - .payingWith(ACCOUNT) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED)))) - .then(withOpContext((spec, ignore) -> allRunFor( - spec, - childRecordsCheck( - NO_FEE_SCHEDULE_KEY_TXN, - CONTRACT_REVERT_EXECUTED, - TransactionRecordAsserts.recordWith().status(TOKEN_HAS_NO_FEE_SCHEDULE_KEY)), - childRecordsCheck( - NO_SUPPLY_KEY_TXN, - CONTRACT_REVERT_EXECUTED, - TransactionRecordAsserts.recordWith().status(TOKEN_HAS_NO_SUPPLY_KEY)), - childRecordsCheck( - NO_WIPE_KEY_TXN, - CONTRACT_REVERT_EXECUTED, - TransactionRecordAsserts.recordWith().status(TOKEN_HAS_NO_WIPE_KEY)), - childRecordsCheck( - NO_PAUSE_KEY_TXN, - CONTRACT_REVERT_EXECUTED, - TransactionRecordAsserts.recordWith().status(TOKEN_HAS_NO_PAUSE_KEY)), - childRecordsCheck( - NO_FREEZE_KEY_TXN, - CONTRACT_REVERT_EXECUTED, - TransactionRecordAsserts.recordWith().status(TOKEN_HAS_NO_FREEZE_KEY)), - childRecordsCheck( - NO_KYC_KEY_TXN, - CONTRACT_REVERT_EXECUTED, - TransactionRecordAsserts.recordWith().status(TOKEN_HAS_NO_KYC_KEY))))); - } - - @HapiTest - final Stream updateTokenWithInvalidKeyValues() { - final AtomicReference vanillaTokenID = new AtomicReference<>(); - return defaultHapiSpec("updateTokenWithInvalidKeyValues") - .given( - newKeyNamed(ED25519KEY).shape(ED25519), - newKeyNamed(ECDSA_KEY).shape(SECP256K1), - newKeyNamed(ACCOUNT_TO_ASSOCIATE_KEY), - newKeyNamed(MULTI_KEY).shape(ED25519_ON), - cryptoCreate(TOKEN_TREASURY), - cryptoCreate(ACCOUNT).balance(ONE_MILLION_HBARS).key(MULTI_KEY), - cryptoCreate(ACCOUNT_TO_ASSOCIATE).key(ACCOUNT_TO_ASSOCIATE_KEY), - uploadInitCode(TOKEN_UPDATE_CONTRACT), - contractCreate(TOKEN_UPDATE_CONTRACT).gas(GAS_TO_OFFER), - tokenCreate(VANILLA_TOKEN) - .tokenType(FUNGIBLE_COMMON) - .treasury(TOKEN_TREASURY) - .adminKey(MULTI_KEY) - .supplyKey(MULTI_KEY) - .feeScheduleKey(MULTI_KEY) - .pauseKey(MULTI_KEY) - .wipeKey(MULTI_KEY) - .freezeKey(MULTI_KEY) - .kycKey(MULTI_KEY) - .initialSupply(1_000) - .exposingCreatedIdTo(id -> vanillaTokenID.set(asToken(id))), - tokenAssociate(ACCOUNT, VANILLA_TOKEN), - grantTokenKyc(VANILLA_TOKEN, ACCOUNT), - cryptoTransfer(moving(500, VANILLA_TOKEN).between(TOKEN_TREASURY, ACCOUNT))) - .when(withOpContext((spec, opLog) -> allRunFor( - spec, - contractCall( - TOKEN_UPDATE_CONTRACT, - "updateTokenWithInvalidKeyValues", - HapiParserUtil.asHeadlongAddress(asAddress(vanillaTokenID.get())), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getAccountID(ACCOUNT))), - AUTO_RENEW_PERIOD) - .via(UPDATE_TXN) - .gas(GAS_TO_OFFER) - .sending(DEFAULT_AMOUNT_TO_SEND) - .hasRetryPrecheckFrom(BUSY) - .payingWith(ACCOUNT) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED), - newKeyNamed(DELEGATE_KEY).shape(DELEGATE_CONTRACT.signedWith(TOKEN_UPDATE_CONTRACT)), - newKeyNamed(TOKEN_UPDATE_AS_KEY).shape(CONTRACT.signedWith(TOKEN_UPDATE_CONTRACT))))) - .then(sourcing(() -> emptyChildRecordsCheck(UPDATE_TXN, CONTRACT_REVERT_EXECUTED))); - } - - @HapiTest - final Stream updateNftTokenKeysWithWrongTokenIdAndMissingAdminKey() { - final AtomicReference nftToken = new AtomicReference<>(); - return defaultHapiSpec("updateNftTokenKeysWithWrongTokenIdAndMissingAdminKey") - .given( - cryptoCreate(TOKEN_TREASURY), - newKeyNamed(ED25519KEY).shape(ED25519), - newKeyNamed(ECDSA_KEY).shape(SECP256K1), - newKeyNamed(ACCOUNT_TO_ASSOCIATE_KEY), - newKeyNamed(MULTI_KEY).shape(ED25519_ON), - cryptoCreate(ACCOUNT).key(MULTI_KEY).balance(ONE_MILLION_HBARS), - cryptoCreate(ACCOUNT_TO_ASSOCIATE).key(ACCOUNT_TO_ASSOCIATE_KEY), - uploadInitCode(TOKEN_UPDATE_CONTRACT), - contractCreate(TOKEN_UPDATE_CONTRACT).gas(GAS_TO_OFFER), - tokenCreate(NFT_TOKEN) - .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .treasury(TOKEN_TREASURY) - .initialSupply(0) - .supplyKey(MULTI_KEY) - .feeScheduleKey(MULTI_KEY) - .pauseKey(MULTI_KEY) - .wipeKey(MULTI_KEY) - .freezeKey(MULTI_KEY) - .kycKey(MULTI_KEY) - .exposingCreatedIdTo(id -> nftToken.set(asToken(id))), - mintToken(NFT_TOKEN, List.of(ByteString.copyFromUtf8("nft4"))), - tokenAssociate(ACCOUNT, NFT_TOKEN)) - .when(withOpContext((spec, opLog) -> allRunFor( - spec, - contractCall( - TOKEN_UPDATE_CONTRACT, - UPDATE_KEY_FUNC, - HapiParserUtil.asHeadlongAddress(new byte[20]), - spec.registry() - .getKey(ED25519KEY) - .getEd25519() - .toByteArray(), - spec.registry() - .getKey(ECDSA_KEY) - .getECDSASecp256K1() - .toByteArray(), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getContractId(TOKEN_UPDATE_CONTRACT)))) - .via(UPDATE_TXN) - .gas(GAS_TO_OFFER) - .sending(DEFAULT_AMOUNT_TO_SEND) - .hasRetryPrecheckFrom(BUSY) - .payingWith(ACCOUNT) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED), - contractCall( - TOKEN_UPDATE_CONTRACT, - UPDATE_KEY_FUNC, - HapiParserUtil.asHeadlongAddress(asAddress(nftToken.get())), - spec.registry() - .getKey(ED25519KEY) - .getEd25519() - .toByteArray(), - spec.registry() - .getKey(ECDSA_KEY) - .getECDSASecp256K1() - .toByteArray(), - HapiParserUtil.asHeadlongAddress( - asAddress(spec.registry().getContractId(TOKEN_UPDATE_CONTRACT)))) - .via(NO_ADMIN_KEY) - .gas(GAS_TO_OFFER) - .sending(DEFAULT_AMOUNT_TO_SEND) - .hasRetryPrecheckFrom(BUSY) - .payingWith(ACCOUNT) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED)))) - .then(withOpContext((spec, opLog) -> allRunFor( - spec, - childRecordsCheck( - UPDATE_TXN, - CONTRACT_REVERT_EXECUTED, - recordWith().status(INVALID_TOKEN_ID)), - childRecordsCheck( - NO_ADMIN_KEY, - CONTRACT_REVERT_EXECUTED, - recordWith().status(TOKEN_IS_IMMUTABLE))))); - } - - @HapiTest - final Stream getTokenKeyForNonFungibleNegative() { - final AtomicReference nftToken = new AtomicReference<>(); - return defaultHapiSpec("getTokenKeyForNonFungibleNegative") - .given( - cryptoCreate(TOKEN_TREASURY), - newKeyNamed(MULTI_KEY).shape(ED25519_ON), - cryptoCreate(ACCOUNT).key(MULTI_KEY).balance(ONE_MILLION_HBARS), - uploadInitCode(TOKEN_UPDATE_CONTRACT), - contractCreate(TOKEN_UPDATE_CONTRACT).gas(GAS_TO_OFFER), - tokenCreate(NFT_TOKEN) - .tokenType(TokenType.NON_FUNGIBLE_UNIQUE) - .treasury(TOKEN_TREASURY) - .initialSupply(0) - .supplyKey(MULTI_KEY) - .exposingCreatedIdTo(id -> nftToken.set(asToken(id))), - mintToken(NFT_TOKEN, List.of(ByteString.copyFromUtf8("nft5"))), - tokenAssociate(ACCOUNT, VANILLA_TOKEN)) - .when(withOpContext((spec, opLog) -> allRunFor( - spec, - contractCallLocal( - TOKEN_UPDATE_CONTRACT, - GET_KEY_FUNC, - HapiParserUtil.asHeadlongAddress(asAddress(nftToken.get())), - BigInteger.valueOf(SUPPLY_KEY_TYPE)), - contractCall( - TOKEN_UPDATE_CONTRACT, - GET_KEY_FUNC, - HapiParserUtil.asHeadlongAddress(asAddress(nftToken.get())), - BigInteger.valueOf(89L)) - .via("Invalid_Key_Type") - .hasKnownStatus(CONTRACT_REVERT_EXECUTED), - contractCall( - TOKEN_UPDATE_CONTRACT, - GET_KEY_FUNC, - HapiParserUtil.asHeadlongAddress(new byte[20]), - BigInteger.valueOf(SUPPLY_KEY_TYPE)) - .via("InvalidTokenId") - .hasKnownStatus(CONTRACT_REVERT_EXECUTED), - contractCall( - TOKEN_UPDATE_CONTRACT, - GET_KEY_FUNC, - HapiParserUtil.asHeadlongAddress(asAddress(nftToken.get())), - BigInteger.valueOf(ADMIN_KEY_TYPE)) - .via(NO_ADMIN_KEY) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED)))) - .then(withOpContext((spec, opLog) -> allRunFor( - spec, - childRecordsCheck( - "InvalidTokenId", - CONTRACT_REVERT_EXECUTED, - recordWith().status(INVALID_TOKEN_ID)), - childRecordsCheck( - NO_ADMIN_KEY, - CONTRACT_REVERT_EXECUTED, - recordWith().status(KEY_NOT_PROVIDED) - // .contractCallResult(ContractFnResultAsserts.resultWith() - // - // .contractCallResult(htsPrecompileResult() - // - // .forFunction(FunctionType.HAPI_GET_TOKEN_KEY) - // .withStatus(KEY_NOT_PROVIDED) - // - // .withTokenKeyValue(Key.newBuilder().build())))) - )))); - } - - @HapiTest - final Stream tokenUpdateSingleFieldCases() { - final var tokenInfoUpdateContract = "TokenInfoSingularUpdate"; - final var newTokenTreasury = "new treasury"; - final var oldName = "Old Name"; - final var sym = "SYM"; - final var memo = "Memo"; - final var newName = "New Name"; - final var sym1 = "SYM1"; - final var newMemo = "New Memo"; - final var updateToEdKey = "updateTokenKeyEd"; - final var updateToContractId = "updateTokenKeyContractId"; - final var contractIdKey = "contractIdKey"; - final AtomicReference token = new AtomicReference<>(); - final AtomicReference newTreasury = new AtomicReference<>(); - final AtomicReference autoRenewAccount = new AtomicReference<>(); - return defaultHapiSpec("tokenUpdateNegativeCases") - .given( - cryptoCreate(TOKEN_TREASURY), - uploadInitCode(tokenInfoUpdateContract), - contractCreate(tokenInfoUpdateContract).gas(GAS_TO_OFFER), - newKeyNamed(MULTI_KEY) - .shape(THRESHOLD_ED_AND_CONTRACT.signedWith(sigs(ON, tokenInfoUpdateContract))), - newKeyNamed(ED25519KEY).shape(ED25519), - newKeyNamed(contractIdKey).shape(CONTRACT.signedWith(tokenInfoUpdateContract)), - cryptoCreate(ACCOUNT).balance(ONE_MILLION_HBARS).key(MULTI_KEY), - cryptoCreate(newTokenTreasury).key(MULTI_KEY).exposingCreatedIdTo(newTreasury::set), - cryptoCreate(AUTO_RENEW_ACCOUNT) - .balance(ONE_MILLION_HBARS) - .key(MULTI_KEY) - .exposingCreatedIdTo(autoRenewAccount::set), - tokenCreate(TOKEN) - .tokenType(FUNGIBLE_COMMON) - .name(oldName) - .symbol(sym) - .entityMemo(memo) - .autoRenewAccount(ACCOUNT) - .autoRenewPeriod(AUTO_RENEW_PERIOD) - .supplyType(TokenSupplyType.INFINITE) - .treasury(TOKEN_TREASURY) - .adminKey(MULTI_KEY) - .supplyKey(MULTI_KEY) - .feeScheduleKey(MULTI_KEY) - .pauseKey(MULTI_KEY) - .wipeKey(MULTI_KEY) - .freezeKey(MULTI_KEY) - .kycKey(MULTI_KEY) - .initialSupply(1_000) - .exposingCreatedIdTo(id -> token.set(asToken(id))), - tokenAssociate(ACCOUNT, TOKEN), - tokenAssociate(AUTO_RENEW_ACCOUNT, TOKEN), - tokenAssociate(newTokenTreasury, TOKEN), - grantTokenKyc(TOKEN, ACCOUNT), - cryptoTransfer(moving(500, TOKEN).between(TOKEN_TREASURY, ACCOUNT))) - .when(withOpContext((spec, opLog) -> allRunFor( - spec, - - // Try updating token name - contractCall( - tokenInfoUpdateContract, - "updateTokenName", - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - newName) - .via("updateOnlyName") - .logged() - .signingWith(MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym) - .hasName(newName) - .hasEntityMemo(memo) - .hasTreasury(TOKEN_TREASURY) - .hasAutoRenewAccount(ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(MULTI_KEY) - .hasKycKey(MULTI_KEY) - .hasFreezeKey(MULTI_KEY) - .hasWipeKey(MULTI_KEY) - .hasFeeScheduleKey(MULTI_KEY) - .hasSupplyKey(MULTI_KEY) - .hasPauseKey(MULTI_KEY), - - // Try updating token symbol - contractCall( - tokenInfoUpdateContract, - "updateTokenSymbol", - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - sym1) - .via("updateOnlySym") - .logged() - .signingWith(MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(memo) - .hasTreasury(TOKEN_TREASURY) - .hasAutoRenewAccount(ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(MULTI_KEY) - .hasKycKey(MULTI_KEY) - .hasFreezeKey(MULTI_KEY) - .hasWipeKey(MULTI_KEY) - .hasFeeScheduleKey(MULTI_KEY) - .hasSupplyKey(MULTI_KEY) - .hasPauseKey(MULTI_KEY), - - // Try updating token memo - contractCall( - tokenInfoUpdateContract, - "updateTokenMemo", - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - newMemo) - .via("updateOnlyMemo") - .logged() - .signingWith(MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(TOKEN_TREASURY) - .hasAutoRenewAccount(ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(MULTI_KEY) - .hasKycKey(MULTI_KEY) - .hasFreezeKey(MULTI_KEY) - .hasWipeKey(MULTI_KEY) - .hasFeeScheduleKey(MULTI_KEY) - .hasSupplyKey(MULTI_KEY) - .hasPauseKey(MULTI_KEY), - - // Try updating token treasury - contractCall( - tokenInfoUpdateContract, - "updateTokenTreasury", - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - HapiParserUtil.asHeadlongAddress(asAddress(newTreasury.get()))) - .via("updateOnlyTreasury") - .logged() - .signingWith(MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(newTokenTreasury) - .hasAutoRenewAccount(ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(MULTI_KEY) - .hasKycKey(MULTI_KEY) - .hasFreezeKey(MULTI_KEY) - .hasWipeKey(MULTI_KEY) - .hasFeeScheduleKey(MULTI_KEY) - .hasSupplyKey(MULTI_KEY) - .hasPauseKey(MULTI_KEY), - - // Try updating auto-renew account - contractCall( - tokenInfoUpdateContract, - "updateTokenAutoRenewAccount", - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - HapiParserUtil.asHeadlongAddress(asAddress(autoRenewAccount.get()))) - .via("updateOnlyAutoRenewAccount") - .logged() - .signingWith(MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(newTokenTreasury) - .hasAutoRenewAccount(AUTO_RENEW_ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(MULTI_KEY) - .hasKycKey(MULTI_KEY) - .hasFreezeKey(MULTI_KEY) - .hasWipeKey(MULTI_KEY) - .hasFeeScheduleKey(MULTI_KEY) - .hasSupplyKey(MULTI_KEY) - .hasPauseKey(MULTI_KEY), - - // Try updating auto-renew period - contractCall( - tokenInfoUpdateContract, - "updateTokenAutoRenewPeriod", - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - AUTO_RENEW_PERIOD - 1000L) - .via("updateOnlyAutoRenewPeriod") - .logged() - .signingWith(MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(newTokenTreasury) - .hasAutoRenewAccount(AUTO_RENEW_ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD - 1000L) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(MULTI_KEY) - .hasKycKey(MULTI_KEY) - .hasFreezeKey(MULTI_KEY) - .hasWipeKey(MULTI_KEY) - .hasFeeScheduleKey(MULTI_KEY) - .hasSupplyKey(MULTI_KEY) - .hasPauseKey(MULTI_KEY), - - // Try update token admin key - contractCall( - tokenInfoUpdateContract, - updateToEdKey, - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - spec.registry() - .getKey(ED25519KEY) - .getEd25519() - .toByteArray(), - 0) - .via("updateOnlyAdminKeyWithEd") - .logged() - .signedBy(ED25519KEY, MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(CONTRACT_REVERT_EXECUTED), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(newTokenTreasury) - .hasAutoRenewAccount(AUTO_RENEW_ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD - 1000L) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(MULTI_KEY) - .hasKycKey(MULTI_KEY) - .hasFreezeKey(MULTI_KEY) - .hasWipeKey(MULTI_KEY) - .hasFeeScheduleKey(MULTI_KEY) - .hasSupplyKey(MULTI_KEY) - .hasPauseKey(MULTI_KEY), - contractCall( - tokenInfoUpdateContract, - updateToContractId, - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - HapiParserUtil.asHeadlongAddress(asAddress(spec.registry() - .getKey(contractIdKey) - .getContractID())), - 0) - .via("updateOnlyAdminKey") - .logged() - .signedBy(ED25519KEY, MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(newTokenTreasury) - .hasAutoRenewAccount(AUTO_RENEW_ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD - 1000L) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(contractIdKey) - .hasKycKey(MULTI_KEY) - .hasFreezeKey(MULTI_KEY) - .hasWipeKey(MULTI_KEY) - .hasFeeScheduleKey(MULTI_KEY) - .hasSupplyKey(MULTI_KEY) - .hasPauseKey(MULTI_KEY), - - // Try update token kyc key - contractCall( - tokenInfoUpdateContract, - updateToEdKey, - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - spec.registry() - .getKey(ED25519KEY) - .getEd25519() - .toByteArray(), - 1) - .via("updateOnlyKycKeyWithEd") - .logged() - .signedBy(ED25519KEY, MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(newTokenTreasury) - .hasAutoRenewAccount(AUTO_RENEW_ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD - 1000L) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(contractIdKey) - .hasKycKey(ED25519KEY) - .hasFreezeKey(MULTI_KEY) - .hasWipeKey(MULTI_KEY) - .hasFeeScheduleKey(MULTI_KEY) - .hasSupplyKey(MULTI_KEY) - .hasPauseKey(MULTI_KEY), - contractCall( - tokenInfoUpdateContract, - updateToContractId, - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - HapiParserUtil.asHeadlongAddress(asAddress(spec.registry() - .getKey(contractIdKey) - .getContractID())), - 1) - .via("updateOnlyKycKey") - .logged() - .signedBy(MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(newTokenTreasury) - .hasAutoRenewAccount(AUTO_RENEW_ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD - 1000L) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(contractIdKey) - .hasKycKey(contractIdKey) - .hasFreezeKey(MULTI_KEY) - .hasWipeKey(MULTI_KEY) - .hasFeeScheduleKey(MULTI_KEY) - .hasSupplyKey(MULTI_KEY) - .hasPauseKey(MULTI_KEY), - - // Try update token freeze key - contractCall( - tokenInfoUpdateContract, - updateToEdKey, - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - spec.registry() - .getKey(ED25519KEY) - .getEd25519() - .toByteArray(), - 2) - .via("updateOnlyFreezeKeyWithEd") - .logged() - .signedBy(contractIdKey, MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(newTokenTreasury) - .hasAutoRenewAccount(AUTO_RENEW_ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD - 1000L) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(contractIdKey) - .hasKycKey(contractIdKey) - .hasFreezeKey(ED25519KEY) - .hasWipeKey(MULTI_KEY) - .hasFeeScheduleKey(MULTI_KEY) - .hasSupplyKey(MULTI_KEY) - .hasPauseKey(MULTI_KEY), - contractCall( - tokenInfoUpdateContract, - updateToContractId, - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - HapiParserUtil.asHeadlongAddress(asAddress(spec.registry() - .getKey(contractIdKey) - .getContractID())), - 2) - .via("updateOnlyFreezeKey") - .logged() - .signedBy(ED25519KEY, MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(newTokenTreasury) - .hasAutoRenewAccount(AUTO_RENEW_ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD - 1000L) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(contractIdKey) - .hasKycKey(contractIdKey) - .hasFreezeKey(contractIdKey) - .hasWipeKey(MULTI_KEY) - .hasFeeScheduleKey(MULTI_KEY) - .hasSupplyKey(MULTI_KEY) - .hasPauseKey(MULTI_KEY), - - // Try update token wipe key - contractCall( - tokenInfoUpdateContract, - updateToEdKey, - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - spec.registry() - .getKey(ED25519KEY) - .getEd25519() - .toByteArray(), - 3) - .via("updateOnlyWipeKeyWithEd") - .logged() - .signedBy(contractIdKey, MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(newTokenTreasury) - .hasAutoRenewAccount(AUTO_RENEW_ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD - 1000L) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(contractIdKey) - .hasKycKey(contractIdKey) - .hasFreezeKey(contractIdKey) - .hasWipeKey(ED25519KEY) - .hasFeeScheduleKey(MULTI_KEY) - .hasSupplyKey(MULTI_KEY) - .hasPauseKey(MULTI_KEY), - contractCall( - tokenInfoUpdateContract, - updateToContractId, - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - HapiParserUtil.asHeadlongAddress(asAddress(spec.registry() - .getKey(contractIdKey) - .getContractID())), - 3) - .via("updateOnlyWipeKey") - .logged() - .signedBy(contractIdKey, MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(newTokenTreasury) - .hasAutoRenewAccount(AUTO_RENEW_ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD - 1000L) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(contractIdKey) - .hasKycKey(contractIdKey) - .hasFreezeKey(contractIdKey) - .hasWipeKey(contractIdKey) - .hasFeeScheduleKey(MULTI_KEY) - .hasSupplyKey(MULTI_KEY) - .hasPauseKey(MULTI_KEY), - - // Try update token supply key - contractCall( - tokenInfoUpdateContract, - updateToEdKey, - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - spec.registry() - .getKey(ED25519KEY) - .getEd25519() - .toByteArray(), - 4) - .via("updateOnlySupplyKeyWithEd") - .logged() - .signedBy(contractIdKey, MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(newTokenTreasury) - .hasAutoRenewAccount(AUTO_RENEW_ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD - 1000L) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(contractIdKey) - .hasKycKey(contractIdKey) - .hasFreezeKey(contractIdKey) - .hasWipeKey(contractIdKey) - .hasSupplyKey(ED25519KEY) - .hasFeeScheduleKey(MULTI_KEY) - .hasPauseKey(MULTI_KEY), - contractCall( - tokenInfoUpdateContract, - updateToContractId, - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - HapiParserUtil.asHeadlongAddress(asAddress(spec.registry() - .getKey(contractIdKey) - .getContractID())), - 4) - .via("updateOnlySupplyKey") - .logged() - .signedBy(contractIdKey, MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(newTokenTreasury) - .hasAutoRenewAccount(AUTO_RENEW_ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD - 1000L) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(contractIdKey) - .hasKycKey(contractIdKey) - .hasFreezeKey(contractIdKey) - .hasWipeKey(contractIdKey) - .hasSupplyKey(contractIdKey) - .hasPauseKey(MULTI_KEY) - .hasFeeScheduleKey(MULTI_KEY) - .hasPauseKey(MULTI_KEY), - - // Try update token fee schedule key - contractCall( - tokenInfoUpdateContract, - updateToEdKey, - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - spec.registry() - .getKey(ED25519KEY) - .getEd25519() - .toByteArray(), - 5) - .via("updateOnlyFeeKeyWithEd") - .logged() - .signedBy(contractIdKey, MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(newTokenTreasury) - .hasAutoRenewAccount(AUTO_RENEW_ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD - 1000L) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(contractIdKey) - .hasKycKey(contractIdKey) - .hasFreezeKey(contractIdKey) - .hasWipeKey(contractIdKey) - .hasSupplyKey(contractIdKey) - .hasFeeScheduleKey(ED25519KEY) - .hasPauseKey(MULTI_KEY), - contractCall( - tokenInfoUpdateContract, - updateToContractId, - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - HapiParserUtil.asHeadlongAddress(asAddress(spec.registry() - .getKey(contractIdKey) - .getContractID())), - 5) - .via("updateOnlyFeeKey") - .logged() - .signedBy(contractIdKey, MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(newTokenTreasury) - .hasAutoRenewAccount(AUTO_RENEW_ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD - 1000L) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(contractIdKey) - .hasKycKey(contractIdKey) - .hasFreezeKey(contractIdKey) - .hasWipeKey(contractIdKey) - .hasSupplyKey(contractIdKey) - .hasFeeScheduleKey(contractIdKey) - .hasPauseKey(MULTI_KEY), - - // Try update token pause key - contractCall( - tokenInfoUpdateContract, - updateToEdKey, - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - spec.registry() - .getKey(ED25519KEY) - .getEd25519() - .toByteArray(), - 6) - .via("updateOnlyPauseKeyWithEd") - .logged() - .signedBy(contractIdKey, MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(newTokenTreasury) - .hasAutoRenewAccount(AUTO_RENEW_ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD - 1000L) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(contractIdKey) - .hasKycKey(contractIdKey) - .hasFreezeKey(contractIdKey) - .hasWipeKey(contractIdKey) - .hasSupplyKey(contractIdKey) - .hasFeeScheduleKey(contractIdKey) - .hasPauseKey(ED25519KEY), - contractCall( - tokenInfoUpdateContract, - updateToContractId, - HapiParserUtil.asHeadlongAddress(asAddress(token.get())), - HapiParserUtil.asHeadlongAddress(asAddress(spec.registry() - .getKey(contractIdKey) - .getContractID())), - 6) - .via("updateOnlyPauseKey") - .logged() - .signedBy(contractIdKey, MULTI_KEY) - .payingWith(ACCOUNT) - .gas(GAS_TO_OFFER) - .hasKnownStatus(SUCCESS), - getTokenInfo(TOKEN) - .logged() - .hasTokenType(TokenType.FUNGIBLE_COMMON) - .hasSymbol(sym1) - .hasName(newName) - .hasEntityMemo(newMemo) - .hasTreasury(newTokenTreasury) - .hasAutoRenewAccount(AUTO_RENEW_ACCOUNT) - .hasAutoRenewPeriod(AUTO_RENEW_PERIOD - 1000L) - .hasSupplyType(TokenSupplyType.INFINITE) - .searchKeysGlobally() - .hasAdminKey(contractIdKey) - .hasKycKey(contractIdKey) - .hasFreezeKey(contractIdKey) - .hasWipeKey(contractIdKey) - .hasSupplyKey(contractIdKey) - .hasFeeScheduleKey(contractIdKey) - .hasPauseKey(contractIdKey)))) - .then( - childRecordsCheck( - "updateOnlyName", SUCCESS, recordWith().status(SUCCESS)), - childRecordsCheck("updateOnlySym", SUCCESS, recordWith().status(SUCCESS)), - childRecordsCheck( - "updateOnlyMemo", SUCCESS, recordWith().status(SUCCESS)), - childRecordsCheck( - "updateOnlyTreasury", SUCCESS, recordWith().status(SUCCESS)), - childRecordsCheck( - "updateOnlyAutoRenewAccount", - SUCCESS, - recordWith().status(SUCCESS)), - childRecordsCheck( - "updateOnlyAutoRenewPeriod", - SUCCESS, - recordWith().status(SUCCESS)), - childRecordsCheck( - "updateOnlyAdminKeyWithEd", - CONTRACT_REVERT_EXECUTED, - recordWith().status(INVALID_SIGNATURE)), - childRecordsCheck( - "updateOnlyAdminKey", SUCCESS, recordWith().status(SUCCESS)), - childRecordsCheck( - "updateOnlyKycKeyWithEd", SUCCESS, recordWith().status(SUCCESS)), - childRecordsCheck( - "updateOnlyKycKey", SUCCESS, recordWith().status(SUCCESS)), - childRecordsCheck( - "updateOnlyFreezeKeyWithEd", - SUCCESS, - recordWith().status(SUCCESS)), - childRecordsCheck( - "updateOnlyFreezeKey", SUCCESS, recordWith().status(SUCCESS)), - childRecordsCheck( - "updateOnlyWipeKeyWithEd", SUCCESS, recordWith().status(SUCCESS)), - childRecordsCheck( - "updateOnlyWipeKey", SUCCESS, recordWith().status(SUCCESS)), - childRecordsCheck( - "updateOnlySupplyKeyWithEd", - SUCCESS, - recordWith().status(SUCCESS)), - childRecordsCheck( - "updateOnlySupplyKey", SUCCESS, recordWith().status(SUCCESS)), - childRecordsCheck( - "updateOnlyFeeKeyWithEd", SUCCESS, recordWith().status(SUCCESS)), - childRecordsCheck( - "updateOnlyFeeKey", SUCCESS, recordWith().status(SUCCESS)), - childRecordsCheck( - "updateOnlyPauseKeyWithEd", - SUCCESS, - recordWith().status(SUCCESS)), - childRecordsCheck( - "updateOnlyPauseKey", SUCCESS, recordWith().status(SUCCESS))); - } - - @HapiTest - public Stream createFungibleTokenAdminKeyFromHollowAccountAlias() { - final var freezeKey = "freezeKey"; - final var newFreezeKey = "newFreezeKey"; - return defaultHapiSpec("CreateFungibleTokenAdminKeyFromHollowAccountAlias") - .given( - // Create an ECDSA key - newKeyNamed(SECP_256K1_SOURCE_KEY).shape(SECP_256K1_SHAPE), - newKeyNamed(freezeKey), - newKeyNamed(newFreezeKey), - cryptoCreate(ACCOUNT).balance(ONE_MILLION_HBARS), - cryptoCreate(TOKEN_TREASURY).balance(ONE_MILLION_HBARS)) - .when(withOpContext((spec, opLog) -> { - final var ecdsaKey = spec.registry() - .getKey(SECP_256K1_SOURCE_KEY) - .getECDSASecp256K1() - .toByteArray(); - final var evmAddress = ByteString.copyFrom(recoverAddressFromPubKey(ecdsaKey)); - spec.registry() - .saveAccountAlias( - SECP_256K1_SOURCE_KEY, - AccountID.newBuilder().setAlias(evmAddress).build()); - - allRunFor( - spec, - // Transfer money to the alias --> creates HOLLOW ACCOUNT - cryptoTransfer( - movingHbar(ONE_HUNDRED_HBARS).distributing(TOKEN_TREASURY, SECP_256K1_SOURCE_KEY)), - // Verify that the account is created and is hollow - getAliasedAccountInfo(SECP_256K1_SOURCE_KEY) - .has(accountWith().hasEmptyKey()), - // Create a token with the ECDSA alias key as ADMIN key - tokenCreate(VANILLA_TOKEN) - .tokenType(FUNGIBLE_COMMON) - .adminKey(SECP_256K1_SOURCE_KEY) - .freezeKey(freezeKey) - .initialSupply(100L) - .treasury(TOKEN_TREASURY)); - })) - .then(withOpContext((spec, opLog) -> { - allRunFor( - spec, - tokenUpdate(VANILLA_TOKEN) - .freezeKey(newFreezeKey) - .signedBy(ACCOUNT, SECP_256K1_SOURCE_KEY) - .payingWith(ACCOUNT), - getTokenInfo(VANILLA_TOKEN).searchKeysGlobally().hasFreezeKey(newFreezeKey)); - })); - } - - @HapiTest - public Stream createNFTTokenAdminKeyFromHollowAccountAlias() { - final var freezeKey = "freezeKey"; - final var newFreezeKey = "newFreezeKey"; - return defaultHapiSpec("CreateNFTTokenAdminKeyFromHollowAccountAlias") - .given( - // Create an ECDSA key - newKeyNamed(SECP_256K1_SOURCE_KEY).shape(SECP_256K1_SHAPE), - newKeyNamed(freezeKey), - newKeyNamed(newFreezeKey), - cryptoCreate(ACCOUNT).balance(ONE_MILLION_HBARS), - cryptoCreate(TOKEN_TREASURY).balance(ONE_MILLION_HBARS)) - .when(withOpContext((spec, opLog) -> { - final var ecdsaKey = spec.registry() - .getKey(SECP_256K1_SOURCE_KEY) - .getECDSASecp256K1() - .toByteArray(); - final var evmAddress = ByteString.copyFrom(recoverAddressFromPubKey(ecdsaKey)); - spec.registry() - .saveAccountAlias( - SECP_256K1_SOURCE_KEY, - AccountID.newBuilder().setAlias(evmAddress).build()); - - allRunFor( - spec, - // Transfer money to the alias --> creates HOLLOW ACCOUNT - cryptoTransfer( - movingHbar(ONE_HUNDRED_HBARS).distributing(TOKEN_TREASURY, SECP_256K1_SOURCE_KEY)), - // Verify that the account is created and is hollow - getAliasedAccountInfo(SECP_256K1_SOURCE_KEY) - .has(accountWith().hasEmptyKey()), - // Create a token with the ECDSA alias key as ADMIN key - tokenCreate(NFT_TOKEN) - .tokenType(NON_FUNGIBLE_UNIQUE) - .adminKey(SECP_256K1_SOURCE_KEY) - .supplyKey(SECP_256K1_SOURCE_KEY) - .freezeKey(freezeKey) - .initialSupply(0L) - .treasury(TOKEN_TREASURY)); - })) - .then(withOpContext((spec, opLog) -> { - allRunFor( - spec, - tokenUpdate(NFT_TOKEN) - .freezeKey(newFreezeKey) - .signedBy(ACCOUNT, SECP_256K1_SOURCE_KEY) - .payingWith(ACCOUNT), - getTokenInfo(NFT_TOKEN).searchKeysGlobally().hasFreezeKey(newFreezeKey)); - })); - } -} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/token/GetTokenKeyPrecompileTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/token/GetTokenKeyPrecompileTest.java new file mode 100644 index 000000000000..3eb673f600c9 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/token/GetTokenKeyPrecompileTest.java @@ -0,0 +1,90 @@ +/* + * 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.services.bdd.suites.contract.precompile.token; + +import static com.hedera.node.app.service.contract.impl.utils.ConversionUtils.keyTupleFor; +import static com.hedera.services.bdd.junit.TestTags.SMART_CONTRACT; +import static com.hedera.services.bdd.spec.HapiSpec.hapiTest; +import static com.hedera.services.bdd.spec.assertions.ContractFnResultAsserts.isLiteralResult; +import static com.hedera.services.bdd.spec.assertions.ContractFnResultAsserts.resultWith; +import static com.hedera.services.bdd.suites.HapiSuite.ZERO_ADDRESS; +import static com.hedera.services.bdd.suites.contract.Utils.FunctionType.FUNCTION; +import static com.hedera.services.bdd.suites.contract.Utils.getABIFor; +import static com.hedera.services.bdd.suites.utils.contracts.precompile.TokenKeyType.FREEZE_KEY; +import static com.hedera.services.bdd.suites.utils.contracts.precompile.TokenKeyType.SUPPLY_KEY; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.CONTRACT_REVERT_EXECUTED; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_TOKEN_ID; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.KEY_NOT_PROVIDED; + +import com.hedera.services.bdd.junit.HapiTest; +import com.hedera.services.bdd.junit.HapiTestLifecycle; +import com.hedera.services.bdd.spec.dsl.annotations.ContractSpec; +import com.hedera.services.bdd.spec.dsl.annotations.NonFungibleTokenSpec; +import com.hedera.services.bdd.spec.dsl.entities.SpecContract; +import com.hedera.services.bdd.spec.dsl.entities.SpecNonFungibleToken; +import java.math.BigInteger; +import java.util.stream.Stream; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Tag; + +@Tag(SMART_CONTRACT) +@DisplayName("getTokenKey") +@SuppressWarnings("java:S1192") +@HapiTestLifecycle +public class GetTokenKeyPrecompileTest { + @ContractSpec(contract = "UpdateTokenInfoContract", creationGas = 4_000_000L) + static SpecContract getTokenKeyContract; + + @NonFungibleTokenSpec(numPreMints = 1) + static SpecNonFungibleToken nonFungibleToken; + + @HapiTest + @DisplayName("can get a token's supply key via static call") + public Stream canGetSupplyKeyViaStaticCall() { + return hapiTest(nonFungibleToken.doWith(token -> getTokenKeyContract + .staticCall("getKeyFromToken", nonFungibleToken, SUPPLY_KEY.asBigInteger()) + .andAssert(query -> query.has(resultWith() + .resultThruAbi( + getABIFor(FUNCTION, "getKeyFromToken", "UpdateTokenInfoContract"), + isLiteralResult(new Object[] {keyTupleFor(token.supplyKeyOrThrow())})))))); + } + + @HapiTest + @DisplayName("cannot get a nonsense key type") + public Stream cannotGetNonsenseKeyType() { + return hapiTest(getTokenKeyContract + .call("getKeyFromToken", nonFungibleToken, BigInteger.valueOf(123L)) + .andAssert(txn -> txn.hasKnownStatuses(CONTRACT_REVERT_EXECUTED, KEY_NOT_PROVIDED))); + } + + @HapiTest + @DisplayName("cannot get a key from a missing token") + public Stream cannotGetMissingTokenKey() { + return hapiTest(getTokenKeyContract + .call("getKeyFromToken", ZERO_ADDRESS, SUPPLY_KEY.asBigInteger()) + .andAssert(txn -> txn.hasKnownStatuses(CONTRACT_REVERT_EXECUTED, INVALID_TOKEN_ID))); + } + + @HapiTest + @DisplayName("cannot get a key not set on the token") + public Stream cannotGetUnsetTokenKey() { + return hapiTest(getTokenKeyContract + .call("getKeyFromToken", nonFungibleToken, FREEZE_KEY.asBigInteger()) + .andAssert(txn -> txn.hasKnownStatuses(CONTRACT_REVERT_EXECUTED, KEY_NOT_PROVIDED))); + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/token/UpdateTokenPrecompileTest.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/token/UpdateTokenPrecompileTest.java new file mode 100644 index 000000000000..d7a519fceb48 --- /dev/null +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/contract/precompile/token/UpdateTokenPrecompileTest.java @@ -0,0 +1,497 @@ +/* + * 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.services.bdd.suites.contract.precompile.token; + +import static com.hedera.services.bdd.junit.TestTags.SMART_CONTRACT; +import static com.hedera.services.bdd.spec.HapiSpec.hapiTest; +import static com.hedera.services.bdd.spec.dsl.entities.SpecKey.Type.SECP_256K1; +import static com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey.ADMIN_KEY; +import static com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey.FEE_SCHEDULE_KEY; +import static com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey.FREEZE_KEY; +import static com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey.KYC_KEY; +import static com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey.PAUSE_KEY; +import static com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey.SUPPLY_KEY; +import static com.hedera.services.bdd.spec.dsl.entities.SpecTokenKey.WIPE_KEY; +import static com.hedera.services.bdd.spec.dsl.utils.DslUtils.contractIdKeyFor; +import static com.hedera.services.bdd.spec.transactions.TxnUtils.randomUppercase; +import static com.hedera.services.bdd.suites.HapiSuite.THREE_MONTHS_IN_SECONDS; +import static com.hedera.services.bdd.suites.HapiSuite.ZERO_ADDRESS; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.CONTRACT_REVERT_EXECUTED; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_SIGNATURE; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.INVALID_TOKEN_ID; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_HAS_NO_FEE_SCHEDULE_KEY; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_HAS_NO_FREEZE_KEY; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_HAS_NO_KYC_KEY; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_HAS_NO_PAUSE_KEY; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_HAS_NO_SUPPLY_KEY; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_HAS_NO_WIPE_KEY; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_IS_IMMUTABLE; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_NAME_TOO_LONG; +import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.TOKEN_SYMBOL_TOO_LONG; + +import com.hedera.services.bdd.junit.HapiTest; +import com.hedera.services.bdd.junit.HapiTestLifecycle; +import com.hedera.services.bdd.junit.support.SpecManager; +import com.hedera.services.bdd.spec.dsl.annotations.AccountSpec; +import com.hedera.services.bdd.spec.dsl.annotations.ContractSpec; +import com.hedera.services.bdd.spec.dsl.annotations.FungibleTokenSpec; +import com.hedera.services.bdd.spec.dsl.annotations.KeySpec; +import com.hedera.services.bdd.spec.dsl.annotations.NonFungibleTokenSpec; +import com.hedera.services.bdd.spec.dsl.entities.SpecAccount; +import com.hedera.services.bdd.spec.dsl.entities.SpecContract; +import com.hedera.services.bdd.spec.dsl.entities.SpecFungibleToken; +import com.hedera.services.bdd.spec.dsl.entities.SpecKey; +import com.hedera.services.bdd.spec.dsl.entities.SpecNonFungibleToken; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Tag; + +@Tag(SMART_CONTRACT) +@DisplayName("updateToken") +@SuppressWarnings("java:S1192") +@HapiTestLifecycle +public class UpdateTokenPrecompileTest { + @ContractSpec(contract = "UpdateTokenInfoContract", creationGas = 4_000_000L) + static SpecContract updateTokenContract; + + @FungibleTokenSpec( + name = "immutableToken", + keys = {FEE_SCHEDULE_KEY, SUPPLY_KEY, WIPE_KEY, PAUSE_KEY, FREEZE_KEY, KYC_KEY}) + static SpecFungibleToken immutableToken; + + @KeySpec + static SpecKey ed25519Key; + + @KeySpec(type = SECP_256K1) + static SpecKey secp256k1Key; + + @HapiTest + @DisplayName("cannot update a missing token") + public Stream cannotUpdateMissingToken() { + return hapiTest(updateTokenContract + .call("tokenUpdateKeys", ZERO_ADDRESS, ed25519Key, secp256k1Key, updateTokenContract) + .andAssert(txn -> txn.hasKnownStatuses(CONTRACT_REVERT_EXECUTED, INVALID_TOKEN_ID))); + } + + @HapiTest + @DisplayName("cannot update an immutable token") + public Stream cannotUpdateImmutableToken(@AccountSpec final SpecAccount newTreasury) { + return hapiTest( + newTreasury.authorizeContract(updateTokenContract), + updateTokenContract + .call("updateTokenTreasury", immutableToken, newTreasury) + .andAssert(txn -> txn.hasKnownStatuses(CONTRACT_REVERT_EXECUTED, TOKEN_IS_IMMUTABLE)), + updateTokenContract + .call("tokenUpdateKeys", immutableToken, ed25519Key, secp256k1Key, updateTokenContract) + .andAssert(txn -> txn.hasKnownStatuses(CONTRACT_REVERT_EXECUTED, TOKEN_IS_IMMUTABLE))); + } + + @HapiTest + @DisplayName("can only update a mutable token treasury if authorized") + public Stream canUpdateMutableTokenTreasuryOnceAuthorized( + @AccountSpec final SpecAccount newTreasury, + @NonFungibleTokenSpec( + numPreMints = 1, + keys = {SUPPLY_KEY, PAUSE_KEY, ADMIN_KEY}) + final SpecNonFungibleToken mutableToken) { + return hapiTest( + // First confirm we CANNOT update the treasury without authorization + updateTokenContract + .call("updateTokenTreasury", mutableToken, newTreasury) + .andAssert(txn -> txn.hasKnownStatuses(CONTRACT_REVERT_EXECUTED, INVALID_SIGNATURE)), + + // So authorize the contract to manage both the token and the new treasury + newTreasury.authorizeContract(updateTokenContract), + mutableToken.authorizeContracts(updateTokenContract), + // Also need to associate the token with the new treasury + newTreasury.associateTokens(mutableToken), + + // Now do a contract-managed treasury update + updateTokenContract.call("updateTokenTreasury", mutableToken, newTreasury), + // And verify a treasury-owned NFT has the new treasury as its owner + mutableToken.serialNo(1).assertOwnerIs(newTreasury)); + } + + @Nested + @DisplayName("once authorized") + class OnceAuthorized { + @ContractSpec(contract = "TokenInfoSingularUpdate", creationGas = 4_000_000L) + static SpecContract updateTokenPropertyContract; + + @AccountSpec(name = "sharedTreasury") + static SpecAccount sharedTreasury; + + @NonFungibleTokenSpec( + numPreMints = 1, + useAutoRenewAccount = true, + keys = { + ADMIN_KEY, + FEE_SCHEDULE_KEY, + SUPPLY_KEY, + WIPE_KEY, + PAUSE_KEY, + FREEZE_KEY, + KYC_KEY, + }) + static SpecNonFungibleToken sharedMutableToken; + + @BeforeAll + static void beforeAll(@NonNull final SpecManager specManager) throws Throwable { + sharedMutableToken.setTreasury(sharedTreasury); + specManager.setup( + sharedTreasury.authorizeContract(updateTokenContract), + sharedMutableToken.authorizeContracts(updateTokenContract, updateTokenPropertyContract)); + } + + @HapiTest + @DisplayName("can update the name") + public Stream canUpdateName() { + return hapiTest( + updateTokenPropertyContract.call("updateTokenName", sharedMutableToken, "NEW_NAME"), + sharedMutableToken.getInfo().andAssert(info -> info.hasName("NEW_NAME"))); + } + + @HapiTest + @DisplayName("can update the symbol") + public Stream canUpdateSymbol() { + return hapiTest( + updateTokenPropertyContract.call("updateTokenSymbol", sharedMutableToken, "NSYM"), + sharedMutableToken.getInfo().andAssert(info -> info.hasSymbol("NSYM"))); + } + + @HapiTest + @DisplayName("can update the memo") + public Stream canUpdateMemo() { + return hapiTest( + updateTokenPropertyContract.call("updateTokenMemo", sharedMutableToken, "Did you get it?"), + sharedMutableToken.getInfo().andAssert(info -> info.hasEntityMemo("Did you get it?"))); + } + + @HapiTest + @DisplayName("can update the auto-renew account") + public Stream canUpdateAutoRenewAccount( + @AccountSpec(name = "autoRenewAccount") SpecAccount autoRenewAccount) { + return hapiTest( + autoRenewAccount.authorizeContract(updateTokenPropertyContract), + updateTokenPropertyContract.call( + "updateTokenAutoRenewAccount", sharedMutableToken, autoRenewAccount), + sharedMutableToken.getInfo().andAssert(info -> info.hasAutoRenewAccount(autoRenewAccount.name()))); + } + + @HapiTest + @DisplayName("can update the auto-renew period") + public Stream canUpdateAutoRenewPeriod() { + final var abbreviatedPeriod = THREE_MONTHS_IN_SECONDS - 1234L; + return hapiTest( + updateTokenPropertyContract.call( + "updateTokenAutoRenewPeriod", sharedMutableToken, abbreviatedPeriod), + sharedMutableToken.getInfo().andAssert(info -> info.hasAutoRenewPeriod(abbreviatedPeriod))); + } + + @HapiTest + @DisplayName("can update the admin key to itself") + public Stream canUpdateAdminKeyToItself(@FungibleTokenSpec final SpecFungibleToken token) { + return hapiTest( + token.authorizeContracts(updateTokenPropertyContract), + // This contract uses 0 as shorthand for the admin key + updateTokenPropertyContract.call("updateTokenKeyContractId", token, updateTokenPropertyContract, 0), + updateTokenPropertyContract.doWith(contract -> + token.getInfo().andAssert(info -> info.hasAdminKey(contractIdKeyFor(contract))))); + } + + @HapiTest + @DisplayName("can update the KYC key to a different Ed25519 key or itself") + public Stream canUpdateKycKeyToEd25519OrItself() { + return hapiTest( + // This contract uses 1 as shorthand for the KYC key + updateTokenPropertyContract.call("updateTokenKeyEd", sharedMutableToken, ed25519Key, 1), + ed25519Key.doWith(key -> sharedMutableToken.getInfo().andAssert(info -> info.hasKycKey(key))), + // And then to the managing contract's id + updateTokenPropertyContract.call( + "updateTokenKeyContractId", sharedMutableToken, updateTokenPropertyContract, 1), + updateTokenPropertyContract.doWith(contract -> sharedMutableToken + .getInfo() + .andAssert(info -> info.hasKycKey(contractIdKeyFor(contract))))); + } + + @HapiTest + @DisplayName("can update the freeze key to a different Ed25519 key or itself") + public Stream canUpdateFreezeKeyToEd25519OrItself() { + return hapiTest( + // This contract uses 2 as shorthand for the freeze key + updateTokenPropertyContract.call("updateTokenKeyEd", sharedMutableToken, ed25519Key, 2), + ed25519Key.doWith(key -> sharedMutableToken.getInfo().andAssert(info -> info.hasFreezeKey(key))), + // And then to the managing contract's id + updateTokenPropertyContract.call( + "updateTokenKeyContractId", sharedMutableToken, updateTokenPropertyContract, 2), + updateTokenPropertyContract.doWith(contract -> sharedMutableToken + .getInfo() + .andAssert(info -> info.hasFreezeKey(contractIdKeyFor(contract))))); + } + + @HapiTest + @DisplayName("can update the wipe key to a different Ed25519 key or itself") + public Stream canUpdateWipeKeyToEd25519OrItself() { + return hapiTest( + // This contract uses 3 as shorthand for the wipe key; first update to an Ed25519 key + updateTokenPropertyContract.call("updateTokenKeyEd", sharedMutableToken, ed25519Key, 3), + ed25519Key.doWith(key -> sharedMutableToken.getInfo().andAssert(info -> info.hasWipeKey(key))), + // And then to the managing contract's id + updateTokenPropertyContract.call( + "updateTokenKeyContractId", sharedMutableToken, updateTokenPropertyContract, 3), + updateTokenPropertyContract.doWith(contract -> sharedMutableToken + .getInfo() + .andAssert(info -> info.hasWipeKey(contractIdKeyFor(contract))))); + } + + @HapiTest + @DisplayName("can update the supply key to a different Ed25519 key or itself") + public Stream canUpdateSupplyKeyToEd25519OrItself() { + return hapiTest( + // This contract uses 4 as shorthand for the supply key; first update to an Ed25519 key + updateTokenPropertyContract.call("updateTokenKeyEd", sharedMutableToken, ed25519Key, 4), + ed25519Key.doWith(key -> sharedMutableToken.getInfo().andAssert(info -> info.hasSupplyKey(key))), + // And then to the managing contract's id + updateTokenPropertyContract.call( + "updateTokenKeyContractId", sharedMutableToken, updateTokenPropertyContract, 4), + updateTokenPropertyContract.doWith(contract -> sharedMutableToken + .getInfo() + .andAssert(info -> info.hasSupplyKey(contractIdKeyFor(contract))))); + } + + @HapiTest + @DisplayName("can update the fee schedule key to a different Ed25519 key or itself") + public Stream canUpdateFeeScheduleKeyToEd25519OrItself() { + return hapiTest( + // This contract uses 5 as shorthand for the fee schedule key; first update to an Ed25519 key + updateTokenPropertyContract.call("updateTokenKeyEd", sharedMutableToken, ed25519Key, 5), + ed25519Key.doWith( + key -> sharedMutableToken.getInfo().andAssert(info -> info.hasFeeScheduleKey(key))), + // And then to the managing contract's id + updateTokenPropertyContract.call( + "updateTokenKeyContractId", sharedMutableToken, updateTokenPropertyContract, 5), + updateTokenPropertyContract.doWith(contract -> sharedMutableToken + .getInfo() + .andAssert(info -> info.hasFeeScheduleKey(contractIdKeyFor(contract))))); + } + + @HapiTest + @DisplayName("can update the pause key to a different Ed25519 key or itself") + public Stream canUpdatePauseKeyToEd25519OrItself() { + return hapiTest( + // This contract uses 6 as shorthand for the pause key; first update to an Ed25519 key + updateTokenPropertyContract.call("updateTokenKeyEd", sharedMutableToken, ed25519Key, 6), + ed25519Key.doWith(key -> sharedMutableToken.getInfo().andAssert(info -> info.hasPauseKey(key))), + // And then to the managing contract's id + updateTokenPropertyContract.call( + "updateTokenKeyContractId", sharedMutableToken, updateTokenPropertyContract, 6), + updateTokenPropertyContract.doWith(contract -> sharedMutableToken + .getInfo() + .andAssert(info -> info.hasPauseKey(contractIdKeyFor(contract))))); + } + + @Nested + @DisplayName("still cannot") + class StillCannot { + @HapiTest + @DisplayName("update to a new crypto admin key") + public Stream cannotUpdateToCryptoAdminKey() { + return hapiTest( + // This contract uses 0 as shorthand for the admin key + updateTokenPropertyContract + .call("updateTokenKeyEd", sharedMutableToken, ed25519Key, 0) + .andAssert(txn -> txn.hasKnownStatuses(CONTRACT_REVERT_EXECUTED, INVALID_SIGNATURE))); + } + + @HapiTest + @DisplayName("exceed name and symbol length limits") + public Stream enforcesNameAndSymbolLengthLimits() { + return hapiTest( + // We cannot update with an overly long token name + updateTokenContract + .call( + "checkNameAndSymbolLength", + sharedMutableToken, + sharedTreasury, + randomUppercase(101), + "SYMBOL") + .andAssert(txn -> txn.hasKnownStatuses(CONTRACT_REVERT_EXECUTED, TOKEN_NAME_TOO_LONG)), + // We cannot update with an overly long token symbol + updateTokenContract + .call( + "checkNameAndSymbolLength", + sharedMutableToken, + sharedTreasury, + "NAME", + randomUppercase(101)) + .andAssert( + txn -> txn.hasKnownStatuses(CONTRACT_REVERT_EXECUTED, TOKEN_SYMBOL_TOO_LONG))); + } + + @HapiTest + @DisplayName("add fee schedule key") + public Stream cannotAddFeeScheduleKey( + @FungibleTokenSpec(keys = {ADMIN_KEY, SUPPLY_KEY, WIPE_KEY, PAUSE_KEY, FREEZE_KEY, KYC_KEY}) + final SpecFungibleToken noFeeScheduleKeyToken) { + noFeeScheduleKeyToken.setTreasury(sharedTreasury); + return hapiTest( + noFeeScheduleKeyToken.authorizeContracts(updateTokenContract), + updateTokenContract + .call( + "updateTokenWithKeys", + noFeeScheduleKeyToken, + sharedTreasury, + ed25519Key, + secp256k1Key, + updateTokenContract) + .andAssert(txn -> + txn.hasKnownStatuses(CONTRACT_REVERT_EXECUTED, TOKEN_HAS_NO_FEE_SCHEDULE_KEY))); + } + + @HapiTest + @DisplayName("add supply key") + public Stream cannotAddSupplyKey( + @FungibleTokenSpec(keys = {ADMIN_KEY, FEE_SCHEDULE_KEY, WIPE_KEY, PAUSE_KEY, FREEZE_KEY, KYC_KEY}) + final SpecFungibleToken noSupplyKeyToken) { + noSupplyKeyToken.setTreasury(sharedTreasury); + return hapiTest( + noSupplyKeyToken.authorizeContracts(updateTokenContract), + updateTokenContract + .call( + "updateTokenWithKeys", + noSupplyKeyToken, + sharedTreasury, + ed25519Key, + secp256k1Key, + updateTokenContract) + .andAssert(txn -> + txn.hasKnownStatuses(CONTRACT_REVERT_EXECUTED, TOKEN_HAS_NO_SUPPLY_KEY))); + } + + @HapiTest + @DisplayName("add wipe key") + public Stream cannotAddWipeKey( + @FungibleTokenSpec(keys = {ADMIN_KEY, FEE_SCHEDULE_KEY, SUPPLY_KEY, PAUSE_KEY, FREEZE_KEY, KYC_KEY}) + final SpecFungibleToken noWipeKeyToken) { + noWipeKeyToken.setTreasury(sharedTreasury); + return hapiTest( + noWipeKeyToken.authorizeContracts(updateTokenContract), + updateTokenContract + .call( + "updateTokenWithKeys", + noWipeKeyToken, + sharedTreasury, + ed25519Key, + secp256k1Key, + updateTokenContract) + .andAssert( + txn -> txn.hasKnownStatuses(CONTRACT_REVERT_EXECUTED, TOKEN_HAS_NO_WIPE_KEY))); + } + + @HapiTest + @DisplayName("add pause key") + public Stream cannotAddPauseKey( + @FungibleTokenSpec(keys = {ADMIN_KEY, FEE_SCHEDULE_KEY, SUPPLY_KEY, WIPE_KEY, FREEZE_KEY, KYC_KEY}) + final SpecFungibleToken noPauseKeyToken) { + noPauseKeyToken.setTreasury(sharedTreasury); + return hapiTest( + noPauseKeyToken.authorizeContracts(updateTokenContract), + updateTokenContract + .call( + "updateTokenWithKeys", + noPauseKeyToken, + sharedTreasury, + ed25519Key, + secp256k1Key, + updateTokenContract) + .andAssert( + txn -> txn.hasKnownStatuses(CONTRACT_REVERT_EXECUTED, TOKEN_HAS_NO_PAUSE_KEY))); + } + + @HapiTest + @DisplayName("add freeze key") + public Stream cannotAddFreezeKey( + @FungibleTokenSpec(keys = {ADMIN_KEY, FEE_SCHEDULE_KEY, SUPPLY_KEY, WIPE_KEY, PAUSE_KEY, KYC_KEY}) + final SpecFungibleToken noFreezeKeyToken) { + noFreezeKeyToken.setTreasury(sharedTreasury); + return hapiTest( + noFreezeKeyToken.authorizeContracts(updateTokenContract), + updateTokenContract + .call( + "updateTokenWithKeys", + noFreezeKeyToken, + sharedTreasury, + ed25519Key, + secp256k1Key, + updateTokenContract) + .andAssert(txn -> + txn.hasKnownStatuses(CONTRACT_REVERT_EXECUTED, TOKEN_HAS_NO_FREEZE_KEY))); + } + + @HapiTest + @DisplayName("add kyc key") + public Stream cannotAddKycKey( + @FungibleTokenSpec( + keys = {ADMIN_KEY, FEE_SCHEDULE_KEY, SUPPLY_KEY, WIPE_KEY, PAUSE_KEY, FREEZE_KEY}) + final SpecFungibleToken noKycKeyToken) { + noKycKeyToken.setTreasury(sharedTreasury); + return hapiTest( + noKycKeyToken.authorizeContracts(updateTokenContract), + updateTokenContract + .call( + "updateTokenWithKeys", + noKycKeyToken, + sharedTreasury, + ed25519Key, + secp256k1Key, + updateTokenContract) + .andAssert( + txn -> txn.hasKnownStatuses(CONTRACT_REVERT_EXECUTED, TOKEN_HAS_NO_KYC_KEY))); + } + + @HapiTest + @DisplayName("set invalid values") + public Stream cannotSetInvalidValues( + @FungibleTokenSpec( + keys = { + ADMIN_KEY, + FEE_SCHEDULE_KEY, + SUPPLY_KEY, + WIPE_KEY, + PAUSE_KEY, + FREEZE_KEY, + KYC_KEY + }) + final SpecFungibleToken allKeysToken) { + allKeysToken.setTreasury(sharedTreasury); + return hapiTest( + allKeysToken.authorizeContracts(updateTokenContract), + updateTokenContract + .call( + "updateTokenWithInvalidKeyValues", + allKeysToken, + sharedTreasury, + THREE_MONTHS_IN_SECONDS) + .andAssert(txn -> txn.hasKnownStatus(CONTRACT_REVERT_EXECUTED))); + } + } + } +} diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/crypto/TransferWithCustomFixedFees.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/crypto/TransferWithCustomFixedFees.java index 6f1091532678..cbad2fcf34cb 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/crypto/TransferWithCustomFixedFees.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/crypto/TransferWithCustomFixedFees.java @@ -50,8 +50,8 @@ import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.*; import com.hedera.node.app.hapi.utils.ByteStringUtils; +import com.hedera.services.bdd.SpecOperation; import com.hedera.services.bdd.junit.HapiTest; -import com.hedera.services.bdd.spec.HapiSpecOperation; import com.hederahashgraph.api.proto.java.TokenSupplyType; import com.hederahashgraph.api.proto.java.TokenType; import java.util.ArrayList; @@ -1125,7 +1125,7 @@ final Stream transferMaxFungibleWith10FixedHtsCustomFees2Layers() { return defaultHapiSpec("transferMaxFungibleWith10FixedHtsCustomFees2Layers") .given(withOpContext((spec, log) -> { - ArrayList ops = new ArrayList<>(); + final ArrayList ops = new ArrayList<>(); var collectorCreate = cryptoCreate(htsCollector); var collector2Create = cryptoCreate(htsCollector2); var tokenOwnerCreate = cryptoCreate(tokenOwner).balance(ONE_MILLION_HBARS); diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/perf/crypto/CryptoTransferLoadTestWithStakedAccounts.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/perf/crypto/CryptoTransferLoadTestWithStakedAccounts.java index 0a0839a418f2..e54e219ef96c 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/perf/crypto/CryptoTransferLoadTestWithStakedAccounts.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/perf/crypto/CryptoTransferLoadTestWithStakedAccounts.java @@ -26,6 +26,7 @@ import static com.hedera.services.bdd.spec.utilops.UtilVerbs.withOpContext; import static com.hederahashgraph.api.proto.java.ResponseCodeEnum.*; +import com.hedera.services.bdd.SpecOperation; import com.hedera.services.bdd.spec.HapiSpecOperation; import com.hedera.services.bdd.spec.utilops.LoadTest; import com.hedera.services.bdd.suites.perf.PerfTestLoadSettings; @@ -115,7 +116,7 @@ final Stream runCryptoTransfers() { .key(GENESIS) .logging(), withOpContext((spec, opLog) -> { - List ops = new ArrayList<>(); + List ops = new ArrayList<>(); var stakedNodeId = settings.getNodeToStake(); for (int i = 0; i < STAKED_CREATIONS; i++) { var stakedAccount = "stakedAccount" + i; diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/token/hip540/Hip540Suite.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/token/hip540/Hip540Suite.java index e823f6f4ec54..b733ca42e3ad 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/token/hip540/Hip540Suite.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/token/hip540/Hip540Suite.java @@ -17,12 +17,10 @@ package com.hedera.services.bdd.suites.token.hip540; import static com.hedera.services.bdd.junit.TestTags.TOKEN; -import static com.hedera.services.bdd.spec.HapiSpec.defaultHapiSpec; -import static com.hedera.services.bdd.spec.utilops.UtilVerbs.inParallel; +import static com.hedera.services.bdd.spec.HapiSpec.namedHapiTest; import static com.hedera.services.bdd.suites.token.hip540.Hip540TestScenarios.ALL_HIP_540_SCENARIOS; import com.hedera.services.bdd.junit.HapiTest; -import com.hedera.services.bdd.spec.HapiSpecOperation; import java.util.stream.Stream; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Tag; @@ -31,11 +29,7 @@ public class Hip540Suite { @HapiTest public final Stream allScenariosAsExpected() { - return defaultHapiSpec("allScenariosAsExpected") - .given() - .when() - .then(inParallel(ALL_HIP_540_SCENARIOS.stream() - .map(Hip540TestScenario::asOperation) - .toArray(HapiSpecOperation[]::new))); + return ALL_HIP_540_SCENARIOS.stream() + .map(scenario -> namedHapiTest(scenario.testName(), scenario.asOperation())); } } diff --git a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/utils/contracts/precompile/TokenKeyType.java b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/utils/contracts/precompile/TokenKeyType.java index a60da775fa60..11ff7feddda5 100644 --- a/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/utils/contracts/precompile/TokenKeyType.java +++ b/hedera-node/test-clients/src/main/java/com/hedera/services/bdd/suites/utils/contracts/precompile/TokenKeyType.java @@ -16,6 +16,8 @@ package com.hedera.services.bdd.suites.utils.contracts.precompile; +import java.math.BigInteger; + /** All key types in one place for easy review. */ public enum TokenKeyType { ADMIN_KEY(1), @@ -36,4 +38,8 @@ public enum TokenKeyType { public int value() { return value; } + + public BigInteger asBigInteger() { + return BigInteger.valueOf(value); + } } diff --git a/hedera-node/test-clients/src/main/java/module-info.java b/hedera-node/test-clients/src/main/java/module-info.java index de8e2f9f081f..9c79cb38f01d 100644 --- a/hedera-node/test-clients/src/main/java/module-info.java +++ b/hedera-node/test-clients/src/main/java/module-info.java @@ -3,6 +3,7 @@ exports com.hedera.services.bdd.spec.utilops.inventory; exports com.hedera.services.bdd.suites; exports com.hedera.services.bdd.suites.utils.sysfiles.serdes; + exports com.hedera.services.bdd; exports com.hedera.services.bdd.spec; exports com.hedera.services.bdd.spec.persistence; exports com.hedera.services.bdd.spec.infrastructure; @@ -41,10 +42,10 @@ requires transitive com.hedera.node.app.hapi.fees; requires transitive com.hedera.node.app.hapi.utils; requires transitive com.hedera.node.hapi; + requires transitive com.swirlds.common; requires transitive com.fasterxml.jackson.annotation; requires transitive com.google.common; requires transitive com.google.protobuf; - requires transitive com.swirlds.common; requires transitive headlong; requires transitive info.picocli; requires transitive io.grpc; @@ -56,16 +57,17 @@ requires transitive org.yaml.snakeyaml; requires transitive tuweni.bytes; requires com.hedera.node.app.service.contract.impl; + requires com.hedera.node.app.service.mono; requires com.hedera.node.app; requires com.hedera.node.config; - requires com.fasterxml.jackson.core; - requires com.fasterxml.jackson.databind; - requires com.github.docker.java.api; - requires com.hedera.evm; requires com.swirlds.base; requires com.swirlds.config.api; requires com.swirlds.config.extensions.test.fixtures; requires com.swirlds.platform.core; + requires com.fasterxml.jackson.core; + requires com.fasterxml.jackson.databind; + requires com.github.docker.java.api; + requires com.hedera.evm; requires grpc.netty; requires grpc.stub; requires io.netty.handler; diff --git a/hedera-node/test-clients/src/main/resource/eet-config/ethereum-tx-lazy-create-fuzzing.properties b/hedera-node/test-clients/src/main/resource/eet-config/ethereum-tx-lazy-create-fuzzing.properties deleted file mode 100644 index 91ab8dd373eb..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/ethereum-tx-lazy-create-fuzzing.properties +++ /dev/null @@ -1,10 +0,0 @@ -# Please see BiasedDelegatingProvider (BDP) javadoc for an explanation -# of an operation's "bias"; please see e.g. IdFuzzingProviderFactory to -# see how these properties are used to configure the individual -# operation providers that BDP delegates to. -#### ACTIVE OPS #### -#randomAccount.bias=60 -#randomAccount.ceilingNum=500 -#randomAccountUpdate.bias=30 -randomTransfer.bias=100 -#randomAccountDeletion.bias=2 diff --git a/hedera-node/test-clients/src/main/resource/eet-config/lazycreate-precompile-fuzzing.properties b/hedera-node/test-clients/src/main/resource/eet-config/lazycreate-precompile-fuzzing.properties deleted file mode 100644 index 9c9ebb29bd04..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/lazycreate-precompile-fuzzing.properties +++ /dev/null @@ -1,12 +0,0 @@ -# Please see BiasedDelegatingProvider (BDP) javadoc for an explanation -# of an operation's "bias"; please see e.g. IdFuzzingProviderFactory to -# see how these properties are used to configure the individual -# operation providers that BDP delegates to. -#### ACTIVE OPS #### - -randomAccount.bias=30 -randomHbar.bias=10 -randomFungibleTransfer.bias=10 -randomNonFungibleTransfer.bias=30 -randomERC20Transfer.bias=10 -randomERC721Transfer.bias=10 \ No newline at end of file diff --git a/hedera-node/test-clients/src/main/resource/eet-config/regression-consensus_ops.properties b/hedera-node/test-clients/src/main/resource/eet-config/regression-consensus_ops.properties deleted file mode 100644 index 15c770a14a23..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/regression-consensus_ops.properties +++ /dev/null @@ -1,29 +0,0 @@ -#### AUTOMATION #### -# maxOpsPerSec=50 -####____________#### - -#### ACTIVE OPS #### -randomTopicCreation.bias=3 -randomTopicDeletion.bias=2 -randomTopicUpdate.bias=1 -randomTopicInfo.bias=50 -randomMessageSubmit.bias=1000 -#### CONFIGURED #### -randomTopicCreation.ceilingNum=50 -####____________#### - -#### IGNORE OPS #### -randomCall.bias=0 -randomContract.bias=0 -randomContractDeletion.bias=0 -randomAccount.bias=0 -randomAccountDeletion.bias=0 -randomTransfer.bias=0 -randomAccountUpdate.bias=0 -randomAppend.bias=0 -randomCallLocal.bias=0 -randomAccountInfo.bias=0 -randomAccountRecords.bias=0 -randomRecord.bias=0 -randomReceipt.bias=0 -####____________#### diff --git a/hedera-node/test-clients/src/main/resource/eet-config/regression-contract_txn1.properties b/hedera-node/test-clients/src/main/resource/eet-config/regression-contract_txn1.properties deleted file mode 100644 index 44608977afe5..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/regression-contract_txn1.properties +++ /dev/null @@ -1,27 +0,0 @@ -#### AUTOMATION #### -# maxOpsPerSec=1 -####____________#### - -#### ACTIVE OPS #### -randomContract.bias=1 -randomContractDeletion.bias=1 -randomCall.bias=10 -####____________#### - -#### IGNORE OPS #### -randomAccount.bias=0 -randomAccountDeletion.bias=0 -randomTransfer.bias=0 -randomAccountUpdate.bias=0 -randomAppend.bias=0 -randomFile.bias=0 -randomFileDeletion.bias=0 -randomFileUpdate.bias=0 -randomCallLocal.bias=0 -randomAccountInfo.bias=0 -randomAccountRecords.bias=0 -randomFileInfo.bias=0 -randomContents.bias=0 -randomRecord.bias=0 -randomReceipt.bias=0 -####____________#### diff --git a/hedera-node/test-clients/src/main/resource/eet-config/regression-contract_txns.properties b/hedera-node/test-clients/src/main/resource/eet-config/regression-contract_txns.properties deleted file mode 100644 index e4e6bf72f940..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/regression-contract_txns.properties +++ /dev/null @@ -1,23 +0,0 @@ -#### AUTOMATION #### -# maxOpsPerSec=14 -####____________#### - -#### ACTIVE OPS #### -randomContract.bias=1 -randomContract.ceilingNum=50 -randomContractDeletion.bias=1 -randomCall.bias=5 -####____________#### - -#### IGNORE OPS #### -randomAccount.bias=0 -randomAccountDeletion.bias=0 -randomTransfer.bias=0 -randomAccountUpdate.bias=0 -randomCallLocal.bias=0 -randomAccountInfo.bias=0 -randomAccountRecords.bias=0 -randomRecord.bias=0 -randomReceipt.bias=0 -####____________#### - diff --git a/hedera-node/test-clients/src/main/resource/eet-config/regression-contract_txns6.properties b/hedera-node/test-clients/src/main/resource/eet-config/regression-contract_txns6.properties deleted file mode 100644 index abbe21d652a0..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/regression-contract_txns6.properties +++ /dev/null @@ -1,22 +0,0 @@ -#### AUTOMATION #### -# maxOpsPerSec=8 -####____________#### - -#### ACTIVE OPS #### -randomContract.bias=1 -randomContractDeletion.bias=1 -randomCall.bias=6 -####____________#### - -#### IGNORE OPS #### -randomAccount.bias=0 -randomAccountDeletion.bias=0 -randomTransfer.bias=0 -randomAccountUpdate.bias=0 -randomCallLocal.bias=0 -randomAccountInfo.bias=0 -randomAccountRecords.bias=0 -randomRecord.bias=0 -randomReceipt.bias=0 -####____________#### - diff --git a/hedera-node/test-clients/src/main/resource/eet-config/regression-crypto_txns.properties b/hedera-node/test-clients/src/main/resource/eet-config/regression-crypto_txns.properties deleted file mode 100644 index 14f969f974ab..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/regression-crypto_txns.properties +++ /dev/null @@ -1,23 +0,0 @@ -#### AUTOMATION #### -# maxOpsPerSec=203,maxPendingOps=1000,backoffSleepSecs=30,statusTimeoutSecs=180 -####____________#### - -#### ACTIVE OPS #### -randomAccount.bias=1 -randomAccount.ceilingNum=50 -randomAccountDeletion.bias=1 -randomAccountUpdate.bias=1 -randomTransfer.bias=200 -####____________#### - -#### IGNORE OPS #### -randomCall.bias=0 -randomContract.bias=0 -randomContractDeletion.bias=0 -randomCallLocal.bias=0 -randomAccountInfo.bias=0 -randomAccountRecords.bias=0 -randomRecord.bias=0 -randomReceipt.bias=0 -####____________#### - diff --git a/hedera-node/test-clients/src/main/resource/eet-config/regression-crypto_txns150.properties b/hedera-node/test-clients/src/main/resource/eet-config/regression-crypto_txns150.properties deleted file mode 100644 index 0b3b2be87171..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/regression-crypto_txns150.properties +++ /dev/null @@ -1,23 +0,0 @@ -#### AUTOMATION #### -# maxOpsPerSec=153,maxPendingOps=1000,backoffSleepSecs=30,statusTimeoutSecs=180 -####____________#### - -#### ACTIVE OPS #### -randomAccount.bias=1 -randomAccount.ceilingNum=50 -randomAccountDeletion.bias=1 -randomAccountUpdate.bias=1 -randomTransfer.bias=150 -####____________#### - -#### IGNORE OPS #### -randomCall.bias=0 -randomContract.bias=0 -randomContractDeletion.bias=0 -randomCallLocal.bias=0 -randomAccountInfo.bias=0 -randomAccountRecords.bias=0 -randomRecord.bias=0 -randomReceipt.bias=0 -####____________#### - diff --git a/hedera-node/test-clients/src/main/resource/eet-config/regression-crypto_txns300.properties b/hedera-node/test-clients/src/main/resource/eet-config/regression-crypto_txns300.properties deleted file mode 100644 index 13de1c7a2d5e..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/regression-crypto_txns300.properties +++ /dev/null @@ -1,29 +0,0 @@ -#### AUTOMATION #### -# maxOpsPerSec=314,maxPendingOps=1000,backoffSleepSecs=30 -####____________#### - -#### ACTIVE OPS #### -randomAccount.ceilingNum=50 -randomTransfer.bias=150 -randomAccount.bias=1 -randomAccountDeletion.bias=1 -randomAccountUpdate.bias=5 -####____________#### - -#### IGNORE OPS #### -randomCall.bias=0 -randomContract.bias=0 -randomContractDeletion.bias=0 -randomAppend.bias=0 -randomFile.bias=0 -randomFileDeletion.bias=0 -randomFileUpdate.bias=0 -randomCallLocal.bias=0 -randomAccountInfo.bias=0 -randomAccountRecords.bias=0 -randomFileInfo.bias=0 -randomContents.bias=0 -randomRecord.bias=0 -randomReceipt.bias=0 -####____________#### - diff --git a/hedera-node/test-clients/src/main/resource/eet-config/regression-crypto_txns50.properties b/hedera-node/test-clients/src/main/resource/eet-config/regression-crypto_txns50.properties deleted file mode 100644 index e62bc1d16037..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/regression-crypto_txns50.properties +++ /dev/null @@ -1,23 +0,0 @@ -#### AUTOMATION #### -# maxOpsPerSec=53,maxPendingOps=1000,backoffSleepSecs=30,statusTimeoutSecs=180 -####____________#### - -#### ACTIVE OPS #### -randomAccount.bias=1 -randomAccount.ceilingNum=50 -randomAccountDeletion.bias=1 -randomAccountUpdate.bias=1 -randomTransfer.bias=50 -####____________#### - -#### IGNORE OPS #### -randomCall.bias=0 -randomContract.bias=0 -randomContractDeletion.bias=0 -randomCallLocal.bias=0 -randomAccountInfo.bias=0 -randomAccountRecords.bias=0 -randomRecord.bias=0 -randomReceipt.bias=0 -####____________#### - diff --git a/hedera-node/test-clients/src/main/resource/eet-config/regression-default.properties b/hedera-node/test-clients/src/main/resource/eet-config/regression-default.properties deleted file mode 100644 index 7e7283c8c96c..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/regression-default.properties +++ /dev/null @@ -1,33 +0,0 @@ -#### AUTOMATION #### -# maxOpsPerSec=500 -######################## - -#### Meta #### -randomRecord.bias=50 -randomReceipt.bias=10 -######################## - -#### Crypto #### -randomTransfer.bias=100 -randomTransfer.recordProbability=0.10 -randomAccount.bias=1 -randomAccount.ceilingNum=100 -randomAccountDeletion.bias=1 -randomAccountUpdate.bias=2 -randomAccountInfo.bias=5 -randomAccountRecords.bias=5 -######################## - -#### File #### -######################## - -#### Contract #### -randomContract.bias=1 -randomContract.ceilingNum=50 -randomContractDeletion.bias=1 -randomCall.bias=5 -randomCallLocal.bias=10 -######################## - -#### Consensus #### -######################## diff --git a/hedera-node/test-clients/src/main/resource/eet-config/regression-file_ops.properties b/hedera-node/test-clients/src/main/resource/eet-config/regression-file_ops.properties deleted file mode 100644 index c03eb06210f5..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/regression-file_ops.properties +++ /dev/null @@ -1,30 +0,0 @@ -#### AUTOMATION #### -# maxOpsPerSec=16 -####____________#### - -#### ACTIVE OPS #### -randomFile.bias=2 -randomFileDeletion.bias=1 -randomFileUpdate.bias=5 -randomFileInfo.bias=5 -randomContents.bias=5 -#### CONFIGURED #### -randomFile.ceilingNum=25 -####____________#### - -#### IGNORE OPS #### -randomCall.bias=0 -randomContract.bias=0 -randomContractDeletion.bias=0 -randomAccount.bias=0 -randomAccountDeletion.bias=0 -randomTransfer.bias=0 -randomAccountUpdate.bias=0 -randomAppend.bias=0 -randomCallLocal.bias=0 -randomAccountInfo.bias=0 -randomAccountRecords.bias=0 -randomRecord.bias=0 -randomReceipt.bias=0 -####____________#### - diff --git a/hedera-node/test-clients/src/main/resource/eet-config/regression-nodel_contract.properties b/hedera-node/test-clients/src/main/resource/eet-config/regression-nodel_contract.properties deleted file mode 100644 index 24d50db57a40..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/regression-nodel_contract.properties +++ /dev/null @@ -1,23 +0,0 @@ -#### AUTOMATION #### -# maxOpsPerSec=6 -####____________#### - -#### ACTIVE OPS #### -randomContract.bias=1 -randomContract.ceilingNum=50 -randomCall.bias=5 -####____________#### - -#### IGNORE OPS #### -randomContractDeletion.bias=0 -randomAccount.bias=0 -randomAccountDeletion.bias=0 -randomTransfer.bias=0 -randomAccountUpdate.bias=0 -randomCallLocal.bias=0 -randomAccountInfo.bias=0 -randomAccountRecords.bias=0 -randomRecord.bias=0 -randomReceipt.bias=0 -####____________#### - diff --git a/hedera-node/test-clients/src/main/resource/eet-config/regression-nodel_crypto.properties b/hedera-node/test-clients/src/main/resource/eet-config/regression-nodel_crypto.properties deleted file mode 100644 index f1aa2ed0b472..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/regression-nodel_crypto.properties +++ /dev/null @@ -1,23 +0,0 @@ -#### AUTOMATION #### -# maxOpsPerSec=325,maxPendingOps=1625,backoffSleepSecs=5 -####____________#### - -#### ACTIVE OPS #### -randomAccount.bias=1 -randomAccount.ceilingNum=50 -randomAccountUpdate.bias=1 -randomTransfer.bias=63 -####____________#### - -#### IGNORE OPS #### -randomCall.bias=0 -randomContract.bias=0 -randomContractDeletion.bias=0 -randomAccountDeletion.bias=0 -randomCallLocal.bias=0 -randomAccountInfo.bias=0 -randomAccountRecords.bias=0 -randomRecord.bias=0 -randomReceipt.bias=0 -####____________#### - diff --git a/hedera-node/test-clients/src/main/resource/eet-config/regression-small-mixed_ops.properties b/hedera-node/test-clients/src/main/resource/eet-config/regression-small-mixed_ops.properties deleted file mode 100644 index ff8df5b25fe1..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/regression-small-mixed_ops.properties +++ /dev/null @@ -1,36 +0,0 @@ -#### AUTOMATION #### -# maxOpsPerSec=500 -####____________#### - -#### ACTIVE OPS #### -randomTopicCreation.bias=2 -randomTopicDeletion.bias=1 -randomTopicUpdate.bias=3 -randomMessageSubmit.bias=100 -randomAccount.bias=1 -randomAccount.ceilingNum=200 -randomAccountDeletion.bias=1 -randomAccountUpdate.bias=25 -randomAccountInfo.bias=25 -randomAccountRecords.bias=5 -randomTransfer.bias=120 -randomToken.bias=3 -randomTokenAssociation.bias=100 -randomTokenAssociation.ceilingNum=1000 -randomTokenDissociation.bias=10 -randomTokenDeletion.bias=1 -randomTokenTransfer.bias=100 -randomTokenKycGrant.bias=10 -randomTokenKycRevoke.bias=5 -randomTokenMint.bias=5 -randomTokenBurn.bias=5 -randomTokenAccountWipe.bias=10 -randomTokenUpdate.bias=20 -randomSchedule.bias=3 -randomScheduleDelete.bias=10 -randomScheduleSign.bias=5 - -#### CONFIGURED #### -randomTopicCreation.ceilingNum=50 -randomSchedule.ceilingNum=50 -####____________#### diff --git a/hedera-node/test-clients/src/main/resource/eet-config/regression-std_contract.properties b/hedera-node/test-clients/src/main/resource/eet-config/regression-std_contract.properties deleted file mode 100644 index 2f2c056809b5..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/regression-std_contract.properties +++ /dev/null @@ -1,22 +0,0 @@ -#### AUTOMATION #### -# maxOpsPerSec=7 -####____________#### - -#### ACTIVE OPS #### -randomContract.bias=1 -randomContractDeletion.bias=1 -randomCall.bias=5 -####____________#### - -#### IGNORE OPS #### -randomAccount.bias=0 -randomAccountDeletion.bias=0 -randomTransfer.bias=0 -randomAccountUpdate.bias=0 -randomCallLocal.bias=0 -randomAccountInfo.bias=0 -randomAccountRecords.bias=0 -randomRecord.bias=0 -randomReceipt.bias=0 -####____________#### - diff --git a/hedera-node/test-clients/src/main/resource/eet-config/regression-std_crypto.properties b/hedera-node/test-clients/src/main/resource/eet-config/regression-std_crypto.properties deleted file mode 100644 index e8b0f18e0da4..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/regression-std_crypto.properties +++ /dev/null @@ -1,22 +0,0 @@ -#### AUTOMATION #### -# maxOpsPerSec=324,maxPendingOps=1625,backoffSleepSecs=5 -####____________#### - -#### ACTIVE OPS #### -randomAccount.bias=1 -randomAccountDeletion.bias=1 -randomAccountUpdate.bias=1 -randomTransfer.bias=105 -####____________#### - -#### IGNORE OPS #### -randomCall.bias=0 -randomContract.bias=0 -randomContractDeletion.bias=0 -randomCallLocal.bias=0 -randomAccountInfo.bias=0 -randomAccountRecords.bias=0 -randomRecord.bias=0 -randomReceipt.bias=0 -####____________#### - diff --git a/hedera-node/test-clients/src/main/resource/eet-config/regression-upd_only_crypto.properties b/hedera-node/test-clients/src/main/resource/eet-config/regression-upd_only_crypto.properties deleted file mode 100644 index 751ee1da2b16..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/regression-upd_only_crypto.properties +++ /dev/null @@ -1,23 +0,0 @@ -#### AUTOMATION #### -# maxOpsPerSec=26 -####____________#### - -#### ACTIVE OPS #### -randomAccount.bias=1 -randomAccount.ceilingNum=50 -randomAccountUpdate.bias=25 -####____________#### - -#### IGNORE OPS #### -randomCall.bias=0 -randomContract.bias=0 -randomContractDeletion.bias=0 -randomAccountDeletion.bias=0 -randomTransfer.bias=0 -randomCallLocal.bias=0 -randomAccountInfo.bias=0 -randomAccountRecords.bias=0 -randomRecord.bias=0 -randomReceipt.bias=0 -####____________#### - diff --git a/hedera-node/test-clients/src/main/resource/eet-config/regression-xfer_only_crypto.properties b/hedera-node/test-clients/src/main/resource/eet-config/regression-xfer_only_crypto.properties deleted file mode 100644 index 23a0b208a6cf..000000000000 --- a/hedera-node/test-clients/src/main/resource/eet-config/regression-xfer_only_crypto.properties +++ /dev/null @@ -1,23 +0,0 @@ -#### AUTOMATION #### -# maxOpsPerSec=320,maxPendingOps=1625,backoffSleepSecs=5 -####____________#### - -#### ACTIVE OPS #### -randomAccount.bias=1 -randomAccount.ceilingNum=50 -randomTransfer.bias=63 -####____________#### - -#### IGNORE OPS #### -randomCall.bias=0 -randomContract.bias=0 -randomContractDeletion.bias=0 -randomAccountDeletion.bias=0 -randomAccountUpdate.bias=0 -randomCallLocal.bias=0 -randomAccountInfo.bias=0 -randomAccountRecords.bias=0 -randomRecord.bias=0 -randomReceipt.bias=0 -####____________#### - diff --git a/hedera-node/test-clients/src/main/resource/feeSchedule.txt b/hedera-node/test-clients/src/main/resource/feeSchedule.txt deleted file mode 100644 index 2205d4b66227..000000000000 --- a/hedera-node/test-clients/src/main/resource/feeSchedule.txt +++ /dev/null @@ -1,212 +0,0 @@ - -Û# -‹ -)€€š¦ê¯ã…ÔŠ ›ìj(î¼é¦0 8X@–[P›ìjXôÕ.€€š¦ê¯ãÃÄ؃5 Ýþì -(™—ÛøÓ0Ãv8ò@£¡ PÝþì -XâÜ".€€š¦ê¯ãÃÄ؃5 Ýþì -(™—ÛøÓ0Ãv8ò@£¡ PÝþì -XâÜ" -‚!~ -&€€š¦ê¯ãêþü" ’”(¯µå‹0N8@ŽP’”Xô)€€š¦ê¯ãåðØÆ î…](൦™0ø8L@³OPî…]Xà©)€€š¦ê¯ãåðØÆ î…](൦™0ø8L@³OPî…]Xà© -{ -%€€š¦ê¯ãÓ™´ çª(©‡Ì-08@ÿPçªX¼(€€š¦ê¯ã¼Í¦” ½¬(‘ßÜÐ0Ë8@óP½¬XŽa(€€š¦ê¯ã¼Í¦” ½¬(‘ßÜÐ0Ë8@óP½¬XŽa -|x -$€€š¦ê¯ãœª¹ ÙŽ(í†ã0 8@zPÙŽXÉ'€€š¦ê¯ãê¤éF ˆ¿(Ù‡›0ž8 @¯ Pˆ¿X°.'€€š¦ê¯ãê¤éF ˆ¿(Ù‡›0ž8 @¯ Pˆ¿X°. -Ž‰ -)€€š¦ê¯ããßÁ‘ –ü7(ßž•Å0ã8.@á/P–ü7X³-€€š¦ê¯ã…ÝÖã ŸÎ×(×”ƒo0‹>8Ô@óìPŸÎ×X®˜-€€š¦ê¯ã…ÝÖã ŸÎ×(×”ƒo0‹>8Ô@óìPŸÎ×X®˜ -š• --€€š¦ê¯ãšëå¨ À–—(òÂÓ˜e0Ì88Ÿ@ôµPÀ–—X•Ê1€€š¦ê¯ãÏñ«’É Ƥ­C(Ëå½À£ -0×ß8—7@ê¼9PƤ­CXÄ×1€€š¦ê¯ãÏñ«’É Ƥ­C(Ëå½À£ -0×ß8—7@ê¼9PƤ­CXÄ× -Ž‰ -)€€š¦ê¯ãú©ë ¦Ð7(Ó ¼¾0à8.@¼/P¦Ð7X²-€€š¦ê¯ãµ¡óÍ ê“Ó(½¨¬n0Û=8Ð@ŒéPê“ÓXŒŠ-€€š¦ê¯ãµ¡óÍ ê“Ó(½¨¬n0Û=8Ð@ŒéPê“ÓXŒŠ -_[ -€€š¦ê¯ãŠŽ l(Ͷ@PlX€€š¦ê¯ã‚·5 ÷ -(ëÅÕ0@ P÷ -X#€€š¦ê¯ã‚·5 ÷ -(ëÅÕ0@ P÷ -X# -WS -€€š¦ê¯ãœ¬ #(©°P#X€€š¦ê¯ãð¾ Ê(›ôE@PÊX €€š¦ê¯ãð¾ Ê(›ôE@PÊX -_ [ -€€š¦ê¯ã¦™ n(ºã@PnX€€š¦ê¯ãòÈ6 • (ðŒÚ0@ -P• X$€€š¦ê¯ãòÈ6 • (ðŒÚ0@ -P• X$ -_[ -€€š¦ê¯ãöó f(ˆÎ@PfX€€š¦ê¯ãÿâ2 ² -(íöÊ0@ P² -X!€€š¦ê¯ãÿâ2 ² -(íöÊ0@ P² -X! -¦¡ -1€€š¦ê¯ã™ª—ÍÍ à°‰*(Ä”œßµ0ÇË8¼"@Ãó#Pà°‰*XœÑ†5€€š¦ê¯ãʤ¯ëð áùù¢(òŠîÕºS0—Ö.8ˆÀ@êÝÒPáùù¢XìŸÖ 5€€š¦ê¯ãʤ¯ëð áùù¢(òŠîÕºS0—Ö.8ˆÀ@êÝÒPáùù¢XìŸÖ -˜“ --€€š¦ê¯ã¸ïʃ æí (ë´à‰,0Ó8í@¿öPæí X¬œ0€€š¦ê¯ãۨͮ ©“«(ë®åþ¼0¸À8„@³„P©“«X¾ð]0€€š¦ê¯ãۨͮ ©“«(ë®åþ¼0¸À8„@³„P©“«X¾ð] -‹ -)€€š¦ê¯ãÃÇ¥ „ìN(ÿõƒ 0Ý8A@¤CP„ìNX§ü.€€š¦ê¯ãì¡è' µü€(üõý²œ0»W8È@ÒêPµü€XõÏ.€€š¦ê¯ãì¡è' µü€(üõý²œ0»W8È@ÒêPµü€XõÏ -š• --€€š¦ê¯ã²°ÚŽ ûó‹(ÌðÄ´<0ä!8Ä@ëÑPûó‹Xóò 1€€š¦ê¯ãõ–¿Ä Àãš((Ö·ÿª‘0™·8ø @õ§"PÀãš(XØÕ€1€€š¦ê¯ãõ–¿Ä Àãš((Ö·ÿª‘0™·8ø @õ§"PÀãš(XØÕ€ -_[ -€€š¦ê¯ãçª r(¶©@PrX€€š¦ê¯ã¾¬8 Ä (šá0@ -PÄ X%€€š¦ê¯ã¾¬8 Ä (šá0@ -PÄ X% -–‘ --€€š¦ê¯ã†òËÜ á×þ(ÿø©î&0Þ8Ñ@ªÙPá×þXÿ®/€€š¦ê¯ãÎÊÚ³~ çôî(ñ¤¡šù0Äš8˜@¨‰PçôîXöâR/€€š¦ê¯ãÎÊÚ³~ çôî(ñ¤¡šù0Äš8˜@¨‰PçôîXöâR -®© -5€€š¦ê¯ã¥ð–ŽÌ …ñݬ(‘àÓÙ­0ÛÞ8À@³“P…ñݬXÐߨ7€€š¦ê¯ãݳ©¸ÝU ½½ÄÅ(ÝáÀÒÖ0Ï¿8ů@§˜üP½½ÄÅX’Û‘87€€š¦ê¯ãݳ©¸ÝU ½½ÄÅ(ÝáÀÒÖ0Ï¿8ů@§˜üP½½ÄÅX’Û‘8 -_[ -€€š¦ê¯ã‰ž o(Áö@PoX€€š¦ê¯ãð†7 ¢ (΄Ü0@ -P¢ X$€€š¦ê¯ãð†7 ¢ (΄Ü0@ -P¢ X$ -TP -€€š¦ê¯ãíN (“»P€€š¦ê¯ãˆ Ò(ô€ @PÒX€€š¦ê¯ãˆ Ò(ô€ @PÒX -˜"“ --€€š¦ê¯ã³¯·µ ò¸ß(„›“Ð50ÿ8 @ó«Pò¸ßXÐä0€€š¦ê¯ã’è϶® ÐãØ#(¶ßù‘¹0ï…8Ÿ@غPÐãØ#XŒœr0€€š¦ê¯ã’è϶® ÐãØ#(¶ßù‘¹0ï…8Ÿ@غPÐãØ#XŒœr -š • --€€š¦ê¯ã¡»ÊÉ è’²(ë¿·ŸB0†%8ä@¼òPè’²X£í -1€€š¦ê¯ã®‚ȼ× Îô‹,(í½Ñ˜Ý0Ïá8$@’Ð%PÎô‹,XÂŒ1€€š¦ê¯ã®‚ȼ× Îô‹,(í½Ñ˜Ý0Ïá8$@’Ð%PÎô‹,XÂŒ - } -%€€š¦ê¯ã×úŸ ̬(¿ÿöT0/8@ÛP̬Xõ )€€š¦ê¯ã溟” ÞÃ8(³ùŠÐ0é8.@ž0PÞÃ8Xò´)€€š¦ê¯ã溟” ÞÃ8(³ùŠÐ0é8.@ž0PÞÃ8Xò´ - ‹ -)€€š¦ê¯ãÃÇ¥ „ìN(ÿõƒ 0Ý8A@¤CP„ìNX§ü.€€š¦ê¯ãì¡è' µü€(üõý²œ0»W8È@ÒêPµü€XõÏ.€€š¦ê¯ãì¡è' µü€(üõý²œ0»W8È@ÒêPµü€XõÏ -š -• --€€š¦ê¯ã¨¨˜× ¡÷´(ΟéÕB0¤%8æ@íôP¡÷´X—ö -1€€š¦ê¯ãƒŒ¼íØ ¬Ž°,(úšØÛâ0Úä8­$@„ï%P¬Ž°,X®€Ž1€€š¦ê¯ãƒŒ¼íØ ¬Ž°,(úšØÛâ0Úä8­$@„ï%P¬Ž°,X®€Ž -_[ -€€š¦ê¯ã¼ o(Žô@PoX€€š¦ê¯ã‰ÿ6   (´åÛ0@ -P  X$€€š¦ê¯ã‰ÿ6   (´åÛ0@ -P  X$ -_[ -€€š¦ê¯ã¨« r(¸«@PrX€€š¦ê¯ã†³8 Å (Ü´á0@ -PÅ X%€€š¦ê¯ã†³8 Å (Ü´á0@ -PÅ X% -2‹ -)€€š¦ê¯ã™†ò– ººm(•ÞéÙ0¬ 8Z@´]PººmX¡Þ.€€š¦ê¯ãÇÐÊ©6 ø÷Ž (•ÈÞÙ0¶y8Ž @Ÿ¾ Pø÷Ž X³É#.€€š¦ê¯ãÇÐÊ©6 ø÷Ž (•ÈÞÙ0¶y8Ž @Ÿ¾ Pø÷Ž X³É# -3{ -%€€š¦ê¯ã„Ü €³(™„ì.08@†P€³XÖ(€€š¦ê¯ã¶ù®˜ û–(Ķüà0Õ8@ÎPû–Xãc(€€š¦ê¯ã¶ù®˜ û–(Ķüà0Õ8@ÎPû–Xãc -Ž4‰ -)€€š¦ê¯ãùéš“ ̨8(•íøË0ç8.@‡0P̨8Xœ´-€€š¦ê¯ã¨áÜù ÝÜ(‹£Ûo0½>8Ø@ßðPÝÜXæ¦-€€š¦ê¯ã¨áÜù ÝÜ(‹£Ûo0½>8Ø@ßðPÝÜXæ¦ -|6x -$€€š¦ê¯ã¹å® ÅŒ(“ø¸0 8@xPÅŒXÂ'€€š¦ê¯ãæ¦àE „£(ý™ä–0œ8 @— P„£XÖ-'€€š¦ê¯ãæ¦àE „£(ý™ä–0œ8 @— P„£XÖ- -_5[ -€€š¦ê¯ãýõ g(£Ö@PgX€€š¦ê¯ãÙý2 · -(ÊáË0@ P· -X!€€š¦ê¯ãÙý2 · -(ÊáË0@ P· -X! -š#• --€€š¦ê¯ãñÙ¤ É’ü(ƒÒÚˆa0¤68‰@ìžPÉ’üXÕó1€€š¦ê¯ã‡½Ý» °ñÍ@(¡ªšñí 0ÙÁ8÷4@ü7P°ñÍ@XÑßÎ1€€š¦ê¯ã‡½Ý» °ñÍ@(¡ªšñí 0ÙÁ8÷4@ü7P°ñÍ@XÑßÎ -+$' - €€š¦ê¯ã €€š¦ê¯ã €€š¦ê¯ã -%! - €€š¦ê¯ã €€š¦ê¯ã €€š¦ê¯ã -%! - €€š¦ê¯ã €€š¦ê¯ã €€š¦ê¯ã -_[ -€€š¦ê¯ãͪ W(„© @PWX€€š¦ê¯ãí©+ ï(±•­0@PïX€€š¦ê¯ãí©+ ï(±•­0@PïXéä²ÿÛ# -‹ -)€€š¦ê¯ã…ÔŠ ›ìj(î¼é¦0 8X@–[P›ìjXôÕ.€€š¦ê¯ãÃÄ؃5 Ýþì -(™—ÛøÓ0Ãv8ò@£¡ PÝþì -XâÜ".€€š¦ê¯ãÃÄ؃5 Ýþì -(™—ÛøÓ0Ãv8ò@£¡ PÝþì -XâÜ" -‚!~ -&€€š¦ê¯ãêþü" ’”(¯µå‹0N8@ŽP’”Xô)€€š¦ê¯ãåðØÆ î…](൦™0ø8L@³OPî…]Xà©)€€š¦ê¯ãåðØÆ î…](൦™0ø8L@³OPî…]Xà© -{ -%€€š¦ê¯ãÓ™´ çª(©‡Ì-08@ÿPçªX¼(€€š¦ê¯ã¼Í¦” ½¬(‘ßÜÐ0Ë8@óP½¬XŽa(€€š¦ê¯ã¼Í¦” ½¬(‘ßÜÐ0Ë8@óP½¬XŽa -|x -$€€š¦ê¯ãœª¹ ÙŽ(í†ã0 8@zPÙŽXÉ'€€š¦ê¯ãê¤éF ˆ¿(Ù‡›0ž8 @¯ Pˆ¿X°.'€€š¦ê¯ãê¤éF ˆ¿(Ù‡›0ž8 @¯ Pˆ¿X°. -Ž‰ -)€€š¦ê¯ããßÁ‘ –ü7(ßž•Å0ã8.@á/P–ü7X³-€€š¦ê¯ã…ÝÖã ŸÎ×(×”ƒo0‹>8Ô@óìPŸÎ×X®˜-€€š¦ê¯ã…ÝÖã ŸÎ×(×”ƒo0‹>8Ô@óìPŸÎ×X®˜ -š• --€€š¦ê¯ãšëå¨ À–—(òÂÓ˜e0Ì88Ÿ@ôµPÀ–—X•Ê1€€š¦ê¯ãÏñ«’É Ƥ­C(Ëå½À£ -0×ß8—7@ê¼9PƤ­CXÄ×1€€š¦ê¯ãÏñ«’É Ƥ­C(Ëå½À£ -0×ß8—7@ê¼9PƤ­CXÄ× -Ž‰ -)€€š¦ê¯ãú©ë ¦Ð7(Ó ¼¾0à8.@¼/P¦Ð7X²-€€š¦ê¯ãµ¡óÍ ê“Ó(½¨¬n0Û=8Ð@ŒéPê“ÓXŒŠ-€€š¦ê¯ãµ¡óÍ ê“Ó(½¨¬n0Û=8Ð@ŒéPê“ÓXŒŠ -_[ -€€š¦ê¯ãŠŽ l(Ͷ@PlX€€š¦ê¯ã‚·5 ÷ -(ëÅÕ0@ P÷ -X#€€š¦ê¯ã‚·5 ÷ -(ëÅÕ0@ P÷ -X# -WS -€€š¦ê¯ãœ¬ #(©°P#X€€š¦ê¯ãð¾ Ê(›ôE@PÊX €€š¦ê¯ãð¾ Ê(›ôE@PÊX -_ [ -€€š¦ê¯ã¦™ n(ºã@PnX€€š¦ê¯ãòÈ6 • (ðŒÚ0@ -P• X$€€š¦ê¯ãòÈ6 • (ðŒÚ0@ -P• X$ -_[ -€€š¦ê¯ãöó f(ˆÎ@PfX€€š¦ê¯ãÿâ2 ² -(íöÊ0@ P² -X!€€š¦ê¯ãÿâ2 ² -(íöÊ0@ P² -X! -¦¡ -1€€š¦ê¯ã™ª—ÍÍ à°‰*(Ä”œßµ0ÇË8¼"@Ãó#Pà°‰*XœÑ†5€€š¦ê¯ãʤ¯ëð áùù¢(òŠîÕºS0—Ö.8ˆÀ@êÝÒPáùù¢XìŸÖ 5€€š¦ê¯ãʤ¯ëð áùù¢(òŠîÕºS0—Ö.8ˆÀ@êÝÒPáùù¢XìŸÖ -˜“ --€€š¦ê¯ã¸ïʃ æí (ë´à‰,0Ó8í@¿öPæí X¬œ0€€š¦ê¯ãۨͮ ©“«(ë®åþ¼0¸À8„@³„P©“«X¾ð]0€€š¦ê¯ãۨͮ ©“«(ë®åþ¼0¸À8„@³„P©“«X¾ð] -‹ -)€€š¦ê¯ãÃÇ¥ „ìN(ÿõƒ 0Ý8A@¤CP„ìNX§ü.€€š¦ê¯ãì¡è' µü€(üõý²œ0»W8È@ÒêPµü€XõÏ.€€š¦ê¯ãì¡è' µü€(üõý²œ0»W8È@ÒêPµü€XõÏ -š• --€€š¦ê¯ã²°ÚŽ ûó‹(ÌðÄ´<0ä!8Ä@ëÑPûó‹Xóò 1€€š¦ê¯ãõ–¿Ä Àãš((Ö·ÿª‘0™·8ø @õ§"PÀãš(XØÕ€1€€š¦ê¯ãõ–¿Ä Àãš((Ö·ÿª‘0™·8ø @õ§"PÀãš(XØÕ€ -_[ -€€š¦ê¯ãçª r(¶©@PrX€€š¦ê¯ã¾¬8 Ä (šá0@ -PÄ X%€€š¦ê¯ã¾¬8 Ä (šá0@ -PÄ X% -–‘ --€€š¦ê¯ã†òËÜ á×þ(ÿø©î&0Þ8Ñ@ªÙPá×þXÿ®/€€š¦ê¯ãÎÊÚ³~ çôî(ñ¤¡šù0Äš8˜@¨‰PçôîXöâR/€€š¦ê¯ãÎÊÚ³~ çôî(ñ¤¡šù0Äš8˜@¨‰PçôîXöâR -®© -5€€š¦ê¯ã¥ð–ŽÌ …ñݬ(‘àÓÙ­0ÛÞ8À@³“P…ñݬXÐߨ7€€š¦ê¯ãݳ©¸ÝU ½½ÄÅ(ÝáÀÒÖ0Ï¿8ů@§˜üP½½ÄÅX’Û‘87€€š¦ê¯ãݳ©¸ÝU ½½ÄÅ(ÝáÀÒÖ0Ï¿8ů@§˜üP½½ÄÅX’Û‘8 -_[ -€€š¦ê¯ã‰ž o(Áö@PoX€€š¦ê¯ãð†7 ¢ (΄Ü0@ -P¢ X$€€š¦ê¯ãð†7 ¢ (΄Ü0@ -P¢ X$ -TP -€€š¦ê¯ãíN (“»P€€š¦ê¯ãˆ Ò(ô€ @PÒX€€š¦ê¯ãˆ Ò(ô€ @PÒX -˜"“ --€€š¦ê¯ã³¯·µ ò¸ß(„›“Ð50ÿ8 @ó«Pò¸ßXÐä0€€š¦ê¯ã’è϶® ÐãØ#(¶ßù‘¹0ï…8Ÿ@غPÐãØ#XŒœr0€€š¦ê¯ã’è϶® ÐãØ#(¶ßù‘¹0ï…8Ÿ@غPÐãØ#XŒœr -š • --€€š¦ê¯ã¡»ÊÉ è’²(ë¿·ŸB0†%8ä@¼òPè’²X£í -1€€š¦ê¯ã®‚ȼ× Îô‹,(í½Ñ˜Ý0Ïá8$@’Ð%PÎô‹,XÂŒ1€€š¦ê¯ã®‚ȼ× Îô‹,(í½Ñ˜Ý0Ïá8$@’Ð%PÎô‹,XÂŒ - } -%€€š¦ê¯ã×úŸ ̬(¿ÿöT0/8@ÛP̬Xõ )€€š¦ê¯ã溟” ÞÃ8(³ùŠÐ0é8.@ž0PÞÃ8Xò´)€€š¦ê¯ã溟” ÞÃ8(³ùŠÐ0é8.@ž0PÞÃ8Xò´ - ‹ -)€€š¦ê¯ãÃÇ¥ „ìN(ÿõƒ 0Ý8A@¤CP„ìNX§ü.€€š¦ê¯ãì¡è' µü€(üõý²œ0»W8È@ÒêPµü€XõÏ.€€š¦ê¯ãì¡è' µü€(üõý²œ0»W8È@ÒêPµü€XõÏ -š -• --€€š¦ê¯ã¨¨˜× ¡÷´(ΟéÕB0¤%8æ@íôP¡÷´X—ö -1€€š¦ê¯ãƒŒ¼íØ ¬Ž°,(úšØÛâ0Úä8­$@„ï%P¬Ž°,X®€Ž1€€š¦ê¯ãƒŒ¼íØ ¬Ž°,(úšØÛâ0Úä8­$@„ï%P¬Ž°,X®€Ž -_[ -€€š¦ê¯ã¼ o(Žô@PoX€€š¦ê¯ã‰ÿ6   (´åÛ0@ -P  X$€€š¦ê¯ã‰ÿ6   (´åÛ0@ -P  X$ -_[ -€€š¦ê¯ã¨« r(¸«@PrX€€š¦ê¯ã†³8 Å (Ü´á0@ -PÅ X%€€š¦ê¯ã†³8 Å (Ü´á0@ -PÅ X% -2‹ -)€€š¦ê¯ã™†ò– ººm(•ÞéÙ0¬ 8Z@´]PººmX¡Þ.€€š¦ê¯ãÇÐÊ©6 ø÷Ž (•ÈÞÙ0¶y8Ž @Ÿ¾ Pø÷Ž X³É#.€€š¦ê¯ãÇÐÊ©6 ø÷Ž (•ÈÞÙ0¶y8Ž @Ÿ¾ Pø÷Ž X³É# -3{ -%€€š¦ê¯ã„Ü €³(™„ì.08@†P€³XÖ(€€š¦ê¯ã¶ù®˜ û–(Ķüà0Õ8@ÎPû–Xãc(€€š¦ê¯ã¶ù®˜ û–(Ķüà0Õ8@ÎPû–Xãc -Ž4‰ -)€€š¦ê¯ãùéš“ ̨8(•íøË0ç8.@‡0P̨8Xœ´-€€š¦ê¯ã¨áÜù ÝÜ(‹£Ûo0½>8Ø@ßðPÝÜXæ¦-€€š¦ê¯ã¨áÜù ÝÜ(‹£Ûo0½>8Ø@ßðPÝÜXæ¦ -|6x -$€€š¦ê¯ã¹å® ÅŒ(“ø¸0 8@xPÅŒXÂ'€€š¦ê¯ãæ¦àE „£(ý™ä–0œ8 @— P„£XÖ-'€€š¦ê¯ãæ¦àE „£(ý™ä–0œ8 @— P„£XÖ- -_5[ -€€š¦ê¯ãýõ g(£Ö@PgX€€š¦ê¯ãÙý2 · -(ÊáË0@ P· -X!€€š¦ê¯ãÙý2 · -(ÊáË0@ P· -X! -š#• --€€š¦ê¯ãñÙ¤ É’ü(ƒÒÚˆa0¤68‰@ìžPÉ’üXÕó1€€š¦ê¯ã‡½Ý» °ñÍ@(¡ªšñí 0ÙÁ8÷4@ü7P°ñÍ@XÑßÎ1€€š¦ê¯ã‡½Ý» °ñÍ@(¡ªšñí 0ÙÁ8÷4@ü7P°ñÍ@XÑßÎ -+$' - €€š¦ê¯ã €€š¦ê¯ã €€š¦ê¯ã -%! - €€š¦ê¯ã €€š¦ê¯ã €€š¦ê¯ã -%! - €€š¦ê¯ã €€š¦ê¯ã €€š¦ê¯ã -_[ -€€š¦ê¯ãͪ W(„© @PWX€€š¦ê¯ãí©+ ï(±•­0@PïX€€š¦ê¯ãí©+ ï(±•­0@PïXµ¡¸Ž \ No newline at end of file diff --git a/hedera-node/test-clients/src/main/resource/feeSchedule_CHANGED.txt b/hedera-node/test-clients/src/main/resource/feeSchedule_CHANGED.txt deleted file mode 100644 index 14afe3ea16ad..000000000000 --- a/hedera-node/test-clients/src/main/resource/feeSchedule_CHANGED.txt +++ /dev/null @@ -1,138 +0,0 @@ - -Ö -’ -)€€š¦ê¯ã¼öþ» °ýú(Ž¼ùé90ž8@« Pøò%X£y/€€š¦ê¯ãÀ‹Ö£´ Ž›¼s(ø¦¤Ï0~8» @Ÿí PÛ‚Æ XÕù$/€€š¦ê¯ãÀ‹Ö£´ Ž›¼s(ø¦¤Ï0~8» @Ÿí PÛ‚Æ XÕù$ -!} -%€€š¦ê¯ã“Öå Ï(À¯‘308@žPÏX°)€€š¦ê¯ãæŸýò æf(®¼¥Ê0Û8T@‘WPæfXßÆ)€€š¦ê¯ãæŸýò æf(®¼¥Ê0Û8T@‘WPæfXßÆ -|x -"€€š¦ê¯ã§€Ž Òk(»¦¶0 8@\PÒkXØ(€€š¦ê¯ãö‹¢  ðå (€ÜÅ€0æ8@þPðå Xùh(€€š¦ê¯ãö‹¢  ðå (€ÜÅ€0æ8@þPðå Xùh -xt - €€š¦ê¯ã‹„ò Ä1(Ø«Ç0@*PÄ1Xž'€€š¦ê¯ã´ŸßI Ë‹(õ§Þ¦0¥8 @ð PË‹X¥0'€€š¦ê¯ã´ŸßI Ë‹(õ§Þ¦0¥8 @ð PË‹X¥0 -Œ‡ -'€€š¦ê¯ãÎóö^ ¾¶(ú´û0Ô8@ÊP¾¶X•>-€€š¦ê¯ãåœö îÌõ(•ñÌs0Ó@8í@¾†PîÌõX©ø-€€š¦ê¯ãåœö îÌõ(•ñÌs0Ó@8í@¾†PîÌõX©ø -š• --€€š¦ê¯ãÏ÷“ó ƒŽé(–üõÈ#0ò8¿@öÆPƒŽéXúé1€€š¦ê¯ã•¹‰‹Û ˆ£ƒG(Ëêø›ë -0å‡8˜:@òÍ(ä«ÅÙ¿ 0ï§8ÿ2@ØŽ5PÙšŸ>X‰—Ç1€€š¦ê¯ã¹¸–° ÙšŸ>(ä«ÅÙ¿ 0ï§8ÿ2@ØŽ5PÙšŸ>X‰—Ç -™ ” -,€€š¦ê¯ãš©ïë „…™(± †­0ˆ 8}@Ì‚P„…™XÝé1€€š¦ê¯ãåÆõîã ™ÄÐ.(æîóÜŽ0¨ý8™&@“å'P™ÄÐ.X§›•1€€š¦ê¯ãåÆõîã ™ÄÐ.(æîóÜŽ0¨ý8™&@“å'P™ÄÐ.X§›• - } -%€€š¦ê¯ãÐÊ¿ ®Ä(·›û08@¨P®ÄXô)€€š¦ê¯ã¿Þ¯¤ ”ê;(Ô­Å 081@‡3P”ê;Xº¿)€€š¦ê¯ã¿Þ¯¤ ”ê;(Ô­Å 081@‡3P”ê;Xº¿ - Š -(€€š¦ê¯ãÅíÛ… ½®(áç·–0«8@­P½®XÈW.€€š¦ê¯ãò¯þÞ( Ü”«(×΀ë¢0ˆ[8ê@ÓŽPÜ”«XõÖ.€€š¦ê¯ãò¯þÞ( Ü”«(×΀ë¢0ˆ[8ê@ÓŽPÜ”«XõÖ -™ -” -,€€š¦ê¯ãš©ïë „…™(± †­0ˆ 8}@Ì‚P„…™XÝé1€€š¦ê¯ãåÆõîã ™ÄÐ.(æîóÜŽ0¨ý8™&@“å'P™ÄÐ.X§›•1€€š¦ê¯ãåÆõîã ™ÄÐ.(æîóÜŽ0¨ý8™&@“å'P™ÄÐ.X§›• -a] -€€š¦ê¯ãÄ£ p(®Œ@PpX€€š¦ê¯ãèé¦ ’"(ïáš0@P’"Xm€€š¦ê¯ãèé¦ ’"(ïáš0@P’"Xm -a] -€€š¦ê¯ãŠ² s(ÀÆ@PsX€€š¦ê¯ã—¡« †#(¾½¬0@P†#Xp€€š¦ê¯ã—¡« †#(¾½¬0@P†#Xp -ž#™ --€€š¦ê¯ã´þß° ²ˆ­(­ö—¼A0Ï$8ß@‘îP²ˆ­XÝ -3€€š¦ê¯ãþÁŸíþ ªÇÝ‚(ʇ¥«ù0÷“ 8Œk@§ÇoPªÇÝ‚X±«¢3€€š¦ê¯ãþÁŸíþ ªÇÝ‚(ʇ¥«ù0÷“ 8Œk@§ÇoPªÇÝ‚X±«¢ -+$' - €€š¦ê¯ã €€š¦ê¯ã €€š¦ê¯ã -a] -€€š¦ê¯ãõ¦ W(£š @PWX€€š¦ê¯ãÉí€ ¯(×€ƒ0@P¯XT€€š¦ê¯ãÉí€ ¯(×€ƒ0@P¯XT -OK -d€ÐÛÃô (08PXd€ÐÛÃô (08PXd€ÐÛÃô (08PXŽ¦®ìÖ -’ -)€€š¦ê¯ã¼öþ» °ýú(Ž¼ùé90ž8@« Pøò%X£y/€€š¦ê¯ãÀ‹Ö£´ Ž›¼s(ø¦¤Ï0~8» @Ÿí PÛ‚Æ XÕù$/€€š¦ê¯ãÀ‹Ö£´ Ž›¼s(ø¦¤Ï0~8» @Ÿí PÛ‚Æ XÕù$ -!} -%€€š¦ê¯ã“Öå Ï(À¯‘308@žPÏX°)€€š¦ê¯ãæŸýò æf(®¼¥Ê0Û8T@‘WPæfXßÆ)€€š¦ê¯ãæŸýò æf(®¼¥Ê0Û8T@‘WPæfXßÆ -|x -"€€š¦ê¯ã§€Ž Òk(»¦¶0 8@\PÒkXØ(€€š¦ê¯ãö‹¢  ðå (€ÜÅ€0æ8@þPðå Xùh(€€š¦ê¯ãö‹¢  ðå (€ÜÅ€0æ8@þPðå Xùh -xt - €€š¦ê¯ã‹„ò Ä1(Ø«Ç0@*PÄ1Xž'€€š¦ê¯ã´ŸßI Ë‹(õ§Þ¦0¥8 @ð PË‹X¥0'€€š¦ê¯ã´ŸßI Ë‹(õ§Þ¦0¥8 @ð PË‹X¥0 -Œ‡ -'€€š¦ê¯ãÎóö^ ¾¶(ú´û0Ô8@ÊP¾¶X•>-€€š¦ê¯ãåœö îÌõ(•ñÌs0Ó@8í@¾†PîÌõX©ø-€€š¦ê¯ãåœö îÌõ(•ñÌs0Ó@8í@¾†PîÌõX©ø -š• --€€š¦ê¯ãÏ÷“ó ƒŽé(–üõÈ#0ò8¿@öÆPƒŽéXúé1€€š¦ê¯ã•¹‰‹Û ˆ£ƒG(Ëêø›ë -0å‡8˜:@òÍ(ä«ÅÙ¿ 0ï§8ÿ2@ØŽ5PÙšŸ>X‰—Ç1€€š¦ê¯ã¹¸–° ÙšŸ>(ä«ÅÙ¿ 0ï§8ÿ2@ØŽ5PÙšŸ>X‰—Ç -™ ” -,€€š¦ê¯ãš©ïë „…™(± †­0ˆ 8}@Ì‚P„…™XÝé1€€š¦ê¯ãåÆõîã ™ÄÐ.(æîóÜŽ0¨ý8™&@“å'P™ÄÐ.X§›•1€€š¦ê¯ãåÆõîã ™ÄÐ.(æîóÜŽ0¨ý8™&@“å'P™ÄÐ.X§›• - } -%€€š¦ê¯ãÐÊ¿ ®Ä(·›û08@¨P®ÄXô)€€š¦ê¯ã¿Þ¯¤ ”ê;(Ô­Å 081@‡3P”ê;Xº¿)€€š¦ê¯ã¿Þ¯¤ ”ê;(Ô­Å 081@‡3P”ê;Xº¿ - Š -(€€š¦ê¯ãÅíÛ… ½®(áç·–0«8@­P½®XÈW.€€š¦ê¯ãò¯þÞ( Ü”«(×΀ë¢0ˆ[8ê@ÓŽPÜ”«XõÖ.€€š¦ê¯ãò¯þÞ( Ü”«(×΀ë¢0ˆ[8ê@ÓŽPÜ”«XõÖ -™ -” -,€€š¦ê¯ãš©ïë „…™(± †­0ˆ 8}@Ì‚P„…™XÝé1€€š¦ê¯ãåÆõîã ™ÄÐ.(æîóÜŽ0¨ý8™&@“å'P™ÄÐ.X§›•1€€š¦ê¯ãåÆõîã ™ÄÐ.(æîóÜŽ0¨ý8™&@“å'P™ÄÐ.X§›• -a] -€€š¦ê¯ãÄ£ p(®Œ@PpX€€š¦ê¯ãèé¦ ’"(ïáš0@P’"Xm€€š¦ê¯ãèé¦ ’"(ïáš0@P’"Xm -a] -€€š¦ê¯ãŠ² s(ÀÆ@PsX€€š¦ê¯ã—¡« †#(¾½¬0@P†#Xp€€š¦ê¯ã—¡« †#(¾½¬0@P†#Xp -ž#™ --€€š¦ê¯ã´þß° ²ˆ­(­ö—¼A0Ï$8ß@‘îP²ˆ­XÝ -3€€š¦ê¯ãþÁŸíþ ªÇÝ‚(ʇ¥«ù0÷“ 8Œk@§ÇoPªÇÝ‚X±«¢3€€š¦ê¯ãþÁŸíþ ªÇÝ‚(ʇ¥«ù0÷“ 8Œk@§ÇoPªÇÝ‚X±«¢ -+$' - €€š¦ê¯ã €€š¦ê¯ã €€š¦ê¯ã -a] -€€š¦ê¯ãõ¦ W(£š @PWX€€š¦ê¯ãÉí€ ¯(×€ƒ0@P¯XT€€š¦ê¯ãÉí€ ¯(×€ƒ0@P¯XT -OK -d€ÐÛÃô (08PXd€ÐÛÃô (08PXd€ÐÛÃô (08PXŽ—Žð \ No newline at end of file diff --git a/hedera-node/test-clients/src/main/resource/feeSchedule_ONLY_CURRENT.txt b/hedera-node/test-clients/src/main/resource/feeSchedule_ONLY_CURRENT.txt deleted file mode 100644 index 4c454c61a936..000000000000 --- a/hedera-node/test-clients/src/main/resource/feeSchedule_ONLY_CURRENT.txt +++ /dev/null @@ -1,68 +0,0 @@ - -‚ -Š -(€€š¦ê¯ã†¿™¹ øò%(›ù˜ä0ž8@« Pøò%X£y.€€š¦ê¯ãàšâ¶8 Û‚Æ (Œ÷ÌÃá0~8» @Ÿí PÛ‚Æ XÕù$.€€š¦ê¯ãàšâ¶8 Û‚Æ (Œ÷ÌÃá0~8» @Ÿí PÛ‚Æ XÕù$ -!} -%€€š¦ê¯ã“Öå Ï(À¯‘308@žPÏX°)€€š¦ê¯ãæŸýò æf(®¼¥Ê0Û8T@‘WPæfXßÆ)€€š¦ê¯ãæŸýò æf(®¼¥Ê0Û8T@‘WPæfXßÆ -|x -"€€š¦ê¯ã§€Ž Òk(»¦¶0 8@\PÒkXØ(€€š¦ê¯ãö‹¢  ðå (€ÜÅ€0æ8@þPðå Xùh(€€š¦ê¯ãö‹¢  ðå (€ÜÅ€0æ8@þPðå Xùh -xt - €€š¦ê¯ã‹„ò Ä1(Ø«Ç0@*PÄ1Xž'€€š¦ê¯ã´ŸßI Ë‹(õ§Þ¦0¥8 @ð PË‹X¥0'€€š¦ê¯ã´ŸßI Ë‹(õ§Þ¦0¥8 @ð PË‹X¥0 -Œ‡ -'€€š¦ê¯ãÎóö^ ¾¶(ú´û0Ô8@ÊP¾¶X•>-€€š¦ê¯ãåœö îÌõ(•ñÌs0Ó@8í@¾†PîÌõX©ø-€€š¦ê¯ãåœö îÌõ(•ñÌs0Ó@8í@¾†PîÌõX©ø -š• --€€š¦ê¯ãÏ÷“ó ƒŽé(–üõÈ#0ò8¿@öÆPƒŽéXúé1€€š¦ê¯ã•¹‰‹Û ˆ£ƒG(Ëêø›ë -0å‡8˜:@òÍ(ä«ÅÙ¿ 0ï§8ÿ2@ØŽ5PÙšŸ>X‰—Ç1€€š¦ê¯ã¹¸–° ÙšŸ>(ä«ÅÙ¿ 0ï§8ÿ2@ØŽ5PÙšŸ>X‰—Ç -™ ” -,€€š¦ê¯ãš©ïë „…™(± †­0ˆ 8}@Ì‚P„…™XÝé1€€š¦ê¯ãåÆõîã ™ÄÐ.(æîóÜŽ0¨ý8™&@“å'P™ÄÐ.X§›•1€€š¦ê¯ãåÆõîã ™ÄÐ.(æîóÜŽ0¨ý8™&@“å'P™ÄÐ.X§›• - } -%€€š¦ê¯ãÐÊ¿ ®Ä(·›û08@¨P®ÄXô)€€š¦ê¯ã¿Þ¯¤ ”ê;(Ô­Å 081@‡3P”ê;Xº¿)€€š¦ê¯ã¿Þ¯¤ ”ê;(Ô­Å 081@‡3P”ê;Xº¿ - Š -(€€š¦ê¯ãÅíÛ… ½®(áç·–0«8@­P½®XÈW.€€š¦ê¯ãò¯þÞ( Ü”«(×΀ë¢0ˆ[8ê@ÓŽPÜ”«XõÖ.€€š¦ê¯ãò¯þÞ( Ü”«(×΀ë¢0ˆ[8ê@ÓŽPÜ”«XõÖ -™ -” -,€€š¦ê¯ãš©ïë „…™(± †­0ˆ 8}@Ì‚P„…™XÝé1€€š¦ê¯ãåÆõîã ™ÄÐ.(æîóÜŽ0¨ý8™&@“å'P™ÄÐ.X§›•1€€š¦ê¯ãåÆõîã ™ÄÐ.(æîóÜŽ0¨ý8™&@“å'P™ÄÐ.X§›• -a] -€€š¦ê¯ãÄ£ p(®Œ@PpX€€š¦ê¯ãèé¦ ’"(ïáš0@P’"Xm€€š¦ê¯ãèé¦ ’"(ïáš0@P’"Xm -a] -€€š¦ê¯ãŠ² s(ÀÆ@PsX€€š¦ê¯ã—¡« †#(¾½¬0@P†#Xp€€š¦ê¯ã—¡« †#(¾½¬0@P†#Xp -ž#™ --€€š¦ê¯ã´þß° ²ˆ­(­ö—¼A0Ï$8ß@‘îP²ˆ­XÝ -3€€š¦ê¯ãþÁŸíþ ªÇÝ‚(ʇ¥«ù0÷“ 8Œk@§ÇoPªÇÝ‚X±«¢3€€š¦ê¯ãþÁŸíþ ªÇÝ‚(ʇ¥«ù0÷“ 8Œk@§ÇoPªÇÝ‚X±«¢ -+$' - €€š¦ê¯ã €€š¦ê¯ã €€š¦ê¯ã -a] -€€š¦ê¯ãõ¦ W(£š @PWX€€š¦ê¯ãÉí€ ¯(×€ƒ0@P¯XT€€š¦ê¯ãÉí€ ¯(×€ƒ0@P¯XTßí£è \ No newline at end of file diff --git a/hedera-node/test-clients/src/main/resource/migration_config_mainnet.properties b/hedera-node/test-clients/src/main/resource/migration_config_mainnet.properties deleted file mode 100644 index 6a2358771eac..000000000000 --- a/hedera-node/test-clients/src/main/resource/migration_config_mainnet.properties +++ /dev/null @@ -1,6 +0,0 @@ -default.payer=0.0.950 -migration.file.id=0.0.29223 -migration.crypto.AccountA.id=0.0.29224 -migration.crypto.AccountB.id=0.0.29225 -migration.smartContract.id=0.0.29227 -nodes=54.162.70.169:0.0.3 \ No newline at end of file diff --git a/hedera-node/test-clients/src/main/resource/migration_config_testnet.properties b/hedera-node/test-clients/src/main/resource/migration_config_testnet.properties deleted file mode 100644 index 25ad974cf8d6..000000000000 --- a/hedera-node/test-clients/src/main/resource/migration_config_testnet.properties +++ /dev/null @@ -1,6 +0,0 @@ -default.payer=0.0.50 -migration.file.id=0.0.124097 -migration.crypto.AccountA.id=0.0.124098 -migration.crypto.AccountB.id=0.0.124099 -migration.smartContract.id=0.0.124101 -nodes=3.13.199.218:0.0.3 \ No newline at end of file diff --git a/hedera-node/test-clients/src/main/resource/regressionTestFiles/1K.bin b/hedera-node/test-clients/src/main/resource/regressionTestFiles/1K.bin deleted file mode 100644 index a988505fa3ec..000000000000 --- a/hedera-node/test-clients/src/main/resource/regressionTestFiles/1K.bin +++ /dev/null @@ -1,21 +0,0 @@ -####################################################################################### -# Configuration file, for automatically running multiple instances -####################################################################################### -swirld, 123 - -#app, HashgraphCoinApp.jar -app, HGCCApp.jar - -address, A, alice, 1, 127.0.0.1, 50204, 127.0.0.1, 50204 -address, B, bob, 1, 127.0.0.1, 50205, 127.0.0.1, 50205 -address, C, carol, 1, 127.0.0.1, 50206, 127.0.0.1, 50206 -#address, D, Dave, 1, 127.0.0.1, 50207, 10.2.3.173, 50207 - -# address, E, Eric, 1, 127.0.0.1, 50204, 127.0.0.1, 50204 -# address, F, Fred, 1, 127.0.0.1, 50205, 127.0.0.1, 50205 -# address, G, Gina, 1, 127.0.0.1, 50206, 127.0.0.1, 50206 -# address, H, Hank, 1, 127.0.0.1, 50207, 127.0.0.1, 50207 -# address, I, Iris, 1, 127.0.0.1, 50208, 127.0.0.1, 50208 -# address, J, Judy, 1, 127.0.0.1, 50209, 127.0.0.1, 50209 -# address, K, Kent, 1, 127.0.0.1, 50210, 127.0.0.1, 50210 -# a \ No newline at end of file diff --git a/hedera-node/test-clients/src/main/resource/regressionTestFiles/1K.jpg b/hedera-node/test-clients/src/main/resource/regressionTestFiles/1K.jpg deleted file mode 100644 index 6968d8147175..000000000000 --- a/hedera-node/test-clients/src/main/resource/regressionTestFiles/1K.jpg +++ /dev/null @@ -1,21 +0,0 @@ -####################################################################################### -# Swirlds configuration file, for automatically running multiple instances -####################################################################################### -swirld, 123 - -#app, HashgraphCoinApp.jar -app, HGCCApp.jar - -address, A, alice, 1, 127.0.0.1, 50204, 127.0.0.1, 50204 -address, B, bob, 1, 127.0.0.1, 50205, 127.0.0.1, 50205 -address, C, carol, 1, 127.0.0.1, 50206, 127.0.0.1, 50206 -#address, D, Dave, 1, 127.0.0.1, 50207, 10.2.3.173, 50207 - -# address, E, Eric, 1, 127.0.0.1, 50204, 127.0.0.1, 50204 -# address, F, Fred, 1, 127.0.0.1, 50205, 127.0.0.1, 50205 -# address, G, Gina, 1, 127.0.0.1, 50206, 127.0.0.1, 50206 -# address, H, Hank, 1, 127.0.0.1, 50207, 127.0.0.1, 50207 -# address, I, Iris, 1, 127.0.0.1, 50208, 127.0.0.1, 50208 -# address, J, Judy, 1, 127.0.0.1, 50209, 127.0.0.1, 50209 -# address, K, Kent, 1, 127.0.0.1, 50210, 127.0.0.1, 50210 -# a \ No newline at end of file diff --git a/hedera-node/test-clients/src/main/resource/regressionTestFiles/1K.pdf b/hedera-node/test-clients/src/main/resource/regressionTestFiles/1K.pdf deleted file mode 100644 index 6968d8147175..000000000000 Binary files a/hedera-node/test-clients/src/main/resource/regressionTestFiles/1K.pdf and /dev/null differ diff --git a/hedera-node/test-clients/src/main/resource/regressionTestFiles/1K.txt b/hedera-node/test-clients/src/main/resource/regressionTestFiles/1K.txt deleted file mode 100644 index a988505fa3ec..000000000000 --- a/hedera-node/test-clients/src/main/resource/regressionTestFiles/1K.txt +++ /dev/null @@ -1,21 +0,0 @@ -####################################################################################### -# Configuration file, for automatically running multiple instances -####################################################################################### -swirld, 123 - -#app, HashgraphCoinApp.jar -app, HGCCApp.jar - -address, A, alice, 1, 127.0.0.1, 50204, 127.0.0.1, 50204 -address, B, bob, 1, 127.0.0.1, 50205, 127.0.0.1, 50205 -address, C, carol, 1, 127.0.0.1, 50206, 127.0.0.1, 50206 -#address, D, Dave, 1, 127.0.0.1, 50207, 10.2.3.173, 50207 - -# address, E, Eric, 1, 127.0.0.1, 50204, 127.0.0.1, 50204 -# address, F, Fred, 1, 127.0.0.1, 50205, 127.0.0.1, 50205 -# address, G, Gina, 1, 127.0.0.1, 50206, 127.0.0.1, 50206 -# address, H, Hank, 1, 127.0.0.1, 50207, 127.0.0.1, 50207 -# address, I, Iris, 1, 127.0.0.1, 50208, 127.0.0.1, 50208 -# address, J, Judy, 1, 127.0.0.1, 50209, 127.0.0.1, 50209 -# address, K, Kent, 1, 127.0.0.1, 50210, 127.0.0.1, 50210 -# a \ No newline at end of file diff --git a/hedera-node/test-clients/src/main/resource/testfiles/1K.txt b/hedera-node/test-clients/src/main/resource/testfiles/1K.txt deleted file mode 100644 index a988505fa3ec..000000000000 --- a/hedera-node/test-clients/src/main/resource/testfiles/1K.txt +++ /dev/null @@ -1,21 +0,0 @@ -####################################################################################### -# Configuration file, for automatically running multiple instances -####################################################################################### -swirld, 123 - -#app, HashgraphCoinApp.jar -app, HGCCApp.jar - -address, A, alice, 1, 127.0.0.1, 50204, 127.0.0.1, 50204 -address, B, bob, 1, 127.0.0.1, 50205, 127.0.0.1, 50205 -address, C, carol, 1, 127.0.0.1, 50206, 127.0.0.1, 50206 -#address, D, Dave, 1, 127.0.0.1, 50207, 10.2.3.173, 50207 - -# address, E, Eric, 1, 127.0.0.1, 50204, 127.0.0.1, 50204 -# address, F, Fred, 1, 127.0.0.1, 50205, 127.0.0.1, 50205 -# address, G, Gina, 1, 127.0.0.1, 50206, 127.0.0.1, 50206 -# address, H, Hank, 1, 127.0.0.1, 50207, 127.0.0.1, 50207 -# address, I, Iris, 1, 127.0.0.1, 50208, 127.0.0.1, 50208 -# address, J, Judy, 1, 127.0.0.1, 50209, 127.0.0.1, 50209 -# address, K, Kent, 1, 127.0.0.1, 50210, 127.0.0.1, 50210 -# a \ No newline at end of file diff --git a/hedera-node/test-clients/src/main/resource/testfiles/hashgraph-hellofuture.jpg b/hedera-node/test-clients/src/main/resource/testfiles/hashgraph-hellofuture.jpg deleted file mode 100644 index 8a7e2ab89b40..000000000000 Binary files a/hedera-node/test-clients/src/main/resource/testfiles/hashgraph-hellofuture.jpg and /dev/null differ diff --git a/hedera-node/test-clients/src/main/resource/testfiles/hg2.pdf b/hedera-node/test-clients/src/main/resource/testfiles/hg2.pdf deleted file mode 100644 index 965ca9e68c1a..000000000000 Binary files a/hedera-node/test-clients/src/main/resource/testfiles/hg2.pdf and /dev/null differ diff --git a/hedera-node/test-clients/src/main/resource/testfiles/overview-frame.html b/hedera-node/test-clients/src/main/resource/testfiles/overview-frame.html deleted file mode 100644 index dde0ad85db68..000000000000 --- a/hedera-node/test-clients/src/main/resource/testfiles/overview-frame.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - -Overview List (Platform API) - - - - - - - - - - - - -
- -
- -

 

- - diff --git a/hedera-node/test-clients/src/main/resource/testfiles/spring-core-4.2.0.RELEASE-1mb-jar b/hedera-node/test-clients/src/main/resource/testfiles/spring-core-4.2.0.RELEASE-1mb-jar deleted file mode 100644 index 728930ab9354..000000000000 Binary files a/hedera-node/test-clients/src/main/resource/testfiles/spring-core-4.2.0.RELEASE-1mb-jar and /dev/null differ diff --git a/hedera-node/test-clients/src/yahcli/java/module-info.java b/hedera-node/test-clients/src/yahcli/java/module-info.java index 71653f02665a..5e1423509a46 100644 --- a/hedera-node/test-clients/src/yahcli/java/module-info.java +++ b/hedera-node/test-clients/src/yahcli/java/module-info.java @@ -2,9 +2,9 @@ requires com.hedera.node.app.hapi.utils; requires com.hedera.node.hapi; requires com.hedera.node.test.clients; + requires com.swirlds.common; requires com.github.spotbugs.annotations; requires com.google.common; - requires com.swirlds.common; requires info.picocli; requires net.i2p.crypto.eddsa; requires org.apache.logging.log4j; diff --git a/platform-sdk/platform-apps/demos/CryptocurrencyDemo/src/main/java/module-info.java b/platform-sdk/platform-apps/demos/CryptocurrencyDemo/src/main/java/module-info.java index 90da3aadf4fc..b42efa99b26b 100644 --- a/platform-sdk/platform-apps/demos/CryptocurrencyDemo/src/main/java/module-info.java +++ b/platform-sdk/platform-apps/demos/CryptocurrencyDemo/src/main/java/module-info.java @@ -1,8 +1,8 @@ module com.swirlds.demo.crypto { - requires com.hedera.pbj.runtime; requires com.swirlds.common; requires com.swirlds.metrics.api; requires com.swirlds.platform.core; + requires com.hedera.pbj.runtime; requires java.desktop; requires static com.github.spotbugs.annotations; } diff --git a/platform-sdk/platform-apps/demos/HelloSwirldDemo/src/main/java/module-info.java b/platform-sdk/platform-apps/demos/HelloSwirldDemo/src/main/java/module-info.java index 7da0b6b2e2e5..5875d87f3eb0 100644 --- a/platform-sdk/platform-apps/demos/HelloSwirldDemo/src/main/java/module-info.java +++ b/platform-sdk/platform-apps/demos/HelloSwirldDemo/src/main/java/module-info.java @@ -1,6 +1,6 @@ module com.swirlds.demo.hello { - requires com.hedera.pbj.runtime; requires com.swirlds.common; requires com.swirlds.platform.core; + requires com.hedera.pbj.runtime; requires java.desktop; } diff --git a/platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/module-info.java b/platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/module-info.java index 3c1f30a01149..9cf7d36b3ea9 100644 --- a/platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/module-info.java +++ b/platform-sdk/platform-apps/tests/AddressBookTestingTool/src/main/java/module-info.java @@ -1,10 +1,10 @@ module com.swirlds.demo.addressbook { - requires com.hedera.pbj.runtime; requires com.swirlds.base; requires com.swirlds.common; requires com.swirlds.config.api; requires com.swirlds.logging; requires com.swirlds.platform.core; + requires com.hedera.pbj.runtime; requires org.apache.logging.log4j; requires static com.github.spotbugs.annotations; } diff --git a/platform-sdk/platform-apps/tests/ConsistencyTestingTool/src/main/java/module-info.java b/platform-sdk/platform-apps/tests/ConsistencyTestingTool/src/main/java/module-info.java index 14b6be7c1f85..be312e2cff34 100644 --- a/platform-sdk/platform-apps/tests/ConsistencyTestingTool/src/main/java/module-info.java +++ b/platform-sdk/platform-apps/tests/ConsistencyTestingTool/src/main/java/module-info.java @@ -1,10 +1,10 @@ module com.swirlds.demo.consistency { - requires com.hedera.pbj.runtime; requires com.swirlds.base; requires com.swirlds.common; requires com.swirlds.config.api; requires com.swirlds.logging; requires com.swirlds.platform.core; + requires com.hedera.pbj.runtime; requires org.apache.logging.log4j; requires static com.github.spotbugs.annotations; } diff --git a/platform-sdk/platform-apps/tests/ISSTestingTool/src/main/java/module-info.java b/platform-sdk/platform-apps/tests/ISSTestingTool/src/main/java/module-info.java index 5a12815a92c4..6b1ce2c53dbf 100644 --- a/platform-sdk/platform-apps/tests/ISSTestingTool/src/main/java/module-info.java +++ b/platform-sdk/platform-apps/tests/ISSTestingTool/src/main/java/module-info.java @@ -1,10 +1,10 @@ module com.swirlds.demo.iss { - requires com.hedera.pbj.runtime; requires com.swirlds.base; requires com.swirlds.common; requires com.swirlds.config.api; requires com.swirlds.logging; requires com.swirlds.platform.core; + requires com.hedera.pbj.runtime; requires org.apache.logging.log4j; requires static com.github.spotbugs.annotations; } diff --git a/platform-sdk/platform-apps/tests/MigrationTestingTool/src/main/java/module-info.java b/platform-sdk/platform-apps/tests/MigrationTestingTool/src/main/java/module-info.java index 234179e59c47..e7e8a7f850d1 100644 --- a/platform-sdk/platform-apps/tests/MigrationTestingTool/src/main/java/module-info.java +++ b/platform-sdk/platform-apps/tests/MigrationTestingTool/src/main/java/module-info.java @@ -1,5 +1,4 @@ module com.swirlds.demo.migration { - requires com.hedera.pbj.runtime; requires com.swirlds.base; requires com.swirlds.common; requires com.swirlds.fcqueue; @@ -9,6 +8,7 @@ requires com.swirlds.metrics.api; requires com.swirlds.platform.core; requires com.swirlds.virtualmap; + requires com.hedera.pbj.runtime; requires java.logging; requires org.apache.logging.log4j; requires static com.github.spotbugs.annotations; diff --git a/platform-sdk/platform-apps/tests/PlatformTestingTool/src/main/java/module-info.java b/platform-sdk/platform-apps/tests/PlatformTestingTool/src/main/java/module-info.java index a24e1d4751fb..cfa03039efdb 100644 --- a/platform-sdk/platform-apps/tests/PlatformTestingTool/src/main/java/module-info.java +++ b/platform-sdk/platform-apps/tests/PlatformTestingTool/src/main/java/module-info.java @@ -23,11 +23,6 @@ exports com.swirlds.demo.virtualmerkle.config to com.fasterxml.jackson.databind; - requires com.fasterxml.jackson.annotation; - requires com.fasterxml.jackson.core; - requires com.fasterxml.jackson.databind; - requires com.google.protobuf; - requires com.hedera.pbj.runtime; requires com.swirlds.base; requires com.swirlds.common.test.fixtures; requires com.swirlds.common; @@ -40,6 +35,11 @@ requires com.swirlds.metrics.api; requires com.swirlds.platform.core; requires com.swirlds.virtualmap; + requires com.fasterxml.jackson.annotation; + requires com.fasterxml.jackson.core; + requires com.fasterxml.jackson.databind; + requires com.google.protobuf; + requires com.hedera.pbj.runtime; requires java.logging; requires java.management; requires org.apache.logging.log4j; diff --git a/platform-sdk/platform-apps/tests/StatsSigningTestingTool/src/main/java/module-info.java b/platform-sdk/platform-apps/tests/StatsSigningTestingTool/src/main/java/module-info.java index e1253c34baeb..509f9f29ca18 100644 --- a/platform-sdk/platform-apps/tests/StatsSigningTestingTool/src/main/java/module-info.java +++ b/platform-sdk/platform-apps/tests/StatsSigningTestingTool/src/main/java/module-info.java @@ -1,10 +1,10 @@ module com.swirlds.demo.stats.signing { - requires com.hedera.pbj.runtime; requires com.swirlds.base; requires com.swirlds.common; requires com.swirlds.logging; requires com.swirlds.metrics.api; requires com.swirlds.platform.core; + requires com.hedera.pbj.runtime; requires lazysodium.java; requires org.apache.logging.log4j; requires org.bouncycastle.provider; diff --git a/platform-sdk/platform-apps/tests/StressTestingTool/src/main/java/module-info.java b/platform-sdk/platform-apps/tests/StressTestingTool/src/main/java/module-info.java index 04849840693e..056479da84b5 100644 --- a/platform-sdk/platform-apps/tests/StressTestingTool/src/main/java/module-info.java +++ b/platform-sdk/platform-apps/tests/StressTestingTool/src/main/java/module-info.java @@ -1,11 +1,11 @@ module com.swirlds.demo.stress { - requires com.hedera.pbj.runtime; requires com.swirlds.base; requires com.swirlds.common; requires com.swirlds.config.api; requires com.swirlds.logging; requires com.swirlds.metrics.api; requires com.swirlds.platform.core; + requires com.hedera.pbj.runtime; requires org.apache.logging.log4j; requires static com.github.spotbugs.annotations; } diff --git a/platform-sdk/swirlds-base/src/testFixtures/java/module-info.java b/platform-sdk/swirlds-base/src/testFixtures/java/module-info.java index 595ca5960bb9..c02583258b3d 100644 --- a/platform-sdk/swirlds-base/src/testFixtures/java/module-info.java +++ b/platform-sdk/swirlds-base/src/testFixtures/java/module-info.java @@ -7,7 +7,7 @@ requires transitive com.swirlds.base; requires transitive org.junit.jupiter.api; - requires static transitive com.github.spotbugs.annotations; requires jakarta.inject; requires org.assertj.core; + requires static transitive com.github.spotbugs.annotations; } diff --git a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/metrics/platform/prometheus/AbstractMetricAdapter.java b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/metrics/platform/prometheus/AbstractMetricAdapter.java index 8286fd770a92..4066fb5ff30a 100644 --- a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/metrics/platform/prometheus/AbstractMetricAdapter.java +++ b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/metrics/platform/prometheus/AbstractMetricAdapter.java @@ -124,10 +124,10 @@ private AdaptedMetricCommonValues(final @NonNull Metric metric, final boolean su } /** - * identifies changes in the metrics name components (category, name, and unit). If a change is detected, error + * Identifies changes in the metrics name components (category, name, and unit). If a change is detected, error * log statements with the purpose of failing JRS are generated to inform developers that adjustments to the * metric name may be required. - *

+ *

* It is not throwing exceptions in order to minimize the possibility for runtime errors produced by * non-critical misconfigurations * diff --git a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/metrics/statistics/StatsSpeedometer.java b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/metrics/statistics/StatsSpeedometer.java index e22ccb2d024b..84fb695c280f 100644 --- a/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/metrics/statistics/StatsSpeedometer.java +++ b/platform-sdk/swirlds-common/src/main/java/com/swirlds/common/metrics/statistics/StatsSpeedometer.java @@ -162,7 +162,7 @@ public void reset(final double halfLife) { /** * Start over on the measurements and counts, to get an exponentially-weighted average number of calls * to cycle() per second, with the weighting having a half life of halfLife seconds. This is equivalent - * to instantiating a new Speedometer. If halfLife < 0.01 then 0.01 will be used. + * to instantiating a new Speedometer. If halfLife < 0.01 then 0.01 will be used. * * @param halfLife * half of the exponential weighting comes from the last halfLife seconds diff --git a/platform-sdk/swirlds-common/src/testFixtures/java/module-info.java b/platform-sdk/swirlds-common/src/testFixtures/java/module-info.java index a7da88694168..de02a9012c75 100644 --- a/platform-sdk/swirlds-common/src/testFixtures/java/module-info.java +++ b/platform-sdk/swirlds-common/src/testFixtures/java/module-info.java @@ -13,12 +13,12 @@ exports com.swirlds.common.test.fixtures.fcqueue; exports com.swirlds.common.test.fixtures.platform; - requires transitive com.hedera.pbj.runtime; requires transitive com.swirlds.base; requires transitive com.swirlds.common; requires transitive com.swirlds.config.api; requires transitive com.swirlds.metrics.api; requires transitive com.swirlds.platform.core; + requires transitive com.hedera.pbj.runtime; requires com.swirlds.logging; requires lazysodium.java; requires org.apache.logging.log4j.core; diff --git a/platform-sdk/swirlds-config-processor/src/main/java/module-info.java b/platform-sdk/swirlds-config-processor/src/main/java/module-info.java index 8cb34ed97caa..8dce1a40f3d9 100644 --- a/platform-sdk/swirlds-config-processor/src/main/java/module-info.java +++ b/platform-sdk/swirlds-config-processor/src/main/java/module-info.java @@ -2,10 +2,10 @@ provides javax.annotation.processing.Processor with com.swirlds.config.processor.ConfigDataAnnotationProcessor; + requires transitive org.antlr.antlr4.runtime; requires com.swirlds.config.api; requires com.squareup.javapoet; requires java.compiler; - requires transitive org.antlr.antlr4.runtime; requires static transitive com.github.spotbugs.annotations; requires static transitive com.google.auto.service; } diff --git a/platform-sdk/swirlds-fcqueue/src/main/java/com/swirlds/fcqueue/FCQueue.java b/platform-sdk/swirlds-fcqueue/src/main/java/com/swirlds/fcqueue/FCQueue.java index 91ce7c63c9ec..af2ed5d37d87 100644 --- a/platform-sdk/swirlds-fcqueue/src/main/java/com/swirlds/fcqueue/FCQueue.java +++ b/platform-sdk/swirlds-fcqueue/src/main/java/com/swirlds/fcqueue/FCQueue.java @@ -67,7 +67,7 @@ private static class ClassVersion { */ public static final int ORIGINAL = 1; /** - * FCQ implements MerkleLeaf, element implements FastCopyable & SerializableHashable + * FCQ implements MerkleLeaf, element implements FastCopyable and SerializableHashable */ public static final int MIGRATE_TO_SERIALIZABLE = 2; diff --git a/platform-sdk/swirlds-jasperdb/src/main/java/com/swirlds/merkledb/collections/AbstractLongList.java b/platform-sdk/swirlds-jasperdb/src/main/java/com/swirlds/merkledb/collections/AbstractLongList.java index 52e6ef452a19..40355fea8808 100644 --- a/platform-sdk/swirlds-jasperdb/src/main/java/com/swirlds/merkledb/collections/AbstractLongList.java +++ b/platform-sdk/swirlds-jasperdb/src/main/java/com/swirlds/merkledb/collections/AbstractLongList.java @@ -638,7 +638,7 @@ public void forEach(final LongAction action) throws Int } /** - * This method returns a snapshot of the current {@link data}. FOR TEST PURPOSES ONLY. NOT + * This method returns a snapshot of the current data. FOR TEST PURPOSES ONLY. NOT * THREAD SAFE * * @return a copy of data. diff --git a/platform-sdk/swirlds-logging-log4j-appender/src/main/java/module-info.java b/platform-sdk/swirlds-logging-log4j-appender/src/main/java/module-info.java index 2975ac56a06c..2afd3fd5e140 100644 --- a/platform-sdk/swirlds-logging-log4j-appender/src/main/java/module-info.java +++ b/platform-sdk/swirlds-logging-log4j-appender/src/main/java/module-info.java @@ -4,11 +4,11 @@ import org.apache.logging.log4j.spi.Provider; module com.swirlds.logging.log4j.appender { - requires static transitive com.github.spotbugs.annotations; - requires static transitive com.google.auto.service; requires transitive com.swirlds.config.api; requires transitive com.swirlds.logging; requires transitive org.apache.logging.log4j; + requires static transitive com.github.spotbugs.annotations; + requires static transitive com.google.auto.service; provides Provider with BaseLoggingProvider; diff --git a/platform-sdk/swirlds-logging/src/testFixtures/java/module-info.java b/platform-sdk/swirlds-logging/src/testFixtures/java/module-info.java index f14b1892f585..951ffb372aed 100644 --- a/platform-sdk/swirlds-logging/src/testFixtures/java/module-info.java +++ b/platform-sdk/swirlds-logging/src/testFixtures/java/module-info.java @@ -1,9 +1,9 @@ open module com.swirlds.logging.test.fixtures { exports com.swirlds.logging.test.fixtures; - requires com.swirlds.base.test.fixtures; requires transitive com.swirlds.config.api; requires transitive com.swirlds.logging; requires transitive org.junit.jupiter.api; + requires com.swirlds.base.test.fixtures; requires static transitive com.github.spotbugs.annotations; } diff --git a/platform-sdk/swirlds-merkle/src/testFixtures/java/module-info.java b/platform-sdk/swirlds-merkle/src/testFixtures/java/module-info.java index d6860d83b5f0..0a92472ef0af 100644 --- a/platform-sdk/swirlds-merkle/src/testFixtures/java/module-info.java +++ b/platform-sdk/swirlds-merkle/src/testFixtures/java/module-info.java @@ -6,17 +6,17 @@ exports com.swirlds.merkle.test.fixtures.map.lifecycle; exports com.swirlds.merkle.test.fixtures.map.pta; - requires com.hedera.pbj.runtime; + requires transitive com.swirlds.common.test.fixtures; + requires transitive com.swirlds.common; + requires transitive com.fasterxml.jackson.annotation; + requires transitive com.fasterxml.jackson.core; + requires transitive com.fasterxml.jackson.databind; requires com.swirlds.base; requires com.swirlds.fchashmap; requires com.swirlds.fcqueue; requires com.swirlds.logging; requires com.swirlds.merkle; + requires com.hedera.pbj.runtime; requires org.apache.logging.log4j.core; requires org.apache.logging.log4j; - requires transitive com.fasterxml.jackson.annotation; - requires transitive com.fasterxml.jackson.core; - requires transitive com.fasterxml.jackson.databind; - requires transitive com.swirlds.common.test.fixtures; - requires transitive com.swirlds.common; } diff --git a/platform-sdk/swirlds-platform-core/src/jmh/java/com/swirlds/platform/core/jmh/EventSerialization.java b/platform-sdk/swirlds-platform-core/src/jmh/java/com/swirlds/platform/core/jmh/EventSerialization.java index b3a3819b834d..1613b00f8125 100644 --- a/platform-sdk/swirlds-platform-core/src/jmh/java/com/swirlds/platform/core/jmh/EventSerialization.java +++ b/platform-sdk/swirlds-platform-core/src/jmh/java/com/swirlds/platform/core/jmh/EventSerialization.java @@ -59,7 +59,7 @@ public void setup() throws IOException, ConstructableRegistryException { final Random random = new Random(seed); event = new TestingEventBuilder(random).setSystemTransactionCount(1).build(); - StaticSoftwareVersion.setSoftwareVersion(event.getHashedData().getSoftwareVersion()); + StaticSoftwareVersion.setSoftwareVersion(event.getSoftwareVersion()); ConstructableRegistry.getInstance().registerConstructables("com.swirlds.platform.system"); final PipedInputStream inputStream = new PipedInputStream(); final PipedOutputStream outputStream = new PipedOutputStream(inputStream); diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformComponentBuilder.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformComponentBuilder.java index 2474dc9de53c..d73bac3d3893 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformComponentBuilder.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformComponentBuilder.java @@ -254,7 +254,7 @@ public PlatformComponentBuilder withEventHasher(@NonNull final EventHasher event @NonNull public EventHasher buildEventHasher() { if (eventHasher == null) { - eventHasher = new DefaultEventHasher(blocks.platformContext()); + eventHasher = new DefaultEventHasher(); } return eventHasher; } diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/transaction/system/SystemTransactionExtractionUtils.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/transaction/system/SystemTransactionExtractionUtils.java index bfbd74c4e333..0c2338b403c6 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/transaction/system/SystemTransactionExtractionUtils.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/components/transaction/system/SystemTransactionExtractionUtils.java @@ -25,6 +25,7 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Objects; @@ -66,24 +67,19 @@ private SystemTransactionExtractionUtils() {} @SuppressWarnings("unchecked") public static @Nullable List> extractFromEvent( @NonNull final GossipEvent event, @NonNull final Class systemTransactionTypeClass) { - - final var transactions = event.getHashedData().getTransactions(); - if (transactions == null) { - return null; - } - final List> scopedTransactions = new ArrayList<>(); - for (final Transaction transaction : event.getHashedData().getTransactions()) { - if (transaction.getPayload() != null - && systemTransactionTypeClass.isInstance( - transaction.getPayload().value())) { + final Iterator transactionIterator = event.transactionIterator(); + while (transactionIterator.hasNext()) { + final Transaction transaction = transactionIterator.next(); + if (systemTransactionTypeClass.isInstance(transaction.getPayload().value())) { scopedTransactions.add(new ScopedSystemTransaction<>( event.getHashedData().getCreatorId(), event.getHashedData().getSoftwareVersion(), (T) transaction.getPayload().value())); } } + return scopedTransactions.isEmpty() ? null : scopedTransactions; } } diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/consensus/GraphGenerations.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/consensus/GraphGenerations.java index c8175d4fda62..264ca2260057 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/consensus/GraphGenerations.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/consensus/GraphGenerations.java @@ -76,6 +76,6 @@ default boolean areAnyEventsAncient() { * @return true if the event is ancient, false otherwise */ default boolean isAncient(@NonNull final GossipEvent event) { - return event.getHashedData().getGeneration() < getMinGenerationNonAncient(); + return event.getGeneration() < getMinGenerationNonAncient(); } } diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/consensus/SyntheticSnapshot.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/consensus/SyntheticSnapshot.java index 3ee4568c35ac..3654704e8d85 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/consensus/SyntheticSnapshot.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/consensus/SyntheticSnapshot.java @@ -65,7 +65,7 @@ private SyntheticSnapshot() {} .toList(); return new ConsensusSnapshot( round, - List.of(judge.getHashedData().getHash()), + List.of(judge.getHash()), minimumJudgeInfos, lastConsensusOrder + 1, ConsensusUtils.calcMinTimestampForNextEvent(roundTimestamp)); diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/DefaultFutureEventBuffer.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/DefaultFutureEventBuffer.java index 85be11885d34..0d2f169906de 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/DefaultFutureEventBuffer.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/DefaultFutureEventBuffer.java @@ -81,15 +81,13 @@ public List addEvent(@NonNull final GossipEvent event) { if (eventWindow.isAncient(event)) { // we can safely ignore ancient events return null; - } else if (event.getHashedData().getBirthRound() <= eventWindow.getPendingConsensusRound()) { + } else if (event.getBirthRound() <= eventWindow.getPendingConsensusRound()) { // this is not a future event, no need to buffer it return List.of(event); } // this is a future event, buffer it - futureEvents - .computeIfAbsent(event.getHashedData().getBirthRound(), BUILD_LIST) - .add(event); + futureEvents.computeIfAbsent(event.getBirthRound(), BUILD_LIST).add(event); bufferedEventCount.incrementAndGet(); return null; } diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/GossipEvent.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/GossipEvent.java index 9fee4163efd1..a0d2e9a43f02 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/GossipEvent.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/GossipEvent.java @@ -21,8 +21,9 @@ import com.hedera.hapi.platform.event.EventConsensusData; import com.hedera.hapi.util.HapiUtils; import com.hedera.pbj.runtime.io.buffer.Bytes; +import com.swirlds.common.crypto.AbstractSerializableHashable; +import com.swirlds.common.crypto.Hash; import com.swirlds.common.crypto.SignatureType; -import com.swirlds.common.io.SelfSerializable; import com.swirlds.common.io.streams.SerializableDataInputStream; import com.swirlds.common.io.streams.SerializableDataOutputStream; import com.swirlds.common.platform.NodeId; @@ -38,13 +39,14 @@ import java.time.Instant; import java.util.Arrays; import java.util.Iterator; +import java.util.List; import java.util.Objects; import java.util.concurrent.CountDownLatch; /** * A class used to hold information about an event transferred through gossip */ -public class GossipEvent implements Event, SelfSerializable { +public class GossipEvent extends AbstractSerializableHashable implements Event { private static final EventConsensusData NO_CONSENSUS = new EventConsensusData(null, ConsensusConstants.NO_CONSENSUS_ORDER); private static final long CLASS_ID = 0xfe16b46795bfb8dcL; @@ -117,6 +119,19 @@ public GossipEvent(final BaseEventHashedData hashedData, final Bytes signature) this.timeReceived = Instant.now(); this.senderId = null; this.consensusData = NO_CONSENSUS; + if (hashedData.getHash() != null) { + setHash(hashedData.getHash()); + } + } + + /** + * Create a copy of this event while populating only the data received via gossip. Consensus data will not be + * copied. + * + * @return a copy of this event + */ + public GossipEvent copyGossipedData() { + return new GossipEvent(hashedData, signature); } /** @@ -144,8 +159,15 @@ public long getStreamSequenceNumber() { } /** - * {@inheritDoc} + * Since events are being migrated to protobuf, calculating the hash of an event will change. This method serializes + * the bytes that make up an event's hash prior to migration. + * @param out the stream to write the bytes to */ + public void serializeLegacyHashBytes(@NonNull final SerializableDataOutputStream out) throws IOException { + Objects.requireNonNull(out); + out.writeSerializable(hashedData, true); + } + @Override public void serialize(final SerializableDataOutputStream out) throws IOException { out.writeSerializable(hashedData, false); @@ -153,9 +175,6 @@ public void serialize(final SerializableDataOutputStream out) throws IOException signature.writeTo(out); } - /** - * {@inheritDoc} - */ @Override public void deserialize(final SerializableDataInputStream in, final int version) throws IOException { hashedData = in.readSerializable(false, BaseEventHashedData::new); @@ -218,6 +237,15 @@ public long getGeneration() { return hashedData.getGeneration(); } + /** + * Get the birth round of the event. + * + * @return the birth round of the event + */ + public long getBirthRound() { + return hashedData.getBirthRound(); + } + /** * @return the number of payloads this event contains */ @@ -316,17 +344,11 @@ public void awaitPrehandleCompletion() { abortAndLogIfInterrupted(prehandleCompleted::await, "interrupted while waiting for prehandle completion"); } - /** - * {@inheritDoc} - */ @Override public long getClassId() { return CLASS_ID; } - /** - * {@inheritDoc} - */ @Override public int getVersion() { return ClassVersion.BIRTH_ROUND; @@ -338,8 +360,31 @@ public int getMinimumSupportedVersion() { } /** - * {@inheritDoc} + * Get the event descriptor for the self parent. + * + * @return the event descriptor for the self parent */ + @Nullable + public EventDescriptor getSelfParent() { + return hashedData.getSelfParent(); + } + + /** + * Get the event descriptors for the other parents. + * + * @return the event descriptors for the other parents + */ + @NonNull + public List getOtherParents() { + return hashedData.getOtherParents(); + } + + /** @return a list of all parents, self parent (if any), + all other parents */ + @NonNull + public List getAllParents() { + return hashedData.getAllParents(); + } + @Override public String toString() { final StringBuilder stringBuilder = new StringBuilder(); @@ -393,12 +438,15 @@ public boolean equals(final Object o) { && Objects.equals(consensusData, that.consensusData); } - /** - * {@inheritDoc} - */ @Override public int hashCode() { - return hashedData.getHash().hashCode(); + return getHash().hashCode(); + } + + @Override + public void setHash(final Hash hash) { + super.setHash(hash); + hashedData.setHash(hash); } /** diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/creation/tipset/TipsetEventCreator.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/creation/tipset/TipsetEventCreator.java index 8d16893e001a..7446e2724159 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/creation/tipset/TipsetEventCreator.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/creation/tipset/TipsetEventCreator.java @@ -18,7 +18,6 @@ import static com.swirlds.logging.legacy.LogMarker.EXCEPTION; import static com.swirlds.platform.event.creation.tipset.TipsetAdvancementWeight.ZERO_ADVANCEMENT_WEIGHT; -import static com.swirlds.platform.event.creation.tipset.TipsetUtils.getParentDescriptors; import static com.swirlds.platform.system.events.EventConstants.CREATOR_ID_UNDEFINED; import com.swirlds.base.time.Time; @@ -175,7 +174,7 @@ public void registerEvent(@NonNull final GossipEvent event) { return; } - final NodeId eventCreator = event.getHashedData().getCreatorId(); + final NodeId eventCreator = event.getCreatorId(); if (!addressBook.contains(eventCreator)) { return; } @@ -186,12 +185,9 @@ public void registerEvent(@NonNull final GossipEvent event) { // Normally we will ingest self events before we get to this point, but it's possible // to learn of self events for the first time here if we are loading from a restart or reconnect. lastSelfEvent = event.getDescriptor(); - lastSelfEventCreationTime = event.getHashedData().getTimeCreated(); - lastSelfEventTransactionCount = event.getHashedData().getTransactions() == null - ? 0 - : event.getHashedData().getTransactions().length; - childlessOtherEventTracker.registerSelfEventParents( - event.getHashedData().getOtherParents()); + lastSelfEventCreationTime = event.getTimeCreated(); + lastSelfEventTransactionCount = event.getPayloadCount(); + childlessOtherEventTracker.registerSelfEventParents(event.getOtherParents()); } else { // We already ingested this self event (when it was created), return; @@ -199,7 +195,7 @@ public void registerEvent(@NonNull final GossipEvent event) { } final EventDescriptor descriptor = event.getDescriptor(); - final List parentDescriptors = getParentDescriptors(event.getHashedData()); + final List parentDescriptors = event.getAllParents(); tipsetTracker.addEvent(descriptor, parentDescriptors); diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/creation/tipset/TipsetUtils.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/creation/tipset/TipsetUtils.java deleted file mode 100644 index e6e6eb3ae571..000000000000 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/creation/tipset/TipsetUtils.java +++ /dev/null @@ -1,51 +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.platform.event.creation.tipset; - -import com.swirlds.platform.system.events.BaseEventHashedData; -import com.swirlds.platform.system.events.EventDescriptor; -import edu.umd.cs.findbugs.annotations.NonNull; -import java.util.ArrayList; -import java.util.List; - -/** - * Misc tipset utilities. - */ -public final class TipsetUtils { - - private TipsetUtils() {} - - /** - * Get the descriptors of an event's parents. - * - * @param event the event to the parent descriptors for - * @return a list of parent descriptors - */ - @NonNull - public static List getParentDescriptors(@NonNull final BaseEventHashedData event) { - final List parentDescriptors = new ArrayList<>(2); - - if (event.hasSelfParent()) { - parentDescriptors.add(event.getSelfParent()); - } - if (event.hasOtherParent()) { - event.getOtherParents().forEach(parentDescriptors::add); - } - - return parentDescriptors; - } -} diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/hashing/DefaultEventHasher.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/hashing/DefaultEventHasher.java index e2cc00a8ebca..35d98a1bf84c 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/hashing/DefaultEventHasher.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/hashing/DefaultEventHasher.java @@ -16,34 +16,17 @@ package com.swirlds.platform.event.hashing; -import com.swirlds.common.context.PlatformContext; -import com.swirlds.common.crypto.Cryptography; import com.swirlds.platform.event.GossipEvent; import edu.umd.cs.findbugs.annotations.NonNull; /** * Default implementation of the {@link EventHasher}. - * */ public class DefaultEventHasher implements EventHasher { - private final Cryptography cryptography; - - /** - * Constructs a new event hasher. - * - * @param platformContext the platform context - */ - public DefaultEventHasher(@NonNull final PlatformContext platformContext) { - this.cryptography = platformContext.getCryptography(); - } - - /** - * {@inheritDoc} - */ @Override @NonNull public GossipEvent hashEvent(@NonNull final GossipEvent event) { - cryptography.digestSync(event.getHashedData()); + new StatefulEventHasher().hashEvent(event); return event; } } diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/hashing/StatefulEventHasher.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/hashing/StatefulEventHasher.java new file mode 100644 index 000000000000..60253c12bb83 --- /dev/null +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/hashing/StatefulEventHasher.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.swirlds.platform.event.hashing; + +import com.swirlds.common.crypto.DigestType; +import com.swirlds.common.crypto.Hash; +import com.swirlds.common.crypto.HashingOutputStream; +import com.swirlds.common.io.streams.SerializableDataOutputStream; +import com.swirlds.platform.event.GossipEvent; +import edu.umd.cs.findbugs.annotations.NonNull; +import java.io.IOException; + +/** + * An implementation of {@link EventHasher} that is stateful and thus not safe to use by multiple threads concurrently. + */ +public class StatefulEventHasher implements EventHasher { + private final HashingOutputStream hashingOutputStream = new HashingOutputStream(DigestType.SHA_384.buildDigest()); + private final SerializableDataOutputStream outputStream = new SerializableDataOutputStream(hashingOutputStream); + + @NonNull + @Override + public GossipEvent hashEvent(@NonNull final GossipEvent event) { + try { + event.serializeLegacyHashBytes(outputStream); + event.setHash(new Hash(hashingOutputStream.getDigest(), DigestType.SHA_384)); + return event; + } catch (final IOException e) { + throw new RuntimeException("An exception occurred while trying to hash an event!", e); + } + } +} diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/linking/AbstractInOrderLinker.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/linking/AbstractInOrderLinker.java index e7df839b06f6..f9c42d079793 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/linking/AbstractInOrderLinker.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/linking/AbstractInOrderLinker.java @@ -29,7 +29,6 @@ import com.swirlds.platform.internal.EventImpl; import com.swirlds.platform.sequence.map.SequenceMap; import com.swirlds.platform.sequence.map.StandardSequenceMap; -import com.swirlds.platform.system.events.BaseEventHashedData; import com.swirlds.platform.system.events.EventDescriptor; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -135,12 +134,11 @@ public EventImpl linkEvent(@NonNull final GossipEvent event) { return null; } - final BaseEventHashedData hashedData = event.getHashedData(); - final EventImpl selfParent = getParentToLink(event, hashedData.getSelfParent()); + final EventImpl selfParent = getParentToLink(event, event.getSelfParent()); // FUTURE WORK: Extend other parent linking to support multiple other parents. // Until then, take the first parent in the list. - final List otherParents = hashedData.getOtherParents(); + final List otherParents = event.getOtherParents(); final EventImpl otherParent = otherParents.isEmpty() ? null : getParentToLink(event, otherParents.get(0)); final EventImpl linkedEvent = new EventImpl(event, selfParent, otherParent); @@ -320,9 +318,8 @@ private EventImpl getParentToLink( return null; } - final Instant parentTimeCreated = - candidateParent.getBaseEvent().getHashedData().getTimeCreated(); - final Instant childTimeCreated = child.getHashedData().getTimeCreated(); + final Instant parentTimeCreated = candidateParent.getBaseEvent().getTimeCreated(); + final Instant childTimeCreated = child.getTimeCreated(); // only do this check for self parent, since the event creator doesn't consider other parent creation time // when deciding on the event creation time diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/orphan/DefaultOrphanBuffer.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/orphan/DefaultOrphanBuffer.java index 12cbd81cf794..dc5d34ed3576 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/orphan/DefaultOrphanBuffer.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/orphan/DefaultOrphanBuffer.java @@ -33,7 +33,6 @@ import edu.umd.cs.findbugs.annotations.NonNull; import java.util.ArrayList; import java.util.Deque; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Objects; @@ -201,9 +200,7 @@ private List missingParentBecameAncient(@NonNull final ParentAndOrp private List getMissingParents(@NonNull final GossipEvent event) { final List missingParents = new ArrayList<>(); - final Iterator parentIterator = new ParentIterator(event); - while (parentIterator.hasNext()) { - final EventDescriptor parent = parentIterator.next(); + for (final EventDescriptor parent : event.getAllParents()) { if (!eventsWithParents.contains(parent) && !eventWindow.isAncient(parent)) { missingParents.add(parent); } diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/orphan/ParentIterator.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/orphan/ParentIterator.java deleted file mode 100644 index ec9e6b4c37cf..000000000000 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/orphan/ParentIterator.java +++ /dev/null @@ -1,82 +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.platform.event.orphan; - -import com.swirlds.platform.event.GossipEvent; -import com.swirlds.platform.system.events.EventDescriptor; -import edu.umd.cs.findbugs.annotations.NonNull; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.NoSuchElementException; - -/** - * Iterates over the parents of an event. This class is temporary and intended to allow code to be written that works - * for the current binary event parentage and the future n-ary event parentage. - */ -class ParentIterator implements Iterator { - /** - * The number of parents that have been returned so far. - */ - private int returnedEvents; - - /** - * The parents of the event. - */ - private final List parents; - - /** - * Constructor. - * - * @param event the event whose parents we want to iterate over - */ - ParentIterator(@NonNull final GossipEvent event) { - parents = new ArrayList<>(); - - final EventDescriptor selfParent = event.getHashedData().getSelfParent(); - final List otherParents = event.getHashedData().getOtherParents(); - - if (selfParent != null) { - parents.add(selfParent); - } - otherParents.forEach(parents::add); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasNext() { - return returnedEvents < parents.size(); - } - - /** - * {@inheritDoc} - */ - @NonNull - @Override - public EventDescriptor next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - - final int indexToReturn = returnedEvents; - returnedEvents++; - - return parents.get(indexToReturn); - } -} diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PcesMutableFile.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PcesMutableFile.java index 0a498380097b..1759606850af 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PcesMutableFile.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PcesMutableFile.java @@ -94,9 +94,8 @@ public boolean canContain(final long ancientIdentifier) { */ public void writeEvent(final GossipEvent event) throws IOException { if (!descriptor.canContain(event.getAncientIndicator(descriptor.getFileType()))) { - throw new IllegalStateException( - "Cannot write event " + event.getHashedData().getHash() + " with ancient indicator " - + event.getAncientIndicator(descriptor.getFileType()) + " to file " + descriptor); + throw new IllegalStateException("Cannot write event " + event.getHash() + " with ancient indicator " + + event.getAncientIndicator(descriptor.getFileType()) + " to file " + descriptor); } out.writeSerializable(event, false); highestAncientIdentifierInFile = diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PcesReplayer.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PcesReplayer.java index 784c08c7d786..77c6d4d2676c 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PcesReplayer.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/preconsensus/PcesReplayer.java @@ -173,7 +173,7 @@ public NoInput replayPces(@NonNull final IOIterator eventIterator) final GossipEvent event = eventIterator.next(); eventCount++; - transactionCount += event.getHashedData().getTransactions().length; + transactionCount += event.getPayloadCount(); eventOutputWire.forward(event); } diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/validation/DefaultEventSignatureValidator.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/validation/DefaultEventSignatureValidator.java index 085089eb8e91..a2c50c0d6a98 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/validation/DefaultEventSignatureValidator.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/validation/DefaultEventSignatureValidator.java @@ -135,7 +135,7 @@ public DefaultEventSignatureValidator( */ @Nullable private AddressBook determineApplicableAddressBook(@NonNull final GossipEvent event) { - final SoftwareVersion eventVersion = event.getHashedData().getSoftwareVersion(); + final SoftwareVersion eventVersion = event.getSoftwareVersion(); final int softwareComparison = currentSoftwareVersion.compareTo(eventVersion); if (softwareComparison < 0) { @@ -176,7 +176,7 @@ private boolean isSignatureValid(@NonNull final GossipEvent event) { return false; } - final NodeId eventCreatorId = event.getHashedData().getCreatorId(); + final NodeId eventCreatorId = event.getCreatorId(); if (!applicableAddressBook.contains(eventCreatorId)) { rateLimitedLogger.error( @@ -195,8 +195,8 @@ private boolean isSignatureValid(@NonNull final GossipEvent event) { return false; } - final boolean isSignatureValid = signatureVerifier.verifySignature( - event.getHashedData().getHash().getBytes(), event.getSignature(), publicKey); + final boolean isSignatureValid = + signatureVerifier.verifySignature(event.getHash().getBytes(), event.getSignature(), publicKey); if (!isSignatureValid) { rateLimitedLogger.error( @@ -204,7 +204,7 @@ private boolean isSignatureValid(@NonNull final GossipEvent event) { "Event failed signature check. Event: {}, Signature: {}, Hash: {}", event, event.getSignature().toHex(), - event.getHashedData().getHash()); + event.getHash()); } return isSignatureValid; diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/validation/DefaultInternalEventValidator.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/validation/DefaultInternalEventValidator.java index 986888befbc1..0f26c8e5c75e 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/validation/DefaultInternalEventValidator.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/event/validation/DefaultInternalEventValidator.java @@ -29,7 +29,7 @@ import com.swirlds.platform.event.GossipEvent; import com.swirlds.platform.eventhandling.EventConfig; import com.swirlds.platform.gossip.IntakeEventCounter; -import com.swirlds.platform.system.events.BaseEventHashedData; +import com.swirlds.platform.system.events.EventConstants; import com.swirlds.platform.system.events.EventDescriptor; import com.swirlds.platform.system.transaction.ConsensusTransaction; import edu.umd.cs.findbugs.annotations.NonNull; @@ -64,7 +64,6 @@ public class DefaultInternalEventValidator implements InternalEventValidator { private final AncientMode ancientMode; - private final RateLimitedLogger nullHashedDataLogger; private final RateLimitedLogger nullUnhashedDataLogger; private final RateLimitedLogger tooManyTransactionBytesLogger; private final RateLimitedLogger inconsistentSelfParentLogger; @@ -73,7 +72,6 @@ public class DefaultInternalEventValidator implements InternalEventValidator { private final RateLimitedLogger invalidGenerationLogger; private final RateLimitedLogger invalidBirthRoundLogger; - private final LongAccumulator nullHashedDataAccumulator; private final LongAccumulator nullUnhashedDataAccumulator; private final LongAccumulator tooManyTransactionBytesAccumulator; private final LongAccumulator inconsistentSelfParentAccumulator; @@ -103,7 +101,6 @@ public DefaultInternalEventValidator( .getConfigData(EventConfig.class) .getAncientMode(); - this.nullHashedDataLogger = new RateLimitedLogger(logger, platformContext.getTime(), MINIMUM_LOG_PERIOD); this.nullUnhashedDataLogger = new RateLimitedLogger(logger, platformContext.getTime(), MINIMUM_LOG_PERIOD); this.tooManyTransactionBytesLogger = new RateLimitedLogger(logger, platformContext.getTime(), MINIMUM_LOG_PERIOD); @@ -115,11 +112,6 @@ public DefaultInternalEventValidator( this.invalidGenerationLogger = new RateLimitedLogger(logger, platformContext.getTime(), MINIMUM_LOG_PERIOD); this.invalidBirthRoundLogger = new RateLimitedLogger(logger, platformContext.getTime(), MINIMUM_LOG_PERIOD); - this.nullHashedDataAccumulator = platformContext - .getMetrics() - .getOrCreate(new LongAccumulator.Config(PLATFORM_CATEGORY, "eventsWithNullHashedData") - .withDescription("Events that had null hashed data") - .withUnit("events")); this.nullUnhashedDataAccumulator = platformContext .getMetrics() .getOrCreate(new LongAccumulator.Config(PLATFORM_CATEGORY, "eventsWithNullUnhashedData") @@ -164,16 +156,9 @@ public DefaultInternalEventValidator( * @return true if the required fields of the event are non-null, otherwise false */ private boolean areRequiredFieldsNonNull(@NonNull final GossipEvent event) { - if (event.getHashedData() == null) { - // do not log the event itself, since toString would throw a NullPointerException - nullHashedDataLogger.error(EXCEPTION.getMarker(), "Event has null hashed data"); - nullHashedDataAccumulator.update(1); - return false; - } - if (event.getSignature() == null) { // do not log the event itself, since toString would throw a NullPointerException - nullUnhashedDataLogger.error(EXCEPTION.getMarker(), "Event has null unhashed data"); + nullUnhashedDataLogger.error(EXCEPTION.getMarker(), "Event has null signature"); nullUnhashedDataAccumulator.update(1); return false; } @@ -213,11 +198,9 @@ private boolean isTransactionByteCountValid(@NonNull final GossipEvent event) { * @return true if the parent hashes and generations of the event are internally consistent, otherwise false */ private boolean areParentsInternallyConsistent(@NonNull final GossipEvent event) { - final BaseEventHashedData hashedData = event.getHashedData(); - // If a parent is not missing, then the generation and birth round must be valid. - final EventDescriptor selfParent = event.getHashedData().getSelfParent(); + final EventDescriptor selfParent = event.getSelfParent(); if (selfParent != null) { if (selfParent.getGeneration() < FIRST_GENERATION) { inconsistentSelfParentLogger.error( @@ -229,7 +212,7 @@ private boolean areParentsInternallyConsistent(@NonNull final GossipEvent event) } } - for (final EventDescriptor otherParent : hashedData.getOtherParents()) { + for (final EventDescriptor otherParent : event.getOtherParents()) { if (otherParent.getGeneration() < FIRST_GENERATION) { inconsistentOtherParentLogger.error( EXCEPTION.getMarker(), @@ -242,7 +225,7 @@ private boolean areParentsInternallyConsistent(@NonNull final GossipEvent event) // only single node networks are allowed to have identical self-parent and other-parent hashes if (!singleNodeNetwork && selfParent != null) { - for (final EventDescriptor otherParent : hashedData.getOtherParents()) { + for (final EventDescriptor otherParent : event.getOtherParents()) { if (selfParent.getHash().equals(otherParent.getHash())) { identicalParentsLogger.error( EXCEPTION.getMarker(), @@ -276,9 +259,9 @@ private boolean isEventGenerationValid(@NonNull final GossipEvent event) { return false; } - long maxParentGeneration = event.getHashedData().getSelfParentGen(); - for (final EventDescriptor otherParent : event.getHashedData().getOtherParents()) { - maxParentGeneration = Math.max(maxParentGeneration, otherParent.getGeneration()); + long maxParentGeneration = EventConstants.GENERATION_UNDEFINED; + for (final EventDescriptor parent : event.getAllParents()) { + maxParentGeneration = Math.max(maxParentGeneration, parent.getGeneration()); } if (eventGeneration != maxParentGeneration + 1) { @@ -309,12 +292,8 @@ private boolean isEventBirthRoundValid(@NonNull final GossipEvent event) { final long eventBirthRound = event.getDescriptor().getBirthRound(); long maxParentBirthRound = ROUND_NEGATIVE_INFINITY; - final EventDescriptor parent = event.getHashedData().getSelfParent(); - if (parent != null) { - maxParentBirthRound = parent.getBirthRound(); - } - for (final EventDescriptor otherParent : event.getHashedData().getOtherParents()) { - maxParentBirthRound = Math.max(maxParentBirthRound, otherParent.getBirthRound()); + for (final EventDescriptor parent : event.getAllParents()) { + maxParentBirthRound = Math.max(maxParentBirthRound, parent.getBirthRound()); } if (eventBirthRound < maxParentBirthRound) { diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gossip/shadowgraph/SyncUtils.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gossip/shadowgraph/SyncUtils.java index a2bf74d4f345..984f916de350 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gossip/shadowgraph/SyncUtils.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gossip/shadowgraph/SyncUtils.java @@ -401,11 +401,7 @@ public static List filterLikelyDuplicates( // If we've decided to send an event, we also want to send its parents if those parents are needed // by the peer. filteredList.addFirst(event); - final Hash selfParentHash = event.getBaseEvent().getHashedData().getSelfParentHash(); - if (selfParentHash != null) { - parentHashesOfEventsToSend.add(selfParentHash); - } - for (final EventDescriptor otherParent : event.getHashedData().getOtherParents()) { + for (final EventDescriptor otherParent : event.getBaseEvent().getAllParents()) { parentHashesOfEventsToSend.add(otherParent.getHash()); } } diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gui/GuiEventStorage.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gui/GuiEventStorage.java index 3002013be6ac..27b27a37a59a 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gui/GuiEventStorage.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gui/GuiEventStorage.java @@ -80,7 +80,8 @@ public Consensus getConsensus() { public synchronized void handlePreconsensusEvent(@NonNull final GossipEvent event) { maxGeneration = Math.max(maxGeneration, event.getGeneration()); - final EventImpl eventImpl = linker.linkEvent(event); + // since the gui will modify the event, we need to copy it + final EventImpl eventImpl = linker.linkEvent(event.copyGossipedData()); if (eventImpl == null) { return; } diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gui/SimpleLinker.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gui/SimpleLinker.java index cc5f85b8e307..63c2b91e2959 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gui/SimpleLinker.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/gui/SimpleLinker.java @@ -24,7 +24,6 @@ import com.swirlds.platform.internal.EventImpl; import com.swirlds.platform.sequence.map.SequenceMap; import com.swirlds.platform.sequence.map.StandardSequenceMap; -import com.swirlds.platform.system.events.BaseEventHashedData; import com.swirlds.platform.system.events.EventDescriptor; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -118,9 +117,8 @@ private EventImpl getParentToLink( return null; } - final Instant parentTimeCreated = - candidateParent.getBaseEvent().getHashedData().getTimeCreated(); - final Instant childTimeCreated = child.getHashedData().getTimeCreated(); + final Instant parentTimeCreated = candidateParent.getBaseEvent().getTimeCreated(); + final Instant childTimeCreated = child.getTimeCreated(); // only do this check for self parent, since the event creator doesn't consider other parent creation time // when deciding on the event creation time @@ -145,12 +143,11 @@ public EventImpl linkEvent(@NonNull final GossipEvent event) { return null; } - final BaseEventHashedData hashedData = event.getHashedData(); - final EventImpl selfParent = getParentToLink(event, hashedData.getSelfParent()); + final EventImpl selfParent = getParentToLink(event, event.getSelfParent()); // FUTURE WORK: Extend other parent linking to support multiple other parents. // Until then, take the first parent in the list. - final List otherParents = hashedData.getOtherParents(); + final List otherParents = event.getOtherParents(); final EventImpl otherParent = otherParents.isEmpty() ? null : getParentToLink(event, otherParents.get(0)); final EventImpl linkedEvent = new EventImpl(event, selfParent, otherParent); diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/internal/EventImpl.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/internal/EventImpl.java index 1ec294279e9d..ce42446e30ee 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/internal/EventImpl.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/internal/EventImpl.java @@ -326,23 +326,11 @@ public ConsensusData getConsensusData() { ////////////////////////////////////////// public Instant getTimeCreated() { - return baseEvent.getHashedData().getTimeCreated(); - } - - public long getOtherParentGen() { - return baseEvent.getHashedData().getOtherParentGen(); - } - - public Hash getSelfParentHash() { - return baseEvent.getHashedData().getSelfParentHash(); - } - - public Hash getOtherParentHash() { - return baseEvent.getHashedData().getOtherParentHash(); + return baseEvent.getTimeCreated(); } public Hash getBaseHash() { - return baseEvent.getHashedData().getHash(); + return baseEvent.getHash(); } /** @@ -407,7 +395,7 @@ public Instant getConsensusTimestamp() { * @return the generation of this event */ public long getGeneration() { - return baseEvent.getHashedData().getGeneration(); + return baseEvent.getGeneration(); } /** @@ -416,7 +404,7 @@ public long getGeneration() { * @return the birth round of this event */ public long getBirthRound() { - return baseEvent.getHashedData().getBirthRound(); + return baseEvent.getBirthRound(); } /** @@ -425,7 +413,7 @@ public long getBirthRound() { @Override @Nullable public SoftwareVersion getSoftwareVersion() { - return baseEvent.getHashedData().getSoftwareVersion(); + return baseEvent.getSoftwareVersion(); } /** @@ -434,7 +422,7 @@ public SoftwareVersion getSoftwareVersion() { @Override @NonNull public NodeId getCreatorId() { - return baseEvent.getHashedData().getCreatorId(); + return baseEvent.getCreatorId(); } /** diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/metrics/AddedEventMetrics.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/metrics/AddedEventMetrics.java index 4f5a8cc619d8..a8d94407b962 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/metrics/AddedEventMetrics.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/metrics/AddedEventMetrics.java @@ -33,6 +33,7 @@ import com.swirlds.metrics.api.Metrics; import com.swirlds.platform.internal.EventImpl; import com.swirlds.platform.stats.AverageStat; +import com.swirlds.platform.system.events.EventDescriptor; import com.swirlds.platform.system.transaction.ConsensusTransaction; import java.time.temporal.ChronoUnit; import java.util.Objects; @@ -157,8 +158,12 @@ public AddedEventMetrics(final NodeId selfId, final Metrics metrics) { public void eventAdded(final EventImpl event) { if (event.isCreatedBy(selfId)) { eventsCreatedPerSecond.cycle(); - if (event.getHashedData().hasOtherParent()) { - averageOtherParentAgeDiff.update(event.getGeneration() - event.getOtherParentGen()); + if (!event.getBaseEvent().getOtherParents().isEmpty()) { + averageOtherParentAgeDiff.update(event.getGeneration() + - event.getBaseEvent().getOtherParents().stream() + .map(EventDescriptor::getGeneration) + .max(Long::compareTo) + .orElse(0L)); } } else { avgCreatedReceivedTime.update( diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/recovery/EventRecoveryWorkflow.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/recovery/EventRecoveryWorkflow.java index a7fcfa4ea8ad..f7c21fe5e65d 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/recovery/EventRecoveryWorkflow.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/recovery/EventRecoveryWorkflow.java @@ -24,7 +24,6 @@ import static com.swirlds.platform.util.BootstrapUtils.setupConstructableRegistry; import com.swirlds.common.context.PlatformContext; -import com.swirlds.common.crypto.CryptographyHolder; import com.swirlds.common.crypto.Hash; import com.swirlds.common.io.IOIterator; import com.swirlds.common.merkle.crypto.MerkleCryptoFactory; @@ -43,6 +42,7 @@ import com.swirlds.platform.consensus.SyntheticSnapshot; import com.swirlds.platform.crypto.CryptoStatic; import com.swirlds.platform.event.GossipEvent; +import com.swirlds.platform.event.hashing.StatefulEventHasher; import com.swirlds.platform.event.preconsensus.PcesFile; import com.swirlds.platform.event.preconsensus.PcesMutableFile; import com.swirlds.platform.eventhandling.EventConfig; @@ -376,7 +376,7 @@ private static ReservedSignedState handleNextRound( previousState.get().getState().throwIfImmutable(); final State newState = previousState.get().getState().copy(); final EventImpl lastEvent = (EventImpl) getLastEvent(round); - CryptographyHolder.get().digestSync(lastEvent.getBaseEvent().getHashedData()); + new StatefulEventHasher().hashEvent(lastEvent.getBaseEvent()); final PlatformState platformState = newState.getPlatformState(); diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/State.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/State.java index 50e16edfe0f6..e80de66511f0 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/State.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/state/State.java @@ -26,9 +26,7 @@ import com.swirlds.common.utility.RuntimeObjectRecord; import com.swirlds.common.utility.RuntimeObjectRegistry; import com.swirlds.platform.consensus.ConsensusSnapshot; -import com.swirlds.platform.internal.EventImpl; import com.swirlds.platform.system.SwirldState; -import java.util.HashMap; import java.util.List; import java.util.Objects; import org.apache.logging.log4j.LogManager; @@ -173,16 +171,6 @@ public State copy() { return new State(this); } - // Perhaps this belongs in a different file - public static void linkParents(final EventImpl[] events) { - final HashMap eventsByHash = new HashMap<>(); - for (final EventImpl event : events) { - eventsByHash.put(event.getBaseHash(), event); - event.setSelfParent(eventsByHash.get(event.getSelfParentHash())); - event.setOtherParent(eventsByHash.get(event.getOtherParentHash())); - } - } - /** * {@inheritDoc} */ diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/system/address/AddressBook.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/system/address/AddressBook.java index ce7ac1edb660..8fd58cd51da8 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/system/address/AddressBook.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/system/address/AddressBook.java @@ -62,7 +62,7 @@ private static class ClassVersion { */ public static final int AD_HOC_SERIALIZATION = 2; /** - * In this version, AddressBook uses the serialization utilities to read & write the list of addresses. + * In this version, AddressBook uses the serialization utilities to read and write the list of addresses. */ public static final int UTILITY_SERIALIZATION = 3; /** diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/system/events/BaseEventHashedData.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/system/events/BaseEventHashedData.java index cd705f5f9c14..935cb2cd227a 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/system/events/BaseEventHashedData.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/system/events/BaseEventHashedData.java @@ -41,6 +41,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.stream.Stream; /** * A class used to store base event data that is used to create the hash of that event. @@ -92,6 +93,9 @@ public static class ClassVersion { */ private List otherParents; + /** a combined list of all parents, selfParent + otherParents */ + private List allParents; + /** * creation time, as claimed by its creator */ @@ -146,6 +150,7 @@ public BaseEventHashedData( Objects.requireNonNull(otherParents, "The otherParents must not be null"); otherParents.forEach(Objects::requireNonNull); this.otherParents = otherParents; + this.allParents = createAllParentsList(); this.birthRound = birthRound; this.birthRoundOverride = birthRound; this.timeCreated = Objects.requireNonNull(timeCreated, "The timeCreated must not be null"); @@ -192,6 +197,7 @@ private void deserialize( } selfParent = in.readSerializable(false, EventDescriptor::new); otherParents = in.readSerializableList(AddressBook.MAX_ADDRESSES, false, EventDescriptor::new); + allParents = createAllParentsList(); birthRound = in.readLong(); birthRoundOverride = birthRound; @@ -312,6 +318,19 @@ public List getOtherParents() { return otherParents; } + /** @return a list of all parents, self parent (if any), + all other parents */ + @NonNull + public List getAllParents() { + return allParents; + } + + @NonNull + private List createAllParentsList() { + return !hasSelfParent() + ? otherParents + : Stream.concat(Stream.of(selfParent), otherParents.stream()).toList(); + } + /** * Get the self parent generation * diff --git a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/PlatformSchedulersConfig.java b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/PlatformSchedulersConfig.java index 4799d22714db..e49d0078c4ff 100644 --- a/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/PlatformSchedulersConfig.java +++ b/platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/wiring/PlatformSchedulersConfig.java @@ -127,7 +127,7 @@ public record PlatformSchedulersConfig( TaskSchedulerConfiguration gossip, @ConfigProperty(defaultValue = "CONCURRENT CAPACITY(-1) UNHANDLED_TASK_METRIC") TaskSchedulerConfiguration eventHasher, - @ConfigProperty(defaultValue = "CONCURRENT CAPACITY(-1) UNHANDLED_TASK_METRIC") + @ConfigProperty(defaultValue = "SEQUENTIAL CAPACITY(-1) UNHANDLED_TASK_METRIC") TaskSchedulerConfiguration postHashCollector, @ConfigProperty(defaultValue = "SEQUENTIAL CAPACITY(500) FLUSHABLE UNHANDLED_TASK_METRIC") TaskSchedulerConfiguration branchDetector, 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 2fd8ee671b35..05f7d3385899 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 @@ -131,6 +131,7 @@ exports com.swirlds.platform.pool; exports com.swirlds.platform.state.snapshot; + requires transitive com.hedera.node.hapi; requires transitive com.swirlds.base; requires transitive com.swirlds.cli; requires transitive com.swirlds.common; @@ -143,7 +144,6 @@ requires transitive com.swirlds.virtualmap; requires transitive com.fasterxml.jackson.annotation; requires transitive com.fasterxml.jackson.databind; - requires transitive com.hedera.node.hapi; requires transitive com.hedera.pbj.runtime; requires transitive info.picocli; requires transitive org.apache.logging.log4j; diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/FutureEventBufferTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/FutureEventBufferTests.java index e542d00b482f..9c0ec340e462 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/FutureEventBufferTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/FutureEventBufferTests.java @@ -82,7 +82,7 @@ void futureEventsBufferedTest() { events.add(event); } // Put the events in topological order - events.sort(Comparator.comparingLong(a -> a.getHashedData().getBirthRound())); + events.sort(Comparator.comparingLong(a -> a.getBirthRound())); final List futureEvents = new ArrayList<>(); for (final GossipEvent event : events) { @@ -92,7 +92,7 @@ void futureEventsBufferedTest() { if (eventWindow.isAncient(event)) { // Ancient events should be discarded. assertNull(returnedEvent); - } else if (event.getHashedData().getBirthRound() <= eventWindow.getPendingConsensusRound()) { + } else if (event.getBirthRound() <= eventWindow.getPendingConsensusRound()) { // Non-future events should be returned immediately. assertSame(event, returnedEvent); } else { @@ -114,7 +114,7 @@ void futureEventsBufferedTest() { final List bufferedEvents = futureEventBuffer.updateEventWindow(newEventWindow); for (final GossipEvent event : bufferedEvents) { - assertEquals(newPendingConsensusRound, event.getHashedData().getBirthRound()); + assertEquals(newPendingConsensusRound, event.getBirthRound()); unBufferedEvents.add(event); } } @@ -167,7 +167,7 @@ void eventsGoAncientWhileBufferedTest() { events.add(event); } // Put the events in topological order - events.sort(Comparator.comparingLong(a -> a.getHashedData().getBirthRound())); + events.sort(Comparator.comparingLong(a -> a.getBirthRound())); for (final GossipEvent event : events) { final List returnedEvents = futureEventBuffer.addEvent(event); @@ -176,7 +176,7 @@ void eventsGoAncientWhileBufferedTest() { if (eventWindow.isAncient(event)) { // Ancient events should be discarded. assertNull(returnedEvent); - } else if (event.getHashedData().getBirthRound() <= eventWindow.getPendingConsensusRound()) { + } else if (event.getBirthRound() <= eventWindow.getPendingConsensusRound()) { // Non-future events should be returned immediately. assertSame(event, returnedEvent); } else { diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/linking/ConsensusEventLinkerTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/linking/ConsensusEventLinkerTests.java index 3b3a5e276a05..c53519055cdf 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/linking/ConsensusEventLinkerTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/linking/ConsensusEventLinkerTests.java @@ -94,17 +94,17 @@ void eventsAreUnlinkedTest(final boolean birthRoundAncientMode) { linkedEvents.add(linkedEvent); assertSame(event, linkedEvent.getBaseEvent()); - final EventDescriptor selfParent = event.getHashedData().getSelfParent(); + final EventDescriptor selfParent = event.getSelfParent(); if (selfParent == null || eventWindow.isAncient(selfParent)) { assertNull(linkedEvent.getSelfParent()); } else { assertNotNull(linkedEvent.getSelfParent()); assertEquals( - event.getHashedData().getSelfParent(), + event.getSelfParent(), linkedEvent.getSelfParent().getBaseEvent().getDescriptor()); } - final List otherParents = event.getHashedData().getOtherParents(); + final List otherParents = event.getOtherParents(); if (otherParents.isEmpty()) { assertNull(linkedEvent.getOtherParent()); } else { diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/linking/InOrderLinkerTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/linking/InOrderLinkerTests.java index 95ebabf08066..6377b0e95f1e 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/linking/InOrderLinkerTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/linking/InOrderLinkerTests.java @@ -134,8 +134,7 @@ private static EventWindow chooseEventWindow( long ancientValue = 0; for (final GossipEvent ancientEvent : ancientEvents) { ancientValue = switch (ancientMode) { - case BIRTH_ROUND_THRESHOLD -> Math.max( - ancientValue, ancientEvent.getHashedData().getBirthRound()); + case BIRTH_ROUND_THRESHOLD -> Math.max(ancientValue, ancientEvent.getBirthRound()); case GENERATION_THRESHOLD -> Math.max(ancientValue, ancientEvent.getGeneration());}; } @@ -168,7 +167,7 @@ void standardOperation(final boolean useBirthRoundForAncient) { .setCreatorId(selfId) .setSelfParent(genesisSelfParent) .setOtherParent(genesisOtherParent) - .setBirthRound(genesisSelfParent.getHashedData().getBirthRound() + 1) + .setBirthRound(genesisSelfParent.getBirthRound() + 1) .setTimeCreated(time.now()) .build(); @@ -189,7 +188,7 @@ void standardOperation(final boolean useBirthRoundForAncient) { .setCreatorId(selfId) .setSelfParent(child1) .setOtherParent(genesisOtherParent) - .setBirthRound(child1.getHashedData().getBirthRound() + 1) + .setBirthRound(child1.getBirthRound() + 1) .setTimeCreated(time.now()) .build(); @@ -210,7 +209,7 @@ void standardOperation(final boolean useBirthRoundForAncient) { .setCreatorId(selfId) .setSelfParent(child1) .setOtherParent(child2) - .setBirthRound(child2.getHashedData().getBirthRound() + 1) + .setBirthRound(child2.getBirthRound() + 1) .setTimeCreated(time.now()) .build(); @@ -229,7 +228,7 @@ void standardOperation(final boolean useBirthRoundForAncient) { .setCreatorId(selfId) .setSelfParent(child2) .setOtherParent(child3) - .setBirthRound(child3.getHashedData().getBirthRound() + 1) + .setBirthRound(child3.getBirthRound() + 1) .setTimeCreated(time.now()) .build(); @@ -356,8 +355,7 @@ void selfParentBirthRoundMismatch(final boolean useBirthRoundForAncient) { .setCreatorId(selfId) .setSelfParent(genesisSelfParent) .setOtherParent(genesisOtherParent) - .overrideSelfParentBirthRound( - genesisSelfParent.getHashedData().getBirthRound() + 1) // birth round doesn't match actual + .overrideSelfParentBirthRound(genesisSelfParent.getBirthRound() + 1) // birth round doesn't match actual .build(); final EventImpl linkedEvent = inOrderLinker.linkEvent(child); @@ -402,7 +400,7 @@ void otherParentBirthRoundMismatch(final boolean useBirthRoundForAncient) { .setSelfParent(genesisSelfParent) .setOtherParent(genesisOtherParent) .overrideOtherParentBirthRound( - genesisOtherParent.getHashedData().getBirthRound() + 1) // birth round doesn't match actual + genesisOtherParent.getBirthRound() + 1) // birth round doesn't match actual .build(); final EventImpl linkedEvent = inOrderLinker.linkEvent(child); diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/orphan/OrphanBufferTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/orphan/OrphanBufferTests.java index 69ffd539bd5f..df08a59f8051 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/orphan/OrphanBufferTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/orphan/OrphanBufferTests.java @@ -22,10 +22,10 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import com.swirlds.common.crypto.Hash; import com.swirlds.common.platform.NodeId; +import com.swirlds.common.test.fixtures.Randotron; import com.swirlds.common.test.fixtures.platform.TestPlatformContextBuilder; import com.swirlds.config.extensions.test.fixtures.TestConfigBuilder; import com.swirlds.platform.consensus.ConsensusConstants; @@ -34,8 +34,6 @@ import com.swirlds.platform.event.GossipEvent; import com.swirlds.platform.eventhandling.EventConfig_; import com.swirlds.platform.gossip.IntakeEventCounter; -import com.swirlds.platform.system.events.BaseEventHashedData; -import com.swirlds.platform.system.events.EventConstants; import com.swirlds.platform.system.events.EventDescriptor; import com.swirlds.platform.test.fixtures.event.TestingEventBuilder; import edu.umd.cs.findbugs.annotations.NonNull; @@ -44,6 +42,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; @@ -175,10 +174,8 @@ private static void assertValidParents( @NonNull final GossipEvent event, @NonNull final EventWindow eventWindow, @NonNull final Collection emittedEvents) { - assertTrue(eventEmittedOrAncient(event.getHashedData().getSelfParent(), eventWindow, emittedEvents)); - - for (final EventDescriptor otherParent : event.getHashedData().getOtherParents()) { - assertTrue(eventEmittedOrAncient(otherParent, eventWindow, emittedEvents)); + for (final EventDescriptor parent : event.getAllParents()) { + assertTrue(eventEmittedOrAncient(parent, eventWindow, emittedEvents)); } } @@ -270,7 +267,7 @@ void standardOperation(final boolean useBirthRoundForAncient) { for (final GossipEvent unorphanedEvent : unorphanedEvents) { assertValidParents(unorphanedEvent, eventWindow, emittedEvents); - emittedEvents.add(unorphanedEvent.getHashedData().getHash()); + emittedEvents.add(unorphanedEvent.getHash()); } } @@ -281,31 +278,32 @@ void standardOperation(final boolean useBirthRoundForAncient) { } @Test - @DisplayName("Test Parent Iterator") + @DisplayName("Test All Parents Iterator") void testParentIterator() { - final GossipEvent event = mock(GossipEvent.class); - - final EventDescriptor selfParent = - new EventDescriptor(new Hash(), new NodeId(0), 0, EventConstants.BIRTH_ROUND_UNDEFINED); - final EventDescriptor otherParent1 = - new EventDescriptor(new Hash(), new NodeId(1), 1, EventConstants.BIRTH_ROUND_UNDEFINED); - final EventDescriptor otherParent2 = - new EventDescriptor(new Hash(), new NodeId(2), 2, EventConstants.BIRTH_ROUND_UNDEFINED); - final EventDescriptor otherParent3 = - new EventDescriptor(new Hash(), new NodeId(3), 3, EventConstants.BIRTH_ROUND_UNDEFINED); - final List otherParents = new ArrayList<>(); - otherParents.add(otherParent1); - otherParents.add(otherParent2); - otherParents.add(otherParent3); + final Random random = Randotron.create(); - final BaseEventHashedData eventBase = mock(BaseEventHashedData.class); - when(eventBase.getSelfParent()).thenReturn(selfParent); - when(eventBase.getOtherParents()).thenReturn(otherParents); - when(event.getHashedData()).thenReturn(eventBase); + final GossipEvent selfParent = + new TestingEventBuilder(random).setCreatorId(new NodeId(0)).build(); + final GossipEvent otherParent1 = + new TestingEventBuilder(random).setCreatorId(new NodeId(1)).build(); + final GossipEvent otherParent2 = + new TestingEventBuilder(random).setCreatorId(new NodeId(2)).build(); + final GossipEvent otherParent3 = + new TestingEventBuilder(random).setCreatorId(new NodeId(3)).build(); + + final GossipEvent event = new TestingEventBuilder(random) + .setSelfParent(selfParent) + .setOtherParents(List.of(otherParent1, otherParent2, otherParent3)) + .build(); + + final List otherParents = new ArrayList<>(); + otherParents.add(otherParent1.getDescriptor()); + otherParents.add(otherParent2.getDescriptor()); + otherParents.add(otherParent3.getDescriptor()); - final ParentIterator iterator = new ParentIterator(event); + final Iterator iterator = event.getAllParents().iterator(); - assertEquals(selfParent, iterator.next(), "The first parent should be the self parent"); + assertEquals(selfParent.getDescriptor(), iterator.next(), "The first parent should be the self parent"); int index = 0; while (iterator.hasNext()) { assertEquals(otherParents.get(index++), iterator.next(), "The next parent should be the next other parent"); diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/stale/TransactionResubmitterTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/stale/TransactionResubmitterTests.java index ee4970b92cf8..e1febe9b0921 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/stale/TransactionResubmitterTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/stale/TransactionResubmitterTests.java @@ -56,7 +56,8 @@ void basicBehaviorTest() { .withConfiguration(configuration) .build(); - final long currentRound = randotron.nextLong(1, 1000); + // the round must be high enough to allow events to be too old + final long currentRound = randotron.nextLong(1000, 2000); final EventWindow eventWindow = new EventWindow( currentRound, 1 /* ignored by resubmitter */, diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/validation/InternalEventValidatorTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/validation/InternalEventValidatorTests.java index 545cd6565809..e1fad01b4b9a 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/validation/InternalEventValidatorTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/event/validation/InternalEventValidatorTests.java @@ -17,7 +17,6 @@ package com.swirlds.platform.event.validation; import static com.swirlds.common.test.fixtures.RandomUtils.getRandomPrintSeed; -import static com.swirlds.common.test.fixtures.RandomUtils.randomHash; import static com.swirlds.platform.system.events.EventConstants.GENERATION_UNDEFINED; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -28,28 +27,23 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.hedera.pbj.runtime.io.buffer.Bytes; import com.swirlds.base.test.fixtures.time.FakeTime; import com.swirlds.base.time.Time; import com.swirlds.common.context.PlatformContext; -import com.swirlds.common.crypto.Hash; import com.swirlds.common.platform.NodeId; import com.swirlds.common.test.fixtures.platform.TestPlatformContextBuilder; import com.swirlds.config.extensions.test.fixtures.TestConfigBuilder; import com.swirlds.platform.event.GossipEvent; import com.swirlds.platform.eventhandling.EventConfig_; import com.swirlds.platform.gossip.IntakeEventCounter; -import com.swirlds.platform.system.events.BaseEventHashedData; -import com.swirlds.platform.system.events.EventDescriptor; -import com.swirlds.platform.system.transaction.ConsensusTransactionImpl; -import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; -import java.util.Collections; +import com.swirlds.platform.test.fixtures.event.TestingEventBuilder; +import java.util.List; import java.util.Random; import java.util.concurrent.atomic.AtomicLong; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; /** * Tests for {@link DefaultInternalEventValidator} @@ -88,66 +82,10 @@ void setup() { singleNodeValidator = new DefaultInternalEventValidator(platformContext, true, intakeEventCounter); } - private static GossipEvent generateEvent( - @NonNull final EventDescriptor self, - @Nullable final EventDescriptor selfParent, - @Nullable final EventDescriptor otherParent, - final int totalTransactionBytes) { - - final ConsensusTransactionImpl[] transactions = new ConsensusTransactionImpl[100]; - for (int index = 0; index < transactions.length; index++) { - transactions[index] = mock(ConsensusTransactionImpl.class); - when(transactions[index].getSerializedLength()).thenReturn(totalTransactionBytes / transactions.length); - } - - final BaseEventHashedData hashedData = mock(BaseEventHashedData.class); - when(hashedData.getSelfParentHash()).thenReturn(selfParent == null ? null : selfParent.getHash()); - when(hashedData.getOtherParentHash()).thenReturn(otherParent == null ? null : otherParent.getHash()); - when(hashedData.getSelfParentGen()) - .thenReturn(selfParent == null ? GENERATION_UNDEFINED : selfParent.getGeneration()); - when(hashedData.getOtherParentGen()) - .thenReturn(otherParent == null ? GENERATION_UNDEFINED : otherParent.getGeneration()); - when(hashedData.getTransactions()).thenReturn(transactions); - when(hashedData.getBirthRound()).thenReturn(self.getBirthRound()); - when(hashedData.getGeneration()).thenReturn(self.getGeneration()); - when(hashedData.getSelfParent()).thenReturn(selfParent); - // FUTURE WORK: Extend to support multiple other parents. - when(hashedData.getOtherParents()) - .thenReturn(otherParent == null ? Collections.EMPTY_LIST : Collections.singletonList(otherParent)); - - final GossipEvent event = mock(GossipEvent.class); - when(event.getHashedData()).thenReturn(hashedData); - when(event.getSignature()).thenReturn(Bytes.EMPTY); - when(event.getGeneration()).thenReturn(self.getGeneration()); - when(event.getDescriptor()).thenReturn(self); - - return event; - } - - private static GossipEvent generateGoodEvent(@NonNull final Random random, final int totalTransactionBytes) { - return generateEvent( - new EventDescriptor(randomHash(random), new NodeId(0), 7, 1), - new EventDescriptor(randomHash(random), new NodeId(0), 5, 1), - new EventDescriptor(randomHash(random), new NodeId(1), 6, 1), - totalTransactionBytes); - } - - @Test - @DisplayName("An event with null hashed data is invalid") - void nullHashedData() { - final GossipEvent event = generateGoodEvent(random, 1111); - when(event.getHashedData()).thenReturn(null); - - assertNull(multinodeValidator.validateEvent(event)); - assertNull(singleNodeValidator.validateEvent(event)); - - assertEquals(2, exitedIntakePipelineCount.get()); - } - @Test @DisplayName("An event with null signature is invalid") void nullSignatureData() { - final GossipEvent event = generateGoodEvent(random, 1111); + final GossipEvent event = Mockito.spy(new TestingEventBuilder(random).build()); when(event.getSignature()).thenReturn(null); assertNull(multinodeValidator.validateEvent(event)); @@ -160,7 +98,11 @@ void nullSignatureData() { @DisplayName("An event with too many transaction bytes is invalid") void tooManyTransactionBytes() { // default max is 245_760 bytes - final GossipEvent event = generateGoodEvent(random, 500_000); + final GossipEvent event = new TestingEventBuilder(random) + .setTransactionSize(100) + .setAppTransactionCount(5000) + .setSystemTransactionCount(0) + .build(); assertNull(multinodeValidator.validateEvent(event)); assertNull(singleNodeValidator.validateEvent(event)); @@ -172,18 +114,16 @@ void tooManyTransactionBytes() { @DisplayName("An event with parent inconsistency is invalid") void inconsistentParents() { // self parent has invalid generation. - final GossipEvent invalidSelfParentGeneration = generateEvent( - new EventDescriptor(randomHash(random), new NodeId(0), 7, 1), - new EventDescriptor(randomHash(random), new NodeId(0), GENERATION_UNDEFINED, 1), - new EventDescriptor(randomHash(random), new NodeId(1), 6, 1), - 1111); + final GossipEvent invalidSelfParentGeneration = new TestingEventBuilder(random) + .setSelfParent(new TestingEventBuilder(random).build()) + .overrideSelfParentGeneration(GENERATION_UNDEFINED) + .build(); // other parent has invalid generation. - final GossipEvent invalidOtherParentGeneration = generateEvent( - new EventDescriptor(randomHash(random), new NodeId(0), 7, 1), - new EventDescriptor(randomHash(random), new NodeId(0), 5, 1), - new EventDescriptor(randomHash(random), new NodeId(1), GENERATION_UNDEFINED, 1), - 1111); + final GossipEvent invalidOtherParentGeneration = new TestingEventBuilder(random) + .setOtherParent(new TestingEventBuilder(random).build()) + .overrideOtherParentGeneration(GENERATION_UNDEFINED) + .build(); assertNull(multinodeValidator.validateEvent(invalidSelfParentGeneration)); assertNull(multinodeValidator.validateEvent(invalidOtherParentGeneration)); @@ -197,10 +137,11 @@ void inconsistentParents() { @Test @DisplayName("An event with identical parents is only valid in a single node network") void identicalParents() { - final Hash sharedHash = randomHash(random); - final EventDescriptor sharedDescriptor = new EventDescriptor(sharedHash, new NodeId(0), 5, 1); - final GossipEvent event = generateEvent( - new EventDescriptor(randomHash(random), new NodeId(0), 6, 1), sharedDescriptor, sharedDescriptor, 1111); + final GossipEvent parent = new TestingEventBuilder(random).build(); + final GossipEvent event = new TestingEventBuilder(random) + .setSelfParent(parent) + .setOtherParent(parent) + .build(); assertNull(multinodeValidator.validateEvent(event)); assertNotEquals(null, singleNodeValidator.validateEvent(event)); @@ -208,63 +149,64 @@ void identicalParents() { assertEquals(1, exitedIntakePipelineCount.get()); } - @Test - @DisplayName("An event must have a generation of the max parent generation + 1") - void invalidGeneration() { - final EventDescriptor selfParent1 = new EventDescriptor(randomHash(random), new NodeId(0), 5, 1); - final EventDescriptor otherParent1 = new EventDescriptor(randomHash(random), new NodeId(1), 7, 1); - final EventDescriptor selfParent2 = new EventDescriptor(randomHash(random), new NodeId(0), 7, 1); - final EventDescriptor otherParent2 = new EventDescriptor(randomHash(random), new NodeId(1), 5, 1); - final EventDescriptor selfMiddle = new EventDescriptor(randomHash(random), new NodeId(0), 6, 1); - final EventDescriptor selfHigh = new EventDescriptor(randomHash(random), new NodeId(0), 9, 1); - final EventDescriptor selfLow = new EventDescriptor(randomHash(random), new NodeId(0), 3, 1); - final EventDescriptor selfGood = new EventDescriptor(randomHash(random), new NodeId(0), 8, 1); - - assertNull(multinodeValidator.validateEvent(generateEvent(selfHigh, selfParent1, otherParent1, 1111))); - assertNull(multinodeValidator.validateEvent(generateEvent(selfHigh, selfParent2, otherParent2, 1111))); - assertNull(multinodeValidator.validateEvent(generateEvent(selfMiddle, selfParent1, otherParent1, 1111))); - assertNull(multinodeValidator.validateEvent(generateEvent(selfMiddle, selfParent2, otherParent2, 1111))); - assertNull(multinodeValidator.validateEvent(generateEvent(selfLow, selfParent1, otherParent1, 1111))); - assertNull(multinodeValidator.validateEvent(generateEvent(selfLow, selfParent2, otherParent2, 1111))); - assertNotNull(multinodeValidator.validateEvent(generateEvent(selfGood, selfParent1, otherParent1, 1111))); - assertNotNull(multinodeValidator.validateEvent(generateEvent(selfGood, selfParent2, otherParent2, 1111))); - - assertNull(singleNodeValidator.validateEvent(generateEvent(selfHigh, selfParent1, otherParent1, 1111))); - assertNull(singleNodeValidator.validateEvent(generateEvent(selfHigh, selfParent2, otherParent2, 1111))); - assertNull(singleNodeValidator.validateEvent(generateEvent(selfMiddle, selfParent1, otherParent1, 1111))); - assertNull(singleNodeValidator.validateEvent(generateEvent(selfMiddle, selfParent2, otherParent2, 1111))); - assertNull(singleNodeValidator.validateEvent(generateEvent(selfLow, selfParent1, otherParent1, 1111))); - assertNull(singleNodeValidator.validateEvent(generateEvent(selfLow, selfParent2, otherParent2, 1111))); - assertNotNull(singleNodeValidator.validateEvent(generateEvent(selfGood, selfParent1, otherParent1, 1111))); - assertNotNull(singleNodeValidator.validateEvent(generateEvent(selfGood, selfParent2, otherParent2, 1111))); - - assertEquals(12, exitedIntakePipelineCount.get()); - } - @Test @DisplayName("An event must have a birth round greater than or equal to the max of all parent birth rounds.") void invalidBirthRound() { - final EventDescriptor selfParent1 = new EventDescriptor(randomHash(random), new NodeId(0), 5, 5); - final EventDescriptor otherParent1 = new EventDescriptor(randomHash(random), new NodeId(1), 7, 7); - final EventDescriptor selfParent2 = new EventDescriptor(randomHash(random), new NodeId(0), 7, 7); - final EventDescriptor otherParent2 = new EventDescriptor(randomHash(random), new NodeId(1), 5, 5); - final EventDescriptor selfMiddle = new EventDescriptor(randomHash(random), new NodeId(0), 8, 6); - final EventDescriptor selfLow = new EventDescriptor(randomHash(random), new NodeId(0), 8, 4); - final EventDescriptor selfGood = new EventDescriptor(randomHash(random), new NodeId(0), 8, 7); - - assertNull(multinodeValidator.validateEvent(generateEvent(selfMiddle, selfParent1, otherParent1, 1111))); - assertNull(multinodeValidator.validateEvent(generateEvent(selfMiddle, selfParent2, otherParent2, 1111))); - assertNull(multinodeValidator.validateEvent(generateEvent(selfLow, selfParent1, otherParent1, 1111))); - assertNull(multinodeValidator.validateEvent(generateEvent(selfLow, selfParent2, otherParent2, 1111))); - assertNotNull(multinodeValidator.validateEvent(generateEvent(selfGood, selfParent1, otherParent1, 1111))); - assertNotNull(multinodeValidator.validateEvent(generateEvent(selfGood, selfParent2, otherParent2, 1111))); + final GossipEvent selfParent1 = new TestingEventBuilder(random) + .setCreatorId(new NodeId(0)) + .setBirthRound(5) + .build(); + final GossipEvent otherParent1 = new TestingEventBuilder(random) + .setCreatorId(new NodeId(1)) + .setBirthRound(7) + .build(); + final GossipEvent selfParent2 = new TestingEventBuilder(random) + .setCreatorId(new NodeId(0)) + .setBirthRound(7) + .build(); + final GossipEvent otherParent2 = new TestingEventBuilder(random) + .setCreatorId(new NodeId(1)) + .setBirthRound(5) + .build(); - assertNull(singleNodeValidator.validateEvent(generateEvent(selfMiddle, selfParent1, otherParent1, 1111))); - assertNull(singleNodeValidator.validateEvent(generateEvent(selfMiddle, selfParent2, otherParent2, 1111))); - assertNull(singleNodeValidator.validateEvent(generateEvent(selfLow, selfParent1, otherParent1, 1111))); - assertNull(singleNodeValidator.validateEvent(generateEvent(selfLow, selfParent2, otherParent2, 1111))); - assertNotNull(singleNodeValidator.validateEvent(generateEvent(selfGood, selfParent1, otherParent1, 1111))); - assertNotNull(singleNodeValidator.validateEvent(generateEvent(selfGood, selfParent2, otherParent2, 1111))); + for (final InternalEventValidator validator : List.of(multinodeValidator, singleNodeValidator)) { + assertNull(validator.validateEvent(new TestingEventBuilder(random) + .setCreatorId(new NodeId(0)) + .setSelfParent(selfParent1) + .setOtherParent(otherParent1) + .setBirthRound(6) + .build())); + assertNull(validator.validateEvent(new TestingEventBuilder(random) + .setCreatorId(new NodeId(0)) + .setSelfParent(selfParent2) + .setOtherParent(otherParent2) + .setBirthRound(6) + .build())); + assertNull(validator.validateEvent(new TestingEventBuilder(random) + .setCreatorId(new NodeId(0)) + .setSelfParent(selfParent1) + .setOtherParent(otherParent1) + .setBirthRound(4) + .build())); + assertNull(validator.validateEvent(new TestingEventBuilder(random) + .setCreatorId(new NodeId(0)) + .setSelfParent(selfParent2) + .setOtherParent(otherParent2) + .setBirthRound(4) + .build())); + assertNotNull(validator.validateEvent(new TestingEventBuilder(random) + .setCreatorId(new NodeId(0)) + .setSelfParent(selfParent1) + .setOtherParent(otherParent1) + .setBirthRound(7) + .build())); + assertNotNull(validator.validateEvent(new TestingEventBuilder(random) + .setCreatorId(new NodeId(0)) + .setSelfParent(selfParent2) + .setOtherParent(otherParent2) + .setBirthRound(7) + .build())); + } assertEquals(8, exitedIntakePipelineCount.get()); } @@ -272,18 +214,19 @@ void invalidBirthRound() { @Test @DisplayName("Test that an event with no issues passes validation") void successfulValidation() { - final GossipEvent normalEvent = generateGoodEvent(random, 1111); - final GossipEvent missingSelfParent = generateEvent( - new EventDescriptor(randomHash(random), new NodeId(0), 7, 1), - null, - new EventDescriptor(randomHash(random), new NodeId(1), 6, 1), - 1111); + final GossipEvent normalEvent = new TestingEventBuilder(random) + .setSelfParent(new TestingEventBuilder(random).build()) + .setOtherParent(new TestingEventBuilder(random).build()) + .build(); + final GossipEvent missingSelfParent = new TestingEventBuilder(random) + .setSelfParent(null) + .setOtherParent(new TestingEventBuilder(random).build()) + .build(); - final GossipEvent missingOtherParent = generateEvent( - new EventDescriptor(randomHash(random), new NodeId(0), 6, 1), - new EventDescriptor(randomHash(random), new NodeId(0), 5, 1), - null, - 1111); + final GossipEvent missingOtherParent = new TestingEventBuilder(random) + .setSelfParent(new TestingEventBuilder(random).build()) + .setOtherParent(null) + .build(); assertNotEquals(null, multinodeValidator.validateEvent(normalEvent)); assertNotEquals(null, multinodeValidator.validateEvent(missingSelfParent)); diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/system/events/BirthRoundMigrationShimTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/system/events/BirthRoundMigrationShimTests.java index 6d06a42e37cf..84ff20cc7c95 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/system/events/BirthRoundMigrationShimTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/system/events/BirthRoundMigrationShimTests.java @@ -26,6 +26,7 @@ import com.swirlds.common.platform.NodeId; import com.swirlds.common.test.fixtures.platform.TestPlatformContextBuilder; import com.swirlds.platform.event.GossipEvent; +import com.swirlds.platform.event.hashing.StatefulEventHasher; import com.swirlds.platform.system.BasicSoftwareVersion; import com.swirlds.platform.system.SoftwareVersion; import com.swirlds.platform.test.fixtures.event.TestingEventBuilder; @@ -38,7 +39,6 @@ class BirthRoundMigrationShimTests { @NonNull private GossipEvent buildEvent( @NonNull final Random random, - @NonNull final PlatformContext platformContext, @NonNull final SoftwareVersion softwareVersion, final long generation, final long birthRound) { @@ -58,7 +58,7 @@ private GossipEvent buildEvent( .overrideSelfParentGeneration(generation - 1) .build(); - platformContext.getCryptography().digestSync(event.getHashedData()); + new StatefulEventHasher().hashEvent(event); return event; } @@ -87,22 +87,21 @@ void ancientEventsTest() { final long birthRound = random.nextLong(100, 1000); final GossipEvent event = buildEvent( random, - platformContext, new BasicSoftwareVersion( firstVersionInBirthRoundMode.getSoftwareVersion() - random.nextInt(1, 100)), lowestJudgeGenerationBeforeBirthRoundMode - random.nextInt(1, 100), birthRound); - assertEquals(birthRound, event.getHashedData().getBirthRound()); - final Hash originalHash = event.getHashedData().getHash(); + assertEquals(birthRound, event.getBirthRound()); + final Hash originalHash = event.getHash(); assertSame(event, shim.migrateEvent(event)); - assertEquals(ROUND_FIRST, event.getHashedData().getBirthRound()); + assertEquals(ROUND_FIRST, event.getBirthRound()); // The hash of the event should not have changed - event.getHashedData().invalidateHash(); - platformContext.getCryptography().digestSync(event.getHashedData()); - assertEquals(originalHash, event.getHashedData().getHash()); + event.invalidateHash(); + new StatefulEventHasher().hashEvent(event); + assertEquals(originalHash, event.getHash()); } } @@ -131,21 +130,20 @@ void barelyNonAncientEventsTest() { final long birthRound = random.nextLong(100, 1000); final GossipEvent event = buildEvent( random, - platformContext, new BasicSoftwareVersion( firstVersionInBirthRoundMode.getSoftwareVersion() - random.nextInt(1, 100)), lowestJudgeGenerationBeforeBirthRoundMode + random.nextInt(0, 10), birthRound); - assertEquals(birthRound, event.getHashedData().getBirthRound()); - final Hash originalHash = event.getHashedData().getHash(); + assertEquals(birthRound, event.getBirthRound()); + final Hash originalHash = event.getHash(); assertSame(event, shim.migrateEvent(event)); - assertEquals(lastRoundBeforeBirthRoundMode, event.getHashedData().getBirthRound()); + assertEquals(lastRoundBeforeBirthRoundMode, event.getBirthRound()); // The hash of the event should not have changed event.getHashedData().invalidateHash(); - platformContext.getCryptography().digestSync(event.getHashedData()); + new StatefulEventHasher().hashEvent(event); assertEquals(originalHash, event.getHashedData().getHash()); } } @@ -174,20 +172,19 @@ void unmodifiedEventsTest() { final long birthRound = random.nextLong(100, 1000); final GossipEvent event = buildEvent( random, - platformContext, new BasicSoftwareVersion(firstVersionInBirthRoundMode.getSoftwareVersion() + random.nextInt(0, 10)), lowestJudgeGenerationBeforeBirthRoundMode - random.nextInt(-100, 100), birthRound); - assertEquals(birthRound, event.getHashedData().getBirthRound()); - final Hash originalHash = event.getHashedData().getHash(); + assertEquals(birthRound, event.getBirthRound()); + final Hash originalHash = event.getHash(); assertSame(event, shim.migrateEvent(event)); - assertEquals(birthRound, event.getHashedData().getBirthRound()); + assertEquals(birthRound, event.getBirthRound()); // The hash of the event should not have changed - event.getHashedData().invalidateHash(); - platformContext.getCryptography().digestSync(event.getHashedData()); + event.invalidateHash(); + new StatefulEventHasher().hashEvent(event); assertEquals(originalHash, event.getHashedData().getHash()); } } diff --git a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/turtle/gossip/SimulatedGossipTests.java b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/turtle/gossip/SimulatedGossipTests.java index a4a5103390cd..f6f921bc01e2 100644 --- a/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/turtle/gossip/SimulatedGossipTests.java +++ b/platform-sdk/swirlds-platform-core/src/test/java/com/swirlds/platform/turtle/gossip/SimulatedGossipTests.java @@ -34,6 +34,7 @@ import com.swirlds.common.wiring.wires.input.BindableInputWire; import com.swirlds.common.wiring.wires.output.StandardOutputWire; import com.swirlds.platform.event.GossipEvent; +import com.swirlds.platform.event.hashing.StatefulEventHasher; import com.swirlds.platform.system.BasicSoftwareVersion; import com.swirlds.platform.system.StaticSoftwareVersion; import com.swirlds.platform.system.address.AddressBook; @@ -65,7 +66,7 @@ class SimulatedGossipTests { private static Set getUniqueDescriptors(@NonNull final List events) { final HashSet uniqueDescriptors = new HashSet<>(); for (final GossipEvent event : events) { - uniqueDescriptors.add(event.getHashedData().getDescriptor()); + uniqueDescriptors.add(event.getDescriptor()); } return uniqueDescriptors; } @@ -143,7 +144,7 @@ void randomDataTest(final int networkSize) { final NodeId creator = addressBook.getNodeId(randotron.nextInt(networkSize)); final GossipEvent event = new TestingEventBuilder(randotron).setCreatorId(creator).build(); - context.getCryptography().digestSync(event.getHashedData()); + new StatefulEventHasher().hashEvent(event); eventsToGossip.add(event); } @@ -153,7 +154,7 @@ void randomDataTest(final int networkSize) { final GossipEvent event = eventsToGossip.get(eventIndex); for (final NodeId nodeId : addressBook.getNodeIdSet()) { - if (event.getHashedData().getCreatorId().equals(nodeId) || randotron.nextBoolean(0.1)) { + if (event.getCreatorId().equals(nodeId) || randotron.nextBoolean(0.1)) { eventSubmitters.get(nodeId).accept(event); // When a node sends out an event, add it to the list of events that node knows about. diff --git a/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/event/TestingEventBuilder.java b/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/event/TestingEventBuilder.java index 2bd09d0f7ca6..e2a11038140d 100644 --- a/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/event/TestingEventBuilder.java +++ b/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/event/TestingEventBuilder.java @@ -16,7 +16,7 @@ package com.swirlds.platform.test.fixtures.event; -import static com.swirlds.platform.system.events.EventConstants.BIRTH_ROUND_UNDEFINED; +import static com.swirlds.platform.system.events.EventConstants.MINIMUM_ROUND_CREATED; import com.hedera.hapi.platform.event.EventConsensusData; import com.hedera.hapi.platform.event.StateSignaturePayload; @@ -35,10 +35,11 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.time.Instant; -import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Random; +import java.util.stream.Stream; /** * A builder for creating event instances for testing purposes. @@ -103,11 +104,9 @@ public class TestingEventBuilder { private GossipEvent selfParent; /** - * The other parent of the event. - *

- * Future work: add support for multiple other parents. + * The other parents of the event. */ - private GossipEvent otherParent; + private List otherParents; /** * Overrides the generation of the configured self parent. @@ -304,15 +303,28 @@ public TestingEventBuilder(@NonNull final Random random) { } /** - * Set the other-parent of an event + * Set an other-parent of an event *

- * If not set, an other parent will NOT be generated: the output event will have a null other parent. + * If not set, no other-parent will be generated: the output event will have a no other-parents. * * @param otherParent the other-parent * @return this instance */ public @NonNull TestingEventBuilder setOtherParent(@Nullable final GossipEvent otherParent) { - this.otherParent = otherParent; + this.otherParents = otherParent == null ? null : List.of(otherParent); + return this; + } + + /** + * Set a list of other-parents of an event + *

+ * If not set, no other-parents will be generated: the output event will have a no other-parents. + * + * @param otherParents the other-parents + * @return this instance + */ + public @NonNull TestingEventBuilder setOtherParents(@NonNull final List otherParents) { + this.otherParents = otherParents; return this; } @@ -507,17 +519,20 @@ private EventDescriptor createDescriptorFromParent( final EventDescriptor selfParentDescriptor = createDescriptorFromParent(selfParent, selfParentGenerationOverride, selfParentBirthRoundOverride); - final EventDescriptor otherParentDescriptor = - createDescriptorFromParent(otherParent, otherParentGenerationOverride, otherParentBirthRoundOverride); + final List otherParentDescriptors = Stream.ofNullable(otherParents) + .flatMap(List::stream) + .map(parent -> createDescriptorFromParent( + parent, otherParentGenerationOverride, otherParentBirthRoundOverride)) + .toList(); if (this.birthRound == null) { - final long maxParentBirthRound = Math.max( - selfParent == null - ? BIRTH_ROUND_UNDEFINED - : selfParent.getHashedData().getBirthRound(), - otherParent == null - ? BIRTH_ROUND_UNDEFINED - : otherParent.getHashedData().getBirthRound()); + + final long maxParentBirthRound = Stream.concat( + Stream.ofNullable(selfParent), + Stream.ofNullable(otherParents).flatMap(List::stream)) + .mapToLong(GossipEvent::getBirthRound) + .max() + .orElse(MINIMUM_ROUND_CREATED); // randomly add between 0 and 2 to max parent birth round birthRound = maxParentBirthRound + random.nextLong(0, 3); @@ -540,20 +555,18 @@ private EventDescriptor createDescriptorFromParent( softwareVersion, creatorId, selfParentDescriptor, - // Future work: add support for multiple other parents - otherParentDescriptor == null - ? Collections.emptyList() - : Collections.singletonList(otherParentDescriptor), + otherParentDescriptors, birthRound, timeCreated, transactions); - hashedData.setHash(RandomUtils.randomHash(random)); final byte[] signature = new byte[SignatureType.RSA.signatureLength()]; random.nextBytes(signature); final GossipEvent gossipEvent = new GossipEvent(hashedData, signature); + gossipEvent.setHash(RandomUtils.randomHash(random)); + if (consensusTimestamp != null || consensusOrder != null) { gossipEvent.setConsensusData(new EventConsensusData.Builder() .consensusTimestamp(HapiUtils.asTimestamp( diff --git a/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/event/generator/StandardGraphGenerator.java b/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/event/generator/StandardGraphGenerator.java index 94af5d3e799d..b6f2d74b1044 100644 --- a/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/event/generator/StandardGraphGenerator.java +++ b/platform-sdk/swirlds-platform-core/src/testFixtures/java/com/swirlds/platform/test/fixtures/event/generator/StandardGraphGenerator.java @@ -21,10 +21,10 @@ import static com.swirlds.platform.test.fixtures.event.RandomEventUtils.DEFAULT_FIRST_EVENT_TIME_CREATED; import com.swirlds.common.context.PlatformContext; -import com.swirlds.common.crypto.CryptographyHolder; import com.swirlds.common.platform.NodeId; import com.swirlds.platform.ConsensusImpl; import com.swirlds.platform.event.GossipEvent; +import com.swirlds.platform.event.hashing.StatefulEventHasher; import com.swirlds.platform.event.linking.ConsensusLinker; import com.swirlds.platform.event.linking.InOrderLinker; import com.swirlds.platform.metrics.NoOpConsensusMetrics; @@ -499,9 +499,11 @@ public IndexedEvent buildNextEvent(final long eventIndex) { // and links it. The event must be hashed and have a descriptor built for its use in the InOrderLinker. // This may leak memory, but is fine in the current testing framework. // When the test ends any memory used will be released. - CryptographyHolder.get().digestSync(next.getBaseEvent().getHashedData()); - consensus.addEvent(inOrderLinker.linkEvent(new GossipEvent( - next.getBaseEvent().getHashedData(), next.getBaseEvent().getSignature()))); + new StatefulEventHasher().hashEvent(next.getBaseEvent()); + final GossipEvent tmp = new GossipEvent( + next.getBaseEvent().getHashedData(), next.getBaseEvent().getSignature()); + tmp.setHash(next.getBaseEvent().getHash()); + consensus.addEvent(inOrderLinker.linkEvent(tmp)); return next; } diff --git a/platform-sdk/swirlds-platform-core/src/testFixtures/java/module-info.java b/platform-sdk/swirlds-platform-core/src/testFixtures/java/module-info.java index 1822dcf8b654..32e6b792af82 100644 --- a/platform-sdk/swirlds-platform-core/src/testFixtures/java/module-info.java +++ b/platform-sdk/swirlds-platform-core/src/testFixtures/java/module-info.java @@ -1,12 +1,12 @@ open module com.swirlds.platform.core.test.fixtures { requires transitive com.hedera.node.hapi; - requires transitive com.hedera.pbj.runtime; requires transitive com.swirlds.common.test.fixtures; requires transitive com.swirlds.common; requires transitive com.swirlds.merkle; requires transitive com.swirlds.platform.core; requires transitive com.swirlds.state.api; requires transitive com.swirlds.virtualmap; + requires transitive com.hedera.pbj.runtime; requires transitive org.junit.jupiter.params; requires com.swirlds.logging; requires com.swirlds.merkledb; 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 new file mode 100644 index 000000000000..56c2be1150b7 --- /dev/null +++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/HapiUtils.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.swirlds.state.spi; + +import com.hedera.hapi.node.base.SemanticVersion; +import edu.umd.cs.findbugs.annotations.Nullable; +import java.util.Comparator; + +/** + * Utility methods for the Hedera API. + */ +public final class HapiUtils { + + // FUTURE WORK: Add unit tests for this class. + /** A {@link Comparator} for {@link SemanticVersion}s that ignores + * any semver part that cannot be parsed as an integer. */ + public static final Comparator SEMANTIC_VERSION_COMPARATOR = Comparator.comparingInt( + SemanticVersion::major) + .thenComparingInt(SemanticVersion::minor) + .thenComparingInt(SemanticVersion::patch) + .thenComparingInt(semVer -> HapiUtils.parsedIntOrZero(semVer.pre())) + .thenComparingInt(semVer -> HapiUtils.parsedIntOrZero(semVer.build())); + + private static int parsedIntOrZero(@Nullable final String s) { + if (s == null || s.isBlank()) { + return 0; + } else { + try { + return Integer.parseInt(s); + } catch (NumberFormatException ignore) { + return 0; + } + } + } +} diff --git a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/state/MigrationContext.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/MigrationContext.java similarity index 94% rename from hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/state/MigrationContext.java rename to platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/MigrationContext.java index 10602c14c067..fcf6cc5ccedf 100644 --- a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/state/MigrationContext.java +++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/MigrationContext.java @@ -14,14 +14,12 @@ * limitations under the License. */ -package com.hedera.node.app.spi.state; +package com.swirlds.state.spi; import com.hedera.hapi.node.base.SemanticVersion; -import com.hedera.node.app.spi.info.NetworkInfo; -import com.hedera.node.app.spi.workflows.record.GenesisRecordsBuilder; import com.swirlds.config.api.Configuration; -import com.swirlds.state.spi.ReadableStates; -import com.swirlds.state.spi.WritableStates; +import com.swirlds.state.spi.info.NetworkInfo; +import com.swirlds.state.spi.workflows.record.GenesisRecordsBuilder; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.Map; diff --git a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/state/Schema.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/Schema.java similarity index 92% rename from hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/state/Schema.java rename to platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/Schema.java index 0a8692c85564..f12be1632596 100644 --- a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/state/Schema.java +++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/Schema.java @@ -14,14 +14,10 @@ * limitations under the License. */ -package com.hedera.node.app.spi.state; - -import static com.hedera.hapi.util.HapiUtils.SEMANTIC_VERSION_COMPARATOR; +package com.swirlds.state.spi; import com.hedera.hapi.node.base.SemanticVersion; import com.hedera.pbj.runtime.Codec; -import com.swirlds.state.spi.ReadableKVState; -import com.swirlds.state.spi.ReadableStates; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Collections; import java.util.Objects; @@ -29,7 +25,7 @@ /** * Defines the schema of all states for a specific {@link SemanticVersion} of a specific {@link - * com.hedera.node.app.spi.Service} instance. It is necessary to create a new {@link Schema} + * Service} instance. It is necessary to create a new {@link Schema} * whenever a new {@link ReadableKVState} is to be created, or an existing one removed, or a * migration has to happen. If your service makes use of a forwards and backwards compatible * serialization system (such as protobuf), then it is not necessary to define a new {@link Schema} @@ -114,7 +110,7 @@ public void restart(@NonNull final MigrationContext ctx) { /** {@inheritDoc} */ @Override public int compareTo(Schema o) { - return SEMANTIC_VERSION_COMPARATOR.compare(this.version, o.version); + return HapiUtils.SEMANTIC_VERSION_COMPARATOR.compare(this.version, o.version); } /** {@inheritDoc} */ diff --git a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/state/SchemaRegistry.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/SchemaRegistry.java similarity index 96% rename from hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/state/SchemaRegistry.java rename to platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/SchemaRegistry.java index 3d179453a783..86547702c54f 100644 --- a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/state/SchemaRegistry.java +++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/SchemaRegistry.java @@ -14,9 +14,8 @@ * limitations under the License. */ -package com.hedera.node.app.spi.state; +package com.swirlds.state.spi; -import com.hedera.node.app.spi.Service; import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Collection; diff --git a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/Service.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/Service.java similarity index 65% rename from hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/Service.java rename to platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/Service.java index 9cf42606737c..8552f4e2c4fd 100644 --- a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/Service.java +++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/Service.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022-2024 Hedera Hashgraph, LLC + * 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. @@ -14,20 +14,16 @@ * limitations under the License. */ -package com.hedera.node.app.spi; +package com.swirlds.state.spi; -import static java.util.Collections.emptySet; - -import com.hedera.node.app.spi.state.SchemaRegistry; -import com.hedera.pbj.runtime.RpcServiceDefinition; import edu.umd.cs.findbugs.annotations.NonNull; -import java.util.Set; /** * A definition of an interface that will be implemented by each conceptual "service" like * crypto-service, token-service etc., */ public interface Service { + /** * A sort value for the service, used to determine the order in which service * schemas are migrated. @@ -46,7 +42,7 @@ default int migrationOrder() { } /** - * Returns the name of the service. This name must be unique for each service deployed on the + * Returns the name of the application state. This name must be unique for each state type deployed on the * application. * * @return the name @@ -55,22 +51,9 @@ default int migrationOrder() { String getServiceName(); /** - * If this service exposes RPC endpoints, then this method returns the RPC service definitions. - * Otherwise, it returns an empty set. - * - * @return The RPC service definitions if this service is exposed via RPC. - */ - @NonNull - default Set rpcDefinitions() { - return emptySet(); - } - - /** - * Registers the schemas this service really uses with the given {@link SchemaRegistry}. + * Registers the schemas this application state really uses with the given {@link SchemaRegistry}. * * @param registry the registry to register the schemas with */ - default void registerSchemas(@NonNull final SchemaRegistry registry) { - // No-op - } + void registerSchemas(@NonNull SchemaRegistry registry); } diff --git a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/state/StateDefinition.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/StateDefinition.java similarity index 98% rename from hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/state/StateDefinition.java rename to platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/StateDefinition.java index dafeaad88f2e..dbf4ca4db311 100644 --- a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/state/StateDefinition.java +++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/StateDefinition.java @@ -14,10 +14,9 @@ * limitations under the License. */ -package com.hedera.node.app.spi.state; +package com.swirlds.state.spi; import com.hedera.pbj.runtime.Codec; -import com.swirlds.state.spi.ReadableKVState; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; diff --git a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/info/NetworkInfo.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/NetworkInfo.java similarity index 97% rename from hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/info/NetworkInfo.java rename to platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/NetworkInfo.java index 886f8c9f5aea..001041b4af99 100644 --- a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/info/NetworkInfo.java +++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/NetworkInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.hedera.node.app.spi.info; +package com.swirlds.state.spi.info; import com.hedera.pbj.runtime.io.buffer.Bytes; import edu.umd.cs.findbugs.annotations.NonNull; diff --git a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/info/NodeInfo.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/NodeInfo.java similarity index 98% rename from hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/info/NodeInfo.java rename to platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/NodeInfo.java index 548df85a0355..7c8e2de4ef5c 100644 --- a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/info/NodeInfo.java +++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/NodeInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.hedera.node.app.spi.info; +package com.swirlds.state.spi.info; import com.hedera.hapi.node.base.AccountID; diff --git a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/info/SelfNodeInfo.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/SelfNodeInfo.java similarity index 97% rename from hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/info/SelfNodeInfo.java rename to platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/SelfNodeInfo.java index 1d79d1e8638f..9dd3e7cd414b 100644 --- a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/info/SelfNodeInfo.java +++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/info/SelfNodeInfo.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.hedera.node.app.spi.info; +package com.swirlds.state.spi.info; import com.hedera.hapi.node.base.SemanticVersion; import edu.umd.cs.findbugs.annotations.NonNull; diff --git a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/workflows/record/GenesisRecordsBuilder.java b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/workflows/record/GenesisRecordsBuilder.java similarity index 97% rename from hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/workflows/record/GenesisRecordsBuilder.java rename to platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/workflows/record/GenesisRecordsBuilder.java index f0614a986a94..c33468544629 100644 --- a/hedera-node/hedera-app-spi/src/main/java/com/hedera/node/app/spi/workflows/record/GenesisRecordsBuilder.java +++ b/platform-sdk/swirlds-state-api/src/main/java/com/swirlds/state/spi/workflows/record/GenesisRecordsBuilder.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.hedera.node.app.spi.workflows.record; +package com.swirlds.state.spi.workflows.record; import com.hedera.hapi.node.state.token.Account; import edu.umd.cs.findbugs.annotations.NonNull; diff --git a/platform-sdk/swirlds-state-api/src/main/java/module-info.java b/platform-sdk/swirlds-state-api/src/main/java/module-info.java index 66c14ac734d7..ec2ebddaaae4 100644 --- a/platform-sdk/swirlds-state-api/src/main/java/module-info.java +++ b/platform-sdk/swirlds-state-api/src/main/java/module-info.java @@ -2,7 +2,11 @@ exports com.swirlds.state; exports com.swirlds.state.spi; exports com.swirlds.state.spi.metrics; + exports com.swirlds.state.spi.info; + exports com.swirlds.state.spi.workflows.record; - requires com.hedera.pbj.runtime; + requires transitive com.hedera.node.hapi; + requires transitive com.swirlds.config.api; + requires transitive com.hedera.pbj.runtime; requires static transitive com.github.spotbugs.annotations; } diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/com/swirlds/platform/test/consensus/TestIntake.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/com/swirlds/platform/test/consensus/TestIntake.java index 60e1c322dbe7..f286b01724fd 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/com/swirlds/platform/test/consensus/TestIntake.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/com/swirlds/platform/test/consensus/TestIntake.java @@ -82,7 +82,7 @@ public TestIntake(@NonNull final PlatformContext platformContext, @NonNull final model = WiringModelBuilder.create(platformContext).build(); hasherWiring = new ComponentWiring<>(model, EventHasher.class, directScheduler("eventHasher")); - final EventHasher eventHasher = new DefaultEventHasher(platformContext); + final EventHasher eventHasher = new DefaultEventHasher(); hasherWiring.bind(eventHasher); final PassThroughWiring postHashCollectorWiring = diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/com/swirlds/platform/test/consensus/framework/validation/NoEventsLost.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/com/swirlds/platform/test/consensus/framework/validation/NoEventsLost.java index 3b5b77b07640..0e501592d75c 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/com/swirlds/platform/test/consensus/framework/validation/NoEventsLost.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/com/swirlds/platform/test/consensus/framework/validation/NoEventsLost.java @@ -16,6 +16,7 @@ package com.swirlds.platform.test.consensus.framework.validation; +import com.swirlds.common.AbstractHashable; import com.swirlds.common.crypto.Hash; import com.swirlds.config.extensions.test.fixtures.TestConfigBuilder; import com.swirlds.platform.consensus.ConsensusConfig; @@ -40,8 +41,8 @@ private NoEventsLost() {} */ public static void validateNoEventsAreLost( @NonNull final ConsensusOutput output, @NonNull final ConsensusOutput ignored) { - final Map stale = output.getStaleEvents().stream() - .collect(Collectors.toMap(e -> e.getHashedData().getHash(), e -> e)); + final Map stale = + output.getStaleEvents().stream().collect(Collectors.toMap(AbstractHashable::getHash, e -> e)); final Map cons = output.getConsensusRounds().stream() .flatMap(r -> r.getConsensusEvents().stream()) .collect(Collectors.toMap(EventImpl::getBaseHash, e -> e)); @@ -59,15 +60,14 @@ public static void validateNoEventsAreLost( // non-ancient events are not checked continue; } - if (stale.containsKey(event.getHashedData().getHash()) - == cons.containsKey(event.getHashedData().getHash())) { + if (stale.containsKey(event.getHash()) == cons.containsKey(event.getHash())) { Assertions.fail(String.format( "An ancient event should be either stale or consensus, but not both!\n" + "nonAncientGen=%d, Event %s, stale=%s, consensus=%s", nonAncientGen, event.getDescriptor(), - stale.containsKey(event.getHashedData().getHash()), - cons.containsKey(event.getHashedData().getHash()))); + stale.containsKey(event.getHash()), + cons.containsKey(event.getHash()))); } } } diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/module-info.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/module-info.java index 2e2d488ff565..e14f0a3e6be0 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/module-info.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/main/java/module-info.java @@ -4,9 +4,9 @@ requires transitive com.swirlds.common; requires transitive com.swirlds.platform.core.test.fixtures; requires transitive com.swirlds.platform.core; + requires com.hedera.node.hapi; requires com.swirlds.config.api; requires com.swirlds.config.extensions.test.fixtures; - requires com.hedera.node.hapi; requires java.desktop; requires org.junit.jupiter.api; requires static transitive com.github.spotbugs.annotations; diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/SerializationTests.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/SerializationTests.java index c087f231c526..1723c58f1831 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/SerializationTests.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/SerializationTests.java @@ -63,6 +63,6 @@ public void serializeDeserializeTest(T generated) t static Stream selfSerializableProvider() { final Random random = RandomUtils.getRandomPrintSeed(); - return Stream.of(arguments(new TestingEventBuilder(random).build().getHashedData())); + return Stream.of(arguments(new TestingEventBuilder(random).build())); } } diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/consensus/GraphGeneratorTests.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/consensus/GraphGeneratorTests.java index 155aff675384..f1bac0c17ed0 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/consensus/GraphGeneratorTests.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/consensus/GraphGeneratorTests.java @@ -183,14 +183,11 @@ protected void verifyExpectedOtherParentRatio( int count = 0; for (final IndexedEvent event : events) { final NodeId otherParentId; - if (event.getBaseEvent().getHashedData().getOtherParents().isEmpty()) { + if (event.getBaseEvent().getOtherParents().isEmpty()) { otherParentId = null; } else { - otherParentId = event.getBaseEvent() - .getHashedData() - .getOtherParents() - .getFirst() - .getCreator(); + otherParentId = + event.getBaseEvent().getOtherParents().getFirst().getCreator(); } if (Objects.equals(otherParentId, nodeId)) { diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/tipset/TipsetEventCreatorTests.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/tipset/TipsetEventCreatorTests.java index d48909a8a965..b0343d456ac8 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/tipset/TipsetEventCreatorTests.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/event/tipset/TipsetEventCreatorTests.java @@ -45,7 +45,6 @@ import com.swirlds.platform.event.creation.tipset.ChildlessEventTracker; import com.swirlds.platform.event.creation.tipset.TipsetEventCreator; import com.swirlds.platform.event.creation.tipset.TipsetTracker; -import com.swirlds.platform.event.creation.tipset.TipsetUtils; import com.swirlds.platform.event.creation.tipset.TipsetWeightCalculator; import com.swirlds.platform.internal.EventImpl; import com.swirlds.platform.system.BasicSoftwareVersion; @@ -236,10 +235,7 @@ private EventImpl linkEvent( @NonNull final Map events, @NonNull final BaseEventHashedData event) { - eventCreators - .get(event.getCreatorId()) - .tipsetTracker - .addEvent(event.getDescriptor(), TipsetUtils.getParentDescriptors(event)); + eventCreators.get(event.getCreatorId()).tipsetTracker.addEvent(event.getDescriptor(), event.getAllParents()); final EventImpl selfParent = events.get(event.getSelfParentHash()); final EventImpl otherParent = events.get(event.getOtherParentHash()); @@ -260,7 +256,7 @@ private void distributeEvent( eventCreator.eventCreator.registerEvent(eventImpl.getBaseEvent()); eventCreator.tipsetTracker.addEvent( eventImpl.getBaseEvent().getDescriptor(), - TipsetUtils.getParentDescriptors(eventImpl.getBaseEvent().getHashedData())); + eventImpl.getBaseEvent().getAllParents()); } } diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/sync/ShadowGraphTest.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/sync/ShadowGraphTest.java index 096958398461..fae0800ff036 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/sync/ShadowGraphTest.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/sync/ShadowGraphTest.java @@ -40,6 +40,7 @@ import com.swirlds.platform.gossip.shadowgraph.ShadowgraphInsertionException; import com.swirlds.platform.internal.EventImpl; import com.swirlds.platform.system.address.AddressBook; +import com.swirlds.platform.system.events.EventDescriptor; import com.swirlds.platform.test.event.emitter.EventEmitterFactory; import com.swirlds.platform.test.event.emitter.StandardEventEmitter; import com.swirlds.platform.test.fixtures.addressbook.RandomAddressBookBuilder; @@ -109,7 +110,7 @@ private void initShadowgraph(final Random random, final int numEvents, final int final IndexedEvent event = emitter.emitEvent(); final Hash hash = event.getBaseHash(); - ancestorsMap.put(hash, ancestorsOf(event.getSelfParentHash(), event.getOtherParentHash())); + ancestorsMap.put(hash, ancestorsOf(event.getBaseEvent().getAllParents())); assertDoesNotThrow(() -> shadowgraph.addEvent(event), "Unable to insert event into shadow graph."); assertTrue( shadowgraph.isHashInGraph(hash), @@ -181,18 +182,12 @@ private void assertSetsContainSameHashes(final Set expected, final Set ancestorsOf(final Hash selfParent, final Hash otherParent) { + private Set ancestorsOf(final List parents) { final Set ancestorSet = new HashSet<>(); - if (selfParent != null) { - ancestorSet.add(selfParent); - if (ancestorsMap.containsKey(selfParent)) { - ancestorSet.addAll(ancestorsMap.get(selfParent)); - } - } - if (otherParent != null) { - ancestorSet.add(otherParent); - if (ancestorsMap.containsKey(otherParent)) { - ancestorSet.addAll(ancestorsMap.get(otherParent)); + for (final EventDescriptor parent : parents) { + ancestorSet.add(parent.getHash()); + if (ancestorsMap.containsKey(parent.getHash())) { + ancestorSet.addAll(ancestorsMap.get(parent.getHash())); } } return ancestorSet; diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/sync/ShadowgraphByBirthRoundTests.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/sync/ShadowgraphByBirthRoundTests.java index c7cdd040fe8d..fe0ea824bcff 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/sync/ShadowgraphByBirthRoundTests.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/sync/ShadowgraphByBirthRoundTests.java @@ -43,6 +43,7 @@ import com.swirlds.platform.gossip.shadowgraph.ShadowgraphInsertionException; import com.swirlds.platform.internal.EventImpl; import com.swirlds.platform.system.address.AddressBook; +import com.swirlds.platform.system.events.EventDescriptor; import com.swirlds.platform.test.event.emitter.EventEmitterFactory; import com.swirlds.platform.test.event.emitter.StandardEventEmitter; import com.swirlds.platform.test.fixtures.addressbook.RandomAddressBookBuilder; @@ -116,7 +117,7 @@ private void initShadowGraph(final Random random, final int numEvents, final int final IndexedEvent event = emitter.emitEvent(); final Hash hash = event.getBaseHash(); - ancestorsMap.put(hash, ancestorsOf(event.getSelfParentHash(), event.getOtherParentHash())); + ancestorsMap.put(hash, ancestorsOf(event.getBaseEvent().getAllParents())); assertDoesNotThrow(() -> shadowGraph.addEvent(event), "Unable to insert event into shadow graph."); assertTrue( shadowGraph.isHashInGraph(hash), @@ -195,18 +196,12 @@ private void assertSetsContainSameHashes(final Set expected, final Set ancestorsOf(final Hash selfParent, final Hash otherParent) { + private Set ancestorsOf(final List parents) { final Set ancestorSet = new HashSet<>(); - if (selfParent != null) { - ancestorSet.add(selfParent); - if (ancestorsMap.containsKey(selfParent)) { - ancestorSet.addAll(ancestorsMap.get(selfParent)); - } - } - if (otherParent != null) { - ancestorSet.add(otherParent); - if (ancestorsMap.containsKey(otherParent)) { - ancestorSet.addAll(ancestorsMap.get(otherParent)); + for (final EventDescriptor parent : parents) { + ancestorSet.add(parent.getHash()); + if (ancestorsMap.containsKey(parent.getHash())) { + ancestorSet.addAll(ancestorsMap.get(parent.getHash())); } } return ancestorSet; diff --git a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/sync/SyncNode.java b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/sync/SyncNode.java index 961e0e406785..1e5cbaa1d934 100644 --- a/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/sync/SyncNode.java +++ b/platform-sdk/swirlds-unit-tests/core/swirlds-platform-test/src/test/java/com/swirlds/platform/test/sync/SyncNode.java @@ -21,7 +21,6 @@ import static org.mockito.Mockito.mock; import com.swirlds.common.context.PlatformContext; -import com.swirlds.common.crypto.CryptographyHolder; import com.swirlds.common.platform.NodeId; import com.swirlds.common.test.fixtures.platform.TestPlatformContextBuilder; import com.swirlds.common.threading.pool.CachedPoolParallelExecutor; @@ -31,6 +30,8 @@ import com.swirlds.platform.consensus.EventWindow; import com.swirlds.platform.event.AncientMode; import com.swirlds.platform.event.GossipEvent; +import com.swirlds.platform.event.hashing.EventHasher; +import com.swirlds.platform.event.hashing.StatefulEventHasher; import com.swirlds.platform.eventhandling.EventConfig_; import com.swirlds.platform.gossip.IntakeEventCounter; import com.swirlds.platform.gossip.NoOpIntakeEventCounter; @@ -222,7 +223,8 @@ private void addToShadowGraph(final IndexedEvent newEvent) { */ public void drainReceivedEventQueue() { receivedEventQueue.drainTo(receivedEvents); - receivedEvents.forEach(e -> CryptographyHolder.get().digestSync((e).getHashedData())); + final EventHasher hasher = new StatefulEventHasher(); + receivedEvents.forEach(hasher::hashEvent); } /** diff --git a/platform-sdk/swirlds-virtualmap/src/main/java/com/swirlds/virtualmap/internal/ConcurrentNodeStatusTracker.java b/platform-sdk/swirlds-virtualmap/src/main/java/com/swirlds/virtualmap/internal/ConcurrentNodeStatusTracker.java index 21bc2111d034..ca8ba989d62c 100644 --- a/platform-sdk/swirlds-virtualmap/src/main/java/com/swirlds/virtualmap/internal/ConcurrentNodeStatusTracker.java +++ b/platform-sdk/swirlds-virtualmap/src/main/java/com/swirlds/virtualmap/internal/ConcurrentNodeStatusTracker.java @@ -276,7 +276,7 @@ private static int getIndexInBitSetFor(final long value) { /** * We use two {@link BitSet} to handle the 3 possible * states of a value: - * + *

    *
  • UNKNOWN: if {@code knowns} is set to false. *
  • *
  • KNOWN: if {@code knowns} is set to true and @@ -285,7 +285,7 @@ private static int getIndexInBitSetFor(final long value) { *
  • NOT_KNOWN: if {@code knowns} is set to true * and {@code status} is not set. *
  • - * + *
*/ private static final class BitSetGroup { private final BitSet status; diff --git a/settings.gradle.kts b/settings.gradle.kts index 5485ea67c970..c9b5c73fb9f4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -31,6 +31,10 @@ include(":app-hapi-fees", "hedera-node/hapi-fees") include(":app-hapi-utils", "hedera-node/hapi-utils") +include(":app-service-addressbook", "hedera-node/hedera-addressbook-service") + +include(":app-service-addressbook-impl", "hedera-node/hedera-addressbook-service-impl") + include(":app-service-consensus", "hedera-node/hedera-consensus-service") include(":app-service-consensus-impl", "hedera-node/hedera-consensus-service-impl")